Angular passing scope to ng-include

2019-01-13 20:04发布

I have a controller that I wrote that I use in multiple places in my app with ng-include and ng-repeat, like this:

<div
  ng-repeat="item in items"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>

In the controller/template, I expect the item value to exist, and the whole thing is built around this idea. Now, though, I need to use the controller in a slightly different way, without the ng-repeat, but still need to be able to pass in an item. I saw ng-init and thought it could do what I needed, like this:

<div
  ng-init="item = leftItem"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>
<div
  ng-init="item = rightItem"
  ng-include="'item.html'"
  ng-controller="ItemController"
></div>

But that does not seem to be working. Anyone have any ideas how I can pass in a variable for scope in a singular instance like this?

Edit: The controller above this is loading in the leftItem and rightItem values, something like this:

.controller('MainController', function($scope, ItemModel) {
    ItemModel.loadItems()
        .then(function(items) {
            $scope.$apply(function() {
                $scope.leftItem = items.left;
                $scope.rightItem = items.right;
            });
        });
});

8条回答
该账号已被封号
2楼-- · 2019-01-13 20:31

ng-init is better for this I think.

<div ng-include='myFile.html' ng-init="myObject = myCtrl.myObject; myOtherObject=myCtrl.myOtherObject"/>
查看更多
相关推荐>>
3楼-- · 2019-01-13 20:40

Late to the party, but there is a little angular 'hack' to achieve this without implementing a dumb directive.

Adding a built-in directive that will extend your controller's scope (like ng-if) everywhere you use the ng-include will actually let you isolate the variable name for all the included scopes.

So:

<div ng-include="'item.html'"
  ng-if="true"
  onload="item = rightItem">
</div>
<div ng-include="'item.html'"
  ng-if="true"
  onload="item = leftItem">
</div>

You can then bind your template item.html to the item variable several times with different items.

Here is a plunker to achieve what you want

The problem was the item keeps changing in the controller scope that only holds one reference to item variable which is erased at each onload instruction.

Introducing a directive that extends the current scope, lets you have a isolated scope for all the ng-include. As a consequence the item reference is preserved and unique in all extended scope.

查看更多
贼婆χ
4楼-- · 2019-01-13 20:41

LOVE @Tanin's answer. solves so many problems at once and in a very elegant way. For those of you like me who don't know Coffeescript, here's the javascript...

NOTE: For reasons I'm too new to understand, this code requires you to quote your template name once, rather than ng-include's requirement to twice-quote your template names, ie. <div ng-include-template="template-name.html" ... > instead of <div ng-include-template="'template-name.html'" ... >

.directive('ngIncludeTemplate', function() {  
  return {  
    templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },  
    restrict: 'A',  
    scope: {  
      'ngIncludeVariables': '&'  
    },  
    link: function(scope, elem, attrs) {  
      var vars = scope.ngIncludeVariables();  
      Object.keys(vars).forEach(function(key) {  
        scope[key] = vars[key];  
      });  
    }  
  }  
})
查看更多
我欲成王,谁敢阻挡
5楼-- · 2019-01-13 20:49

I ended up rewriting it into a directive and binding the needed value in the scope with

scope: {
    item: '='
}
查看更多
地球回转人心会变
6楼-- · 2019-01-13 20:51

You can use the onload attribute that ngInclude provides to do this:

<div ng-include="'item.html'"
     ng-controller="ItemController"
     onload="item = rightItem">
</div>

Link to the documentation.

EDIT

Try doing something like this in the parent scope:

$scope.dataHolder = {};

Then when the asynchronous data is received, store the data on dataHolder:

$scope.dataHolder.leftItem = items.leftItem;
$scope.dataHolder.rightItem = items.rightItem;

Now when ng-include loads the template, it will create a child scope that inherits the properties of the parent. So $scope.dataHolder will be defined in this child scope (initially as an empty object). But when your asynchronous data is received, the reference to the empty object should then contain the newly received data.

查看更多
相关推荐>>
7楼-- · 2019-01-13 20:51

Above will not work for second level attributes, like <div ng-include-template=... ng-include-variables="{ id: var.id }">. Notice the var.id.

Updated directive(ugly, but works):

.directive('ngIncludeTemplate', function() {
  return {
    templateUrl: function(elem, attrs) { return attrs.ngIncludeTemplate; },
    restrict: 'A',
    scope: {
      'ngIncludeVariables': '&'
    },
    link: function(scope, elem, attrs) {
      var cache = scope.ngIncludeVariables();
      Object.keys(cache).forEach(function(key) {
        scope[key] = cache[key];
      });

      scope.$watch(
        function() {
          var val = scope.ngIncludeVariables();
          if (angular.equals(val, cache)) {
            return cache;
          }
          cache = val;
          return val;
        },
        function(newValue, oldValue) {
          if (!angular.equals(newValue, oldValue)) {
            Object.keys(newValue).forEach(function(key) {
              scope[key] = newValue[key];
            });
          }
        }
      );

    }
  };
});
查看更多
登录 后发表回答