javascript - 使用工厂时 Controller /模板不共享相同状态

标签 javascript angularjs

我正在开发一个示例 Rails 应用程序,它使用 AngularJS 从 JSON 后端提供数据。下面列出了代码。我在两条路线之间共享工厂数据时遇到问题,每条路线都由一个单独的 Controller 控制(在本例中实际上是相同的)。

这是一个场景:

  • 假设我已经导航到/lists 路径并刷新了浏览器。将出现我的人员列表。
  • 我点击一个人,这会将其切换为选中状态并以红色突出显示。
  • 我导航到/roller 路径,但那个人没有被选中。
  • 我导航回/lists 路径,但那里不再选择 Person。
  • 我在没有刷新浏览器的情况下重复这些步骤,现在保持对任何人的选择。

这是另一种情况:

  • 假设我已经导航到/roller 路径并刷新了浏览器。
  • 我点击一个人来选择它。
  • 我导航到/lists 路径,其中也选中了那个人。
  • 我导航回/roller 路径,该 Person 仍处于选中状态。

这两种情况都不一致。从本质上讲,这种行为是不可预测的(我对 Angular 的内部工作原理了解有限)。两条路线将在离开时保持选择或不保持选择。我想知道 Angular 中发生了什么,以便我可以找到该用例的可靠替代方案。

(index.html 的正文)

<header>
  <nav>
    <a id="logo" href="/#/"><h2>Roller</h2></a>
    <ul class="links">
      <li><a href="/#/people">People</a></li>
      <li><a href="/#/lists">Lists</a></li>
    </ul>
  </nav>
</header>

<div id="main" ng-app="RollerApp">
  <ng-view></ng-view>
</div>

lists.html

<div ng-controller="ListCtrl">
  <ul>
    <li ng-repeat="person in people">
    <a class="selected-{{ person.selected }}" ng-click="toggleSelect( person )">{{ person.name }}</a>
    </li>
  </ul>
</div>

roller.html

<div ng-controller="RollerCtrl">
  <ul>
    <li ng-repeat="person in people">
    <a class="selected-{{ person.selected }}" ng-click="toggleSelect( person )">{{ person.name }}</a>
    </li>
  </ul>
</div>

roller.js

rollerApp = angular.module('RollerApp', ['People']);

rollerApp.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider){
  $routeProvider.when('/', {
    templateUrl: 'assets/roller.html'
  }).when('/lists', {
    templateUrl: 'assets/lists.html'
  }).otherwise({
    template: 'Not Found'
  });
}]);

roller_ctrl.js

rollerApp = angular.module('RollerApp');

rollerApp.controller('RollerCtrl', ['$scope', 'Person', function($scope, Person){
  $scope.people = Person.all();

  $scope.toggleSelect = function(person){
    person.selected = !person.selected;
  };
}]);

list_ctrl.js

rollerApp = angular.module('RollerApp');

rollerApp.controller('ListCtrl', ['$scope', 'Person', function($scope, Person){
  $scope.people = Person.all();

  $scope.toggleSelect = function(person){
    person.selected = !person.selected;
  };
}]);

person_factory.js

people = angular.module('People', ['ngResource']);

people.factory('Person', ['$resource', '$q', function($resource, $q){
  var _person = $resource('people/:id', {id: '@id'}, {});
  var people = null;

  function query(){
    if(!people){
      var deferred = $q.defer();
      people = _person.query(function(success){
        deferred.resolve(success);
      }, function(failure){
        deferred.reject(failure);
      });
      return deferred.promise;
    }else{
      return people;
    }
  }

  return {
    all: function(){
      return query();
    }
  }
}])

styles.css

a.selected-true { color: red }

编辑以包含@jpmorin 建议的实现

补充问题

当使用工厂使用来自另一个工厂的数据时,我将如何使用基于 promise 的方法?

list-factory.js

lists = angular.module('Lists', ['ngResource']);

lists.factory('List', ['$resource', '$q', 'Person', function($resource, $q, Person){
  var _list = $resource('lists/:id', {id: '@id'}, {});
  var people = Person.all();
  var lists = null;

  function query(){
    if(!lists){
      var deferred = $q.defer();
      lists = _list.query(function(success){
        success.forEach(function(list){
          people.then(function(value){
            addPeopleToList(list, value);
          });
        });
        deferred.resolve(success);
      }, function(failure){
        deferred.reject(failure);
      });
      return deferred.promise;
    }else{
      return lists;
    }
  }

  function personById(id, people){
    return people.filter(function(person){ return id === person.id })[0];
  }

  function addPeopleToList(list, people){
    ids = list.people.map(function(person){ return person.id });
    list.people = ids.map(function(id){ return personById(id, people) });
  }

  return {
    all: function(){
      return query();
    }
  }
}])

List 的上述实现成功解决了我原来问题的另一半(我缩小了比例以简化示例)。但是,我担心让这个“列表”工厂知道从 Person.all() 返回的值是一个 promise 。除了用我自己的“类”(通常是首选)包装这些组件之外,还有其他方法可以限制这种跨工厂知识吗?

最佳答案

我创建了一个演示,试图在您的 Person 工厂中使用 $q$timeout 模拟您的数据库调用。

演示 http://plnkr.co/edit/Q5NbIuj2JUMxjf8OkT8J?p=preview

在第一次调用时,从数据库请求数据,但随后会缓存数据以供下一次调用。由于它们是对象并且它们的引用通过您的应用程序传递,因此您所做的任何修改都将在其他任何地方可用。这里我们使用 promises,因为它是一个异步调用,我们不知道数据何时可用。因此,在您的 Controller 中,您必须使用以下方法等待 promise 的回调:

Person.all().then(function(data) { 
    $scope.people = data;
});

第一次调用会有一点延迟,但接下来的调用会立即使用缓存数据。

关于javascript - 使用工厂时 Controller /模板不共享相同状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19581171/

相关文章:

c# - 回发后如何检查表单

javascript - 是否可以计算 Angular 谷歌地图中两个标记之间的距离?

javascript - 使用 Jasmine 2.0 对 $scope.$evalAsync 函数进行单元测试

json - Angular js JSON数据与DateTime的MVC模型绑定(bind)?

javascript - Dropzone 在 init 函数上添加 id 并排序

c# - 正则表达式匹配 4 组中的 2 组

没有按住的Javascript onmouseUp

angularjs - 如何在 angularjs $http.get 中设置 contentType

javascript - 如何将基于条件的 href 添加到 AngularJs 中的 <a> 标记

ruby-on-rails - 使用 AngularJS 访问 API 时,Rails 请求内容类型似乎不正确