AngularJS directive does not update on scope variable changes
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've tried to write a small directive, to wrap its contents with another template file.
This code:
My cool content
Should have this output:
My cool content
Because the layout "Default" has this code:
{{content}}
Here the code of the directive:
app.directive('layout', function($http, $compile){ return { restrict: 'E', link: function(scope, element, attributes) { var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default'; $http.get(scope.constants.pathLayouts + layoutName + '.html') .success(function(layout){ var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g; var result = regexp.exec(layout); var templateWithLayout = result[1] + element.html() + result[2]; element.html($compile(templateWithLayout)(scope)); }); } }
});
My problem:
When I'm using scope variables in template (in layout template or inside of layout tag), eg. {{whatever}} it just work initially. If I update the whatever variable, the directive is not updated anymore. The whole link function will just get triggered once.
I think, that AngularJS does not know, that this directive uses scope variables and therefore it will not be updated. But I have no clue how to fix this behavior.
回答1:
You should create a bound scope variable and watch its changes:
I needed a solution for this issue as well and I used the answers in this thread to come up with the following:
.directive('tpReport', ['$parse', '$http', '$compile', '$templateCache', function($parse, $http, $compile, $templateCache) { var getTemplateUrl = function(type) { var templateUrl = ''; switch (type) { case 1: // Table templateUrl = 'modules/tpReport/directives/table-report.tpl.html'; break; case 0: templateUrl = 'modules/tpReport/directives/default.tpl.html'; break; default: templateUrl = ''; console.log("Type not defined for tpReport"); break; } return templateUrl; }; var linker = function (scope, element, attrs) { scope.$watch('data', function(){ var templateUrl = getTemplateUrl(scope.data[0].typeID); var data = $templateCache.get(templateUrl); element.html(data); $compile(element.contents())(scope); }); }; return { controller: 'tpReportCtrl', template: '
{{data}}
', // Remove all existing content of the directive. transclude: true, restrict: "E", scope: { data: '=' }, link: linker }; }]) ;
Include in your html:
This directive is used for dynamically loading report templates based on the dataset retrieved from the server.
It sets a watch on the scope.data property and whenever this gets updated (when the users requests a new dataset from the server) it loads the corresponding directive to show the data.
回答3:
You need to tell Angular that your directive uses a scope variable:
You need to bind some property of the scope to your directive:
The only disadvantage I see currently, is the fact that transcluded templates got their own scope. They get the values from their parents, but instead of change the value in the parent, the value get stored in an own, new child-scope. To avoid this, I am now using $parent.whatever instead of whatever.
Example:
回答5:
You should keep a watch on your scope.
Here is how you can do it:
Your directive should look like
app.directive('layout', function($http, $compile){ return { restrict: 'E', scope: { layoutId: "=layoutId" }, link: function(scope, element, attributes) { var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default'; $http.get(scope.constants.pathLayouts + layoutName + '.html') .success(function(layout){ var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g; var result = regexp.exec(layout); var templateWithLayout = result[1] + element.html() + result[2]; element.html($compile(templateWithLayout)(scope)); }); } } $scope.$watch('myScope',function(){ //Do Whatever you want },true)
Similarly you can models in your directive, so if model updates automatically your watch method will update your directive.
回答6:
I know this an old subject but in case any finds this like myself:
I used the following code when i needed my directive to update values when the "parent scope" updated. Please by all means correct me if am doing something wrong as i am still learning angular, but this did what i needed;
directive:
directive('dateRangePrint', function(){ return { restrict: 'E', scope:{ //still using the single dir binding From: '@rangeFrom', To: '@rangeTo', format: '@format' }, controller: function($scope, $element){ $scope.viewFrom = function(){ return formatDate($scope.From, $scope.format); } $scope.viewTo = function(){ return formatDate($scope.To, $scope.format); } function formatDate(date, format){ format = format || 'DD-MM-YYYY'; //do stuff to date... return date.format(format); } }, replace: true, // note the parenthesis after scope var template: '{{ viewFrom() }} - {{ viewTo() }}' } })
A Fiddle to play around, here we are changing the scope value in the controller and automatically the directive updates on scope change. http://jsfiddle.net/spechackers/1ywL3fnq/