Angularjs做“主 - 细节”指令间通信的“正确”方式是什么

时间:2014-12-04 10:57:48

标签: angularjs angularjs-directive

我有一个显示“主”项目列表的指令,当用户点击其中一个项目时,我希望页面上的任何“详细信息”指令(可能有多个)更新详细信息当前选择的“主”项目。

目前我正在使用id和href属性作为“详细信息”指令的一种方式来查找其相应的主指令。但我的印象是,这不是有角度的方式,所以如果不是,那么什么是更好的解决方案呢?

我很欣赏通常在引发指令之间的相互通信问题时,显而易见的解决方案是使用require:“^ master-directive”或使用服务,但在这种情况下,指令不在相同的层次结构,我不认为使用服务是合适的,因为它会使解决方案更复杂。

这是一些说明性代码,显示了我目前正在做的事情。

<div>                                                                                                                                      
  <master-list id="master1"></master-list>                                                                     
</div>                                                                                                                                                 
<div>                                                                                                                                
  <details-item href="#master1" ></details-item>                             
</div>                                                                                                                                                 

在master-list指令中选择一个项目时,我设置一个属性来指示当前选择的主项目:

attrs.$set('masterListItemId',item.id);

在details-item指令的链接函数中我做:

if (attrs.href) {
  var id = attrs.href.split('#')[1];
  var masterList = angular.element(document.getElementById(id));                                                                             
  if (masterList) {
    var ctrl = masterList.controller('masterList');
    ctrl.attrs().$observe('masterListItemId',function(value) {
        attrs.$set('detailItemId',value);
    });
  }
}

attrs.$observe('detailItemId',function(id) {
    // detail id changed so refresh
});

使我不能使用服务进行指令间通信的一个方面是(在我的情况下)可能在同一页面上有多个'masterList'元素,如果这些元素在逻辑上与同一服务相关,该服务最终将管理多个masterList元素的选择状态。如果您认为每个masterList元素都有一个关联的detailItem,那么如何更新正确的detailItem元素以反映其关联的masterList的状态?

<div>                                                                                                                                      
  <master-list id="master1"></master-list>                                                                     
</div>                                                                                                                                                 
<div>                                                                                                                                      
  <master-list id="master2"></master-list>                                                                     
</div>                                                                                                                                                 
<div>                                                                                                                                
  <details-item href="#master1" ></details-item>                             
</div>                                                                                                                                                 
<div>                                                                                                                                
  <details-item href="#master2" ></details-item>                             
</div>                                                                                                                                                 

最后我试图使用指令,而不是使用控制器代码(因为已经明智地建议),因为我真的很喜欢masterList和它关联的detailItems之间的关系在html中被'声明',而不是javascript因此,通过单独查看html,这些元素之间的关系是显而易见的。

这一点尤其重要,因为我有足够的知识来创建使用指令的html ui的用户,但理解javascript是一个太过分了。

是否有更好的方法来实现与角度做事方式更加一致的相同事物?

2 个答案:

答案 0 :(得分:2)

我想我会为此使用服务。该服务将保存您关注的详细数据,因此它看起来像这样。

master-list模板中,您可能会有类似项目列表的内容:

<ul>
  <li ng-repeat"item in items"><a ng-click="select(item)">{{item.name}}</a></li>
</ul>

......或类似的。

然后在您的指令中,您将拥有(仅部分代码)

.directive('masterList',function(DetailsService) {
   return {
     controller: function($scope) {
       $scope.select = function(item) {
         DetailsService.pick(item);  // or however you get and retrieve data
       };
     }
   };
})
.directive('detailsItem',function(DetailsService) {
  return {
    controller: function($scope) { // you could do this in the link as well
      $scope.data = DetailsService.item;
    }
  };
})

然后在您的详细信息模板中使用data

<div>Details for {{data.name}}</div>
<ul>
  <li ng-repeat="detail in data.details">{{detail.description}}</li>
</ul>

或类似的东西。

我不会使用idhref,而是使用服务来检索,保存和传递信息。

编辑:

这是一个jsfiddle,它在2个控制器之间执行,但指令是相同的想法 http://jsfiddle.net/u3u5kte7/

编辑: 如果您想拥有多个主服务器和详细信息,请保持模板不变,但更改指令控制器和服务,如下所示:

.directive('masterList',function(DetailsService) {
   return {
     controller: function($scope) {
       $scope.select = function(item) {
         DetailsService.pick($scope.listId,item);  // or however you get and retrieve data
       };
     }
   };
})
.directive('detailsItem',function(DetailsService) {
  return {
    controller: function($scope) { // you could do this in the link as well
      $scope.data = DetailsService.get($scope.listId).item;
    }
  };
})
.factory('DetailsService',function(){
  var data = {};
  return {
    pick: function(id,item) {
      data[id] = data[id] || {item:{}};
      // set data[id].item to whatever you want here
    },
    get: function(id) {
      data[id] = data[id] || {item:{}};
      return data[id];
    }
  };
})

答案 1 :(得分:2)

如果没有指令,我会选择完全不同的方法。指令是DOM操作的理想选择。但在这种情况下,我会坚持只使用模板和管理所有数据的控制器,并摆脱指令。使用ng-repeat重复项目

请查看这个小提琴,以获取此示例:http://jsfiddle.net/wbrand/2xrne4k3

模板:

<div ng-controller="ItemController as ic">
    Masterlist:
    <ul><li ng-repeat="item in ic.items" ng-click="ic.selected($index)">{{item.prop1}}</li></ul>
    Detaillist:
    <ul><li ng-repeat="item in ic.items" >
        {{item.prop1}} 
        <span ng-if="item.selected">SELECTED!</span>
     </li></ul>
</div>

控制器:

angular.module('app',[]).controller('ItemController',function(){
this.items = [{prop1:'some value'},{prop1:'some other value'}]
this.selectedItemIndex;

this.selected = function(index){
    this.items.forEach(function(item){
        item.selected = false;
    })
    this.items[index].selected = true
}
})