2

私は、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()ただし、返される値がプロミスであることをこの「リスト」ファクトリに知らせることには注意が必要です。これらのコンポーネントを独自の「クラス」でラップする (通常はこれが好みです) 以外に、この工場間の知識を制限する別の方法はありますか?

4

1 に答える 1

1

工場で$qandを使用してデータベース呼び出しをシミュレートしようとするデモを作成$timeoutします。Person

デモ http://plnkr.co/edit/Q5NbIuj2JUMxjf8OkT8J?p=preview

最初の呼び出しでデータベースからデータが要求されますが、次の呼び出しのためにキャッシュされます。それらはオブジェクトであり、それらの参照はアプリケーション全体に渡されるため、行った変更は他の場所で利用できます。ここでは非同期呼び出しであり、データがいつ利用可能になるかわからないため、promise を使用します。したがって、コントローラーでは、次を使用して promise のコールバックを待つ必要があります。

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

最初の呼び出しにはわずかな遅延がありますが、次の呼び出しはキャッシュされたデータを使用して即座に行われます。

于 2013-10-25T20:01:14.217 に答える