Обработка привязки данных в службах AngularJS

Я пытаюсь выяснить, как правильно обрабатывать привязку, когда мои данные хранятся в службе.

Я могу заставить все работать, если он помещает службу в область $scope, а затем заставляет шаблоны напрямую связываться с ней, но это кажется очень плохой идеей.

Мне бы хотелось, чтобы мои взгляды/контроллеры могли легко изменять состояние в службе и отражать ее везде.

Мне кажется, что я могу сделать что-то вроде следующего, но он не работает (http://jsfiddle.net/aidankane/AtRVD/1/).

HTML

<div ng-controller="MyCtl">
    <select ng-model="drawing" ng-options="d.file for d in drawings"></select>
</div>
<div ng-controller="MyOtherCtl">
    {{ drawing }}
</div>

JS

var myApp = angular.module('myApp', []);

myApp.factory('myService', function(){
    var me = {
        drawings: [{'file':'a'}, {'file':'b'}]
    };
    // selected drawing
    me.drawing = me.drawings[0];
    return me;
});

function MyCtl($scope, myService){
    // can do:
    // $scope.mys = myService;
    // and then in html ng-model="mys.drawing"
    // but that seems wrong

    $scope.drawings = myService.drawings;
    $scope.drawing = myService.drawing;

    // can I not do this? it doesn't seem to work anyway...
    $scope.$watch('drawing', function(drawing){
        myService.drawing = drawing;
    });
}

function MyOtherCtl($scope, myService){
    $scope.drawing = myService.drawing;
}

MyCtl.$inject = ['$scope', 'myService'];
MyOtherCtl.$inject = ['$scope', 'myService'];

Ответ 1

Вы можете привязываться к службам с помощью $watch и передавать функцию:

$scope.$watch( function () { return myService.drawing; }, function ( drawing ) {
  // handle it here. e.g.:
  $scope.drawing = drawing;
});

И затем используйте $scope.drawing в своих шаблонах, и они будут автоматически обновляться:

<div ng-controller="MyOtherCtl">
  {{ drawing }}
</div>

Ответ 2

Я думаю, что еще более элегантно работать с promises (см. $q.deferred()) и разрешать их асинхронно. В функции обещания вы можете назначить данные членам $scope.

Ответ 3

Существует два способа привязки данных от службы: 1) По значению (для проверки изменений переменных для значения примитива службы требуется наблюдатель) 2) По ссылке (значения напрямую связанный), который является моим предпочтительным методом привязки данных.

Я объясню только вторую возможность, так как принятый ответ уже показывает, как наблюдатель может быть реализован. Этот блог описывает то, что я собираюсь объяснить очень хорошо.

Я создал этот plunk, чтобы проиллюстрировать привязку данных по ссылке.

Вот код из панели:

HTML

<!DOCTYPE html>
<html ng-app="myApp">

  <head>
    <script data-require="[email protected]" data-semver="1.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body ng-controller="myAppCntrl">
    <h1>Hello Plunker!</h1>
    <h3>By value</h3>
    <p>{{byValue}}</p>
    <p>{{objByValue}}</p>
    <h3>By object in service reference</h3>
    <p>{{byRefence.stringPrimitive}}</p>
    <h3>By reference to service singleton</h3>
    <p>{{myservice.stringPrimitive}}</p>
    <p style="color: green">of course, you can reference an object through the service as well</p>
    <p>{{myservice.objectWithPrimitive.stringPrimitive}}</p>

    <button ng-click=update()>Update strings on service</button>
    <br />
    <button ng-click=setDefaults()>Restore Defaults</button>
  </body>

</html>

JAVASCRIPT

var myApp = angular.module("myApp", []);

myApp.controller('myAppCntrl', function($scope, myAppService){
  $scope.myservice = myAppService;
  $scope.byValue = myAppService.stringPrimitive;
  $scope.objByValue = myAppService.objectWithPrimitive.stringPrimitive;
  $scope.byRefence = myAppService.objectWithPrimitive;

  $scope.update = function () {
    myAppService.stringPrimitive = "updated string";
    myAppService.objectWithPrimitive.stringPrimitive = "updated string here too";
  };
  $scope.setDefaults = function () {
    myAppService.stringPrimitive = 'string primitive';
    myAppService.objectWithPrimitive.stringPrimitive = 'string primitive';
  };
});

myApp.service('myAppService', function(){
  this.stringPrimitive = 'string primitive';
  this.objectWithPrimitive = {
    stringPrimitive: 'string primitive'
  };
});

Итак, как это работает?

Важно понимать, что это мало связано с тем, как работает angular, и многое зависит от того, как работает Javascript. Когда переменная устанавливается равным первообразному значению (или передается функции) в javascript (целое число, строка и т.д.) , var устанавливается значением. Это означает, что новая переменная является копией переменной, которую вы устанавливаете равной новому местоположению в памяти. Когда переменная устанавливается равным объекту (или передается функции) в javascript , var устанавливается по ссылке.

Что это значит?

Когда переменная $scope var задается по значению и изменяется служебная переменная, поскольку переменная $scope является только копией служебной переменной, служебная переменная больше не имеет ничего общего с служебной переменной и не изменится когда выполняется сервис var.

Когда параметр $scope var установлен равным и объекту, он присваивается ссылкой. Это означает, что когда объект службы (обратите внимание, что служба является объектом, поскольку она создается с помощью нового ключевого слова, и вы можете ссылаться на этот объект с помощью "this" внутри службы) или любые объекты в службе, на которые ссылаются, изменены, любая переменная $scope, которая ссылается на эти объекты, также будет обновлена.