AngularJS : 서비스 속성에 대한 올바른 바인딩 방법
Angular에서 서비스 속성에 바인드하는 방법에 대한 베스트 프랙티스를 찾고 있습니다.JS.
Angular를 사용하여 작성된 서비스의 속성에 바인드하는 방법을 이해하기 위해 여러 예를 살펴보았습니다.JS.
아래에는 서비스 속성에 바인드하는 방법의 두 가지 예를 나타냅니다.둘 다 동작합니다.첫 번째 예에서는 기본 바인딩을 사용하고 두 번째 예에서는 $scope를 사용합니다.서비스 속성에 바인드하는 $watch
서비스 속성에 바인딩할 때 다음 중 하나를 선호합니까? 아니면 제가 모르는 다른 옵션이 권장됩니까?
이러한 예의 전제는 서비스 속성이 "lastUpdated" 및 "calls"를 5초마다 갱신해야 한다는 것입니다.서비스 속성이 갱신되면 뷰에 이러한 변경이 반영됩니다.이 두 예시는 모두 성공적으로 작동하는데, 나는 그것을 하는 더 나은 방법이 없을지 궁금하다.
기본 바인딩
다음 코드를 표시하고 실행할 수 있습니다.http://plnkr.co/edit/d3c16z
<html>
<body ng-app="ServiceNotification" >
<div ng-controller="TimerCtrl1" style="border-style:dotted">
TimerCtrl1 <br/>
Last Updated: {{timerData.lastUpdated}}<br/>
Last Updated: {{timerData.calls}}<br/>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script type="text/javascript">
var app = angular.module("ServiceNotification", []);
function TimerCtrl1($scope, Timer) {
$scope.timerData = Timer.data;
};
app.factory("Timer", function ($timeout) {
var data = { lastUpdated: new Date(), calls: 0 };
var updateTimer = function () {
data.lastUpdated = new Date();
data.calls += 1;
console.log("updateTimer: " + data.lastUpdated);
$timeout(updateTimer, 5000);
};
updateTimer();
return {
data: data
};
});
</script>
</body>
</html>
서비스 속성에 대한 바인딩을 해결한 다른 방법은 $scope를 사용하는 것입니다.$watch in 컨트롤러.
$180.$watch
다음 코드를 표시하고 실행할 수 있습니다.http://plnkr.co/edit/dSBlC9
<html>
<body ng-app="ServiceNotification">
<div style="border-style:dotted" ng-controller="TimerCtrl1">
TimerCtrl1<br/>
Last Updated: {{lastUpdated}}<br/>
Last Updated: {{calls}}<br/>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script type="text/javascript">
var app = angular.module("ServiceNotification", []);
function TimerCtrl1($scope, Timer) {
$scope.$watch(function () { return Timer.data.lastUpdated; },
function (value) {
console.log("In $watch - lastUpdated:" + value);
$scope.lastUpdated = value;
}
);
$scope.$watch(function () { return Timer.data.calls; },
function (value) {
console.log("In $watch - calls:" + value);
$scope.calls = value;
}
);
};
app.factory("Timer", function ($timeout) {
var data = { lastUpdated: new Date(), calls: 0 };
var updateTimer = function () {
data.lastUpdated = new Date();
data.calls += 1;
console.log("updateTimer: " + data.lastUpdated);
$timeout(updateTimer, 5000);
};
updateTimer();
return {
data: data
};
});
</script>
</body>
</html>
$rootscope를 사용할 수 있습니다.$140, $root 입니다.컨트롤러에서는 $on이지만 첫 번째 브로드캐스트에서 $broadcast/$를 사용하는 다른 예에서는 컨트롤러에 의해 캡처되지 않지만 브로드캐스트된 추가 콜이 컨트롤러에서 트리거됩니다.$rootscope를 해결하는 방법을 알고 있다면.$125 문제, 답변을 주세요.
그러나 앞서 말씀드린 내용을 다시 말씀드리기 위해 서비스 속성에 바인드하는 방법에 대한 베스트 프랙티스를 알고 싶습니다.
갱신하다
이 질문은 원래 2013년 4월에 질문 및 답변되었습니다.2014년 5월, Gil Birman이 새로운 답을 제시했고, 저는 그것을 정답으로 변경했습니다.Gil Birman의 답변은 상향 투표가 거의 없기 때문에, 이 질문을 읽는 사람들이 그의 답변을 무시하고 더 많은 표를 얻은 다른 답변에 유리하게 반응할까 봐 걱정입니다.가장 좋은 답을 결정하기 전에 Gil Birman의 답변을 적극 추천합니다.
두 번째 접근법의 장단점을 몇 가지 고려합니다.
0
{{lastUpdated}}
{{timerData.lastUpdated}}
「 」와 「 」가 될 수 있습니다.{{timer.lastUpdated}}
더 읽기 쉬울지도 모르지만 (논쟁은 하지 맙시다...에 대해서는 판단해 주세요.)+1 컨트롤러가 마크업용 API의 일종으로서 기능하는 것이 편리할 수 있습니다.데이터 모델의 구조가 변경되었을 경우, (이론적으로) html partial을 터치하지 않고 컨트롤러의 API 매핑을 갱신할 수 있습니다.
-1 그러나 이론이 항상 실천적인 것은 아니며, 어쨌든 변화가 요구될 때 마크업과 컨트롤러 로직을 수정해야 하는 경우가 많습니다.따라서 API를 작성하기 위한 추가적인 노력은 API의 이점을 무효화시킵니다.
-1 게다가 이 어프로치는 그다지 건조하지 않습니다.
-1 데이터를 바인드하는 경우
ng-model
패키징해야 에 코드 가 더욱 .$scope.scalar_values
REST하다- 0.1 퍼포먼스 적중으로 감시자가 증가.또한 특정 컨트롤러에서 감시할 필요가 없는 모델에 데이터 속성이 연결되어 있으면 심층 감시자를 위한 추가 오버헤드가 발생합니다.
-1 여러 컨트롤러에 동일한 데이터 모델이 필요한 경우즉, 모델을 변경할 때마다 여러 API를 업데이트해야 합니다.
$scope.timerData = Timer.data;
지금 당장 엄청나게 유혹적으로 들리기 시작하는데...마지막 포인트로 좀 더 깊이 들어가 볼까요?어떤 모델 변경에 대해 얘기했었죠?백엔드 모델(서버)아니면 프런트엔드에만 존재하는 모델인가요?어느 경우든 기본적으로 데이터 매핑 API는 프론트 엔드 서비스 계층(각진 공장 또는 서비스)에 속합니다.(첫 번째 예(내 취향)는 서비스 계층에 API가 없습니다.필요없을 정도로 심플하기 때문에 괜찮습니다.)
결론적으로, 모든 것이 분리될 필요는 없다.데이터 모델에서 마크업을 완전히 분리하는 한 단점이 장점보다 큽니다.
컨트롤러는 일반적으로 혼재되어서는 안 됩니다.$scope = injectable.data.scalar
s. 오, 것, 것, 려, 려, 려, 려, 니, 니, 니, 니, s, ', ', ', ', ', s, s$scope = injectable.data
promise.then(..)
»$scope.complexClickAction = function() {..}
데이터 디커플링과 뷰 캡슐화를 실현하기 위한 대안적 접근법으로서 뷰와 모델을 분리하는 것이 실제로 의미가 있는 유일한 방법은 디렉티브를 사용하는 것입니다.하지만 그곳에서도, 하지 마세요.$watch
값controller
★★★★★★★★★★★★★★★★★」link
기능들.그렇다고 해서 시간을 절약하거나 코드를 더 이상 유지하거나 읽을 수 있는 상태가 되지는 않습니다.각도에서의 견고한 테스트는 보통 결과 DOM을 테스트하기 때문에 테스트를 쉽게 할 수 없습니다.명령어에서는 오브젝트 형식으로 데이터 API를 요구하여$watch
에 의해 작성된 ng-bind
.
예: http://plnkr.co/edit/MVeU1GKRTN4bqA3h9Yio
<body ng-app="ServiceNotification">
<div style="border-style:dotted" ng-controller="TimerCtrl1">
TimerCtrl1<br/>
Bad:<br/>
Last Updated: {{lastUpdated}}<br/>
Last Updated: {{calls}}<br/>
Good:<br/>
Last Updated: {{data.lastUpdated}}<br/>
Last Updated: {{data.calls}}<br/>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.js"></script>
<script type="text/javascript">
var app = angular.module("ServiceNotification", []);
function TimerCtrl1($scope, Timer) {
$scope.data = Timer.data;
$scope.lastUpdated = Timer.data.lastUpdated;
$scope.calls = Timer.data.calls;
};
app.factory("Timer", function ($timeout) {
var data = { lastUpdated: new Date(), calls: 0 };
var updateTimer = function () {
data.lastUpdated = new Date();
data.calls += 1;
console.log("updateTimer: " + data.lastUpdated);
$timeout(updateTimer, 500);
};
updateTimer();
return {
data: data
};
});
</script>
</body>
업데이트: 이 질문으로 돌아와서 어느 쪽도 "잘못된" 접근이라고 생각하지 않는다는 점을 덧붙였습니다.원래 저는 조쉬 데이비드 밀러의 답변이 틀렸다고 썼지만, 돌이켜보면 그의 지적, 특히 우려의 분리에 대한 그의 지적은 완전히 타당합니다.
우려의 분리(단, 접선 관계)는 차치하고, 방어적인 카피의 또 다른 이유는 고려하지 못했습니다.이 질문은 대부분 서비스에서 직접 데이터를 읽는 것에 관한 것입니다.그러나 뷰에 데이터를 표시하기 전에 컨트롤러가 사소한 방법으로 데이터를 변환해야 한다고 팀의 개발자가 결정하면 어떻게 됩니까? (컨트롤러가 데이터를 변환해야 하는지 여부는 다른 논의 사항입니다.먼저 객체를 복사하지 않으면 자신도 모르게 동일한 데이터를 소비하는 다른 뷰 컴포넌트에 퇴행 현상이 발생할 수 있습니다.
이 질문에서 실제로 강조되는 것은 일반적인 각도 애플리케이션(및 실제로 모든 JavaScript 애플리케이션)의 아키텍처 단점입니다. 즉, 우려 사항의 긴밀한 결합과 객체 가변성입니다.최근에 저는 React와 불변의 데이터 구조를 갖춘 아키텍처 애플리케이션에 매료되었습니다.이렇게 하면 다음 두 가지 문제가 훌륭하게 해결됩니다.
우려의 분리: 컴포넌트는 소품을 통해 모든 데이터를 소비하고 글로벌 싱글톤(Angular 서비스 등)에 거의 의존하지 않으며 뷰 계층에서 그 위에 무슨 일이 일어났는지 전혀 알지 못합니다.
가변성:모든 소품은 불변하기 때문에 의도하지 않은 데이터 돌연변이의 위험이 배제됩니다.
Angular 2.0은 이제 위의 두 가지 사항을 달성하기 위해 React에서 많은 부분을 차용하는 궤도에 올랐다.
때, 가 from from from from from from from from from 。$watch
베스트 프랙티스 방법이라고 생각합니다.
실제로 예를 조금 간략화할 수 있습니다.
function TimerCtrl1($scope, Timer) {
$scope.$watch( function () { return Timer.data; }, function (data) {
$scope.lastUpdated = data.lastUpdated;
$scope.calls = data.calls;
}, true);
}
그것만 있으면 돼
속성이 동시에 업데이트되므로 시계가 하나만 필요합니다.하나의물체에서 '', '보여주세요', '보여주세요', '보여주세요' 요.Timer.data
마지막으로 입니다.$watch
는 참조가 동일한지 확인하는 것이 아니라 완전한 평등을 확인하도록 지시합니다.
문맥을 간단히 설명하자면, 서비스 가치를 직접 스코프에 배치하는 것보다 이 방법을 선호하는 이유는 관심사를 적절히 분리하기 위해서입니다.서비스를 운영하기 위해서는 뷰가 서비스에 대해 아무것도 알 필요가 없습니다.컨트롤러의 역할은 모든 것을 하나로 묶는 것입니다.컨트롤러의 역할은 서비스로부터 데이터를 가져와 필요에 따라 처리한 후 필요한 모든 세부 사항을 사용자에게 제공하는 것입니다.하지만 나는 그 서비스가 단지 전망에 따라 전달되는 것이라고 생각하지 않는다.그렇지 않으면 컨트롤러가 거기서 뭘 하는 거죠?JS 때 JS "로직") .if
이치노
공정하게 말하자면, 여기에는 여러 가지 관점이 있을 수 있으며, 저는 다른 답변을 기대하고 있습니다.
파티에 늦었지만 미래의 구글을 위해 제공된 답변을 사용하지 마십시오.
JavaScript는 오브젝트를 참조로 전달하는 메커니즘을 가지고 있지만 값 "number, string 등"에 대해서는 얕은 복사본만 전달합니다.
위의 예에서는 서비스의 속성을 바인딩하는 대신 서비스를 범위에 노출하는 것이 어떨까요?
$scope.hello = HelloService;
이 간단한 접근법은 앵글이 쌍방향 바인딩과 당신이 필요로 하는 모든 마법적인 것들을 할 수 있게 할 것이다.감시자나 불필요한 마크업으로 컨트롤러를 해킹하지 마십시오.
, 실수로 어트리뷰트를 는, 「」를 사용해 .defineProperty
읽기, 열거, 구성 또는 getters 및 setters 정의.서비스를 보다 견고하게 함으로써 많은 제어를 얻을 수 있습니다.
마지막 힌트: 서비스보다 컨트롤러에 더 많은 시간을 할애하는 것은 잘못된 것입니다. : (
당신이 제공한 특정 데모 코드에서 나는 당신이 다음을 할 것을 추천한다:
function TimerCtrl1($scope, Timer) {
$scope.timer = Timer;
}
///Inside view
{{ timer.time_updated }}
{{ timer.other_property }}
etc...
편집:
위에서 설명한 바와 같이 서비스 속성의 동작을 제어할 수 있습니다.
예:
// Lets expose a property named "propertyWithSetter" on our service
// and hook a setter function that automatically saves new value to db !
Object.defineProperty(self, 'propertyWithSetter', {
get: function() { return self.data.variable; },
set: function(newValue) {
self.data.variable = newValue;
// let's update the database too to reflect changes in data-model !
self.updateDatabaseWithNewData(data);
},
enumerable: true,
configurable: true
});
이제 컨트롤러에서 다음을 수행할 수 있습니다.
$scope.hello = HelloService;
$scope.hello.propertyWithSetter = 'NEW VALUE';
는 our의서 of of of of of of of of of of of of of of of of of of of of of of of 의 값을 합니다.propertyWithSetter
새로운 가치를 어떻게든 데이터베이스에 게시할 수 있습니다!
아니면 우리가 원하는 어떤 접근법도 취할 수 있습니다.
다음 MDN 매뉴얼을 참조하십시오.defineProperty
.
나는 이 질문이 맥락적인 요소를 가지고 있다고 생각한다.
단순히 서비스에서 데이터를 가져와 해당 정보를 해당 뷰에 복사하는 것이라면 서비스 속성에 직접 바인딩하는 것이 좋다고 생각합니다.서비스 속성을 모델 속성에 매핑하여 소비하기 위해 많은 보일러 플레이트 코드를 작성하고 싶지 않습니다.
또한 각도 성능은 두 가지에 기초한다.첫 번째는 페이지에 있는 바인딩의 개수입니다.두 번째는 게터 기능이 얼마나 비싼가 하는 것입니다.미스코는 여기서 이것에 대해 이야기한다.
서비스 데이터에 대해 인스턴스 고유의 로직을 수행해야 하며(서비스 자체에 적용되는 데이터 마사지와는 달리), 그 결과가 뷰에 표시되는 데이터 모델에 영향을 미친다면 $watcher가 적절하다고 생각합니다.단, 함수가 너무 비싸지만 않다면 말입니다.고가의 함수의 경우 로컬(컨트롤러) 변수에 결과를 캐싱하여 $watcher 함수 이외의 복잡한 작업을 수행하고 그 결과에 따라 스코프를 바인딩하는 것이 좋습니다.
주의사항으로 $스코프에 직접 자산을 걸어두면 안 됩니다.그$scope
변수가 모형이 아닙니다.모델에 대한 참조가 있습니다.
제 생각에는, 「베스트 프랙티스」라고 하는 것은, 간단하게 정보를 서비스로부터 송신해 표시하기 위해서입니다.
function TimerCtrl1($scope, Timer) {
$scope.model = {timerData: Timer.data};
};
에 "이렇게 하면"이 됩니다.{{model.timerData.lastupdated}}
위의 예를 바탕으로 컨트롤러 변수를 서비스 변수에 투과적으로 바인드하는 방법을 생각해 보았습니다.
합니다.$scope.count
됩니다.count
★★★★★★ 。
실제 운영에서는 이 바인딩을 사용하여 서비스의 ID를 업데이트하고 비동기적으로 데이터를 가져오고 서비스 변수를 업데이트합니다.추가 바인딩은 서비스 자체 업데이트 시 컨트롤러가 자동으로 업데이트됨을 의미합니다.
다음 코드는 http://jsfiddle.net/xuUHS/163/에서 확인할 수 있습니다.
표시:
<div ng-controller="ServiceCtrl">
<p> This is my countService variable : {{count}}</p>
<input type="number" ng-model="count">
<p> This is my updated after click variable : {{countS}}</p>
<button ng-click="clickC()" >Controller ++ </button>
<button ng-click="chkC()" >Check Controller Count</button>
</br>
<button ng-click="clickS()" >Service ++ </button>
<button ng-click="chkS()" >Check Service Count</button>
</div>
서비스/컨트롤러:
var app = angular.module('myApp', []);
app.service('testService', function(){
var count = 10;
function incrementCount() {
count++;
return count;
};
function getCount() { return count; }
return {
get count() { return count },
set count(val) {
count = val;
},
getCount: getCount,
incrementCount: incrementCount
}
});
function ServiceCtrl($scope, testService)
{
Object.defineProperty($scope, 'count', {
get: function() { return testService.count; },
set: function(val) { testService.count = val; },
});
$scope.clickC = function () {
$scope.count++;
};
$scope.chkC = function () {
alert($scope.count);
};
$scope.clickS = function () {
++testService.count;
};
$scope.chkS = function () {
alert(testService.count);
};
}
서비스의 속성보다 서비스 자체에 바인딩하는 것이 더 좋다고 생각합니다.
이유는 다음과 같습니다.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.7/angular.min.js"></script>
<body ng-app="BindToService">
<div ng-controller="BindToServiceCtrl as ctrl">
ArrService.arrOne: <span ng-repeat="v in ArrService.arrOne">{{v}}</span>
<br />
ArrService.arrTwo: <span ng-repeat="v in ArrService.arrTwo">{{v}}</span>
<br />
<br />
<!-- This is empty since $scope.arrOne never changes -->
arrOne: <span ng-repeat="v in arrOne">{{v}}</span>
<br />
<!-- This is not empty since $scope.arrTwo === ArrService.arrTwo -->
<!-- Both of them point the memory space modified by the `push` function below -->
arrTwo: <span ng-repeat="v in arrTwo">{{v}}</span>
</div>
<script type="text/javascript">
var app = angular.module("BindToService", []);
app.controller("BindToServiceCtrl", function ($scope, ArrService) {
$scope.ArrService = ArrService;
$scope.arrOne = ArrService.arrOne;
$scope.arrTwo = ArrService.arrTwo;
});
app.service("ArrService", function ($interval) {
var that = this,
i = 0;
this.arrOne = [];
that.arrTwo = [];
$interval(function () {
// This will change arrOne (the pointer).
// However, $scope.arrOne is still same as the original arrOne.
that.arrOne = that.arrOne.concat([i]);
// This line changes the memory block pointed by arrTwo.
// And arrTwo (the pointer) itself never changes.
that.arrTwo.push(i);
i += 1;
}, 1000);
});
</script>
</body>
이 플런커로 연주할 수 있어요.
나는 내 감시자들을 가능한 적게 두는 편이 낫다.내 이유는 내 경험에 근거한 것이고 이론적으로 주장할 수도 있다.
워처를 사용하는 경우의 문제는 스코프의 임의의 속성을 사용하여 원하는 컴포넌트 또는 서비스의 임의의 메서드를 호출할 수 있다는 것입니다.
실제 프로젝트에서는 추적 불가능한(추적이 어려운) 메서드 체인이 호출되고 값이 변경되어 온보드 프로세스가 특히 비극적이게 됩니다.
데이터를 바인드하는 것, 즉 서비스를 송신하는 것은 좋은 생각(아키텍처)은 아니지만, 더 이상 필요하시면 두 가지 방법을 제안합니다.
1) 서비스 내 이외의 데이터를 입수할 수 있습니다.데이터를 컨트롤러/디렉티브에 넣을 수 있어 어디에나 바인드할 수 있습니다.
2) angularjs 이벤트를 사용할 수 있습니다.언제든지 신호를 보낼 수 있습니다($rootScope에서). 원하는 장소에서 신호를 받을 수 있습니다.해당 eventName에 대한 데이터도 전송할 수 있습니다.
이게 도움이 될지도 몰라예를 들어 더 많은 정보가 필요한 경우 다음 링크를 참조하십시오.
http://www.w3docs.com/snippets/angularjs/bind-value-between-service-and-controller-directive.html
어때
scope = _.extend(scope, ParentScope);
Parent Scope는 삽입된 서비스입니까?
가장 우아한 솔루션...
app.service('svc', function(){ this.attr = []; return this; });
app.controller('ctrl', function($scope, svc){
$scope.attr = svc.attr || [];
$scope.$watch('attr', function(neo, old){ /* if necessary */ });
});
app.run(function($rootScope, svc){
$rootScope.svc = svc;
$rootScope.$watch('svc', function(neo, old){ /* change the world */ });
});
또한 EDA(Event-Driven Architecture)를 작성하기 위해 다음과 같은 작업을 수행하는 경향이 있습니다.
var Service = function Service($rootScope) {
var $scope = $rootScope.$new(this);
$scope.that = [];
$scope.$watch('that', thatObserver, true);
function thatObserver(what) {
$scope.$broadcast('that:changed', what);
}
};
그런 다음 원하는 채널의 컨트롤러에 청취자를 배치하고 로컬 스코프를 최신 상태로 유지합니다.
결론적으로, 물건을 견고하게 유지하고 약한 결합을 사용하는 한, "베스트 프랙티스"는 많지 않습니다.- 오히려 선호도가 높습니다.내가 후자의 코드를 옹호하는 이유는 EDA가 본질적으로 실현 가능한 가장 낮은 결합을 가지고 있기 때문이다.그리고 당신이 이 사실에 대해 크게 염려하지 않는다면, 우리는 같은 프로젝트를 함께 하는 것을 피합시다.
이게 도움이 되길...
언급URL : https://stackoverflow.com/questions/15800454/angularjs-the-correct-way-of-binding-to-a-service-properties
'it-source' 카테고리의 다른 글
여러 get 요청을 요청하는 API 가져오기 (0) | 2023.03.27 |
---|---|
jquery ajax 응답을 javascript/browser에서 캐시하는 중 (0) | 2023.03.27 |
APC -> APCu/OPCache, 퍼포먼스 저하 (0) | 2023.03.27 |
JSON 들여쓰기 수준 표기법이란 무엇입니까? (0) | 2023.03.27 |
스프링 보안 설정: HTTP 403 오류 (0) | 2023.03.27 |