私は、AngularJS を使用して JSON バックエンドからフィードするサンプル Rails アプリケーションに取り組んでいます。コードを以下に示します。2 つのルート間でファクトリ データを共有する際に問題が発生しています。それぞれが別のコントローラーによって制御されています (この例では事実上同じです)。
シナリオは次のとおりです。
- /lists パスに移動し、ブラウザーを更新したとします。個人のリストが表示されます。
- Person をクリックすると、選択された状態に切り替わり、赤で強調表示されます。
- /roller パスに移動しましたが、その Person が選択されていません。
- /lists パスに戻りましたが、Person が選択されていません。
- ブラウザーを更新せずに手順を繰り返します。これにより、People の選択が維持されるようになりました。
別のシナリオを次に示します。
- /roller パスに移動し、ブラウザーを更新したとします。
- 個人をクリックして選択します。
- その Person も選択されている /lists パスに移動します。
- その人物がまだ選択されている /roller パスに戻ります。
どちらのシナリオも一貫していません。基本的に、動作は予測できません (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>
リスト.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>
ローラー.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>
ローラー.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();
}
}
}])
スタイル.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()
ただし、返される値がプロミスであることをこの「リスト」ファクトリに知らせることには注意が必要です。これらのコンポーネントを独自の「クラス」でラップする (通常はこれが好みです) 以外に、この工場間の知識を制限する別の方法はありますか?