Директива AngularJS не обновляет изменения переменных области
Я попытался написать небольшую директиву, чтобы обернуть ее содержимое другим файлом шаблона.
Этот код:
<layout name="Default">My cool content</layout>
Должен иметь этот вывод:
<div class="layoutDefault">My cool content</div>
Поскольку макет "По умолчанию" имеет этот код:
<div class="layoutDefault">{{content}}</div>
Здесь код директивы:
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));
});
}
}
});
Моя проблема:
Когда я использую переменные области видимости в шаблоне (в шаблоне макета или внутри тега макета), например. {{whatever}}
он просто работает изначально. Если я обновляю переменную whatever
, директива больше не обновляется. Вся функция связи будет только запускаться один раз.
Я думаю, что AngularJS не знает, что эта директива использует переменные области видимости и поэтому не обновляется. Но я не знаю, как исправить это поведение.
Ответы
Ответ 1
Вы должны создать переменную связанной области и посмотреть ее изменения:
return {
restrict: 'E',
scope: {
name: '='
},
link: function(scope) {
scope.$watch('name', function() {
// all the code here...
});
}
};
Ответ 2
Мне также понадобилось решение этой проблемы, и я использовал ответы в этом потоке, чтобы придумать следующее:
.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: '<div>{{data}}</div>',
// Remove all existing content of the directive.
transclude: true,
restrict: "E",
scope: {
data: '='
},
link: linker
};
}])
;
Включить в свой html:
<tp-report data='data'></tp-report>
Эта директива используется для динамической загрузки шаблонов отчетов на основе набора данных, полученного с сервера.
Он устанавливает часы в свойстве scope.data и всякий раз, когда это обновляется (когда пользователи запрашивают новый набор данных с сервера), он загружает соответствующую директиву для отображения данных.
Ответ 3
Вам нужно указать Angular, что ваша директива использует переменную области видимости:
Вам нужно связать некоторое свойство области с вашей директивой:
return {
restrict: 'E',
scope: {
whatever: '='
},
...
}
а затем $watch
it:
$scope.$watch('whatever', function(value) {
// do something with the new value
});
Для получения дополнительной информации обратитесь к Angular документации по директивам.
Ответ 4
Я нашел гораздо лучшее решение:
app.directive('layout', function(){
var settings = {
restrict: 'E',
transclude: true,
templateUrl: function(element, attributes){
var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default';
return constants.pathLayouts + layoutName + '.html';
}
}
return settings;
});
Единственным недостатком, который я вижу в настоящее время, является тот факт, что зашифрованные шаблоны получили свой собственный объем. Они получают значения от своих родителей, но вместо того, чтобы изменять значение в родительском, значение будет храниться в собственной, новой области. Чтобы этого избежать, теперь я использую $parent.whatever
вместо whatever
.
Пример:
<layout name="Default">
<layout name="AnotherNestedLayout">
<label>Whatever:</label>
<input type="text" ng-model="$parent.whatever">
</layout>
</layout>
Ответ 5
Вы должны хранить часы в своей области.
Вот как вы можете это сделать:
<layout layoutId="myScope"></layout>
Ваша директива должна выглядеть как
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)
Аналогичным образом вы можете моделировать свою директиву, поэтому, если модель автоматически обновится, ваш метод просмотра обновит вашу директиву.
Ответ 6
Я знаю это старую тему, но в случае, если какие-либо нахожу это как я:
Я использовал следующий код, когда мне нужна моя директива для обновления значений, когда обновлена "родительская область". Пожалуйста, во что бы то ни стало поправьте меня, если я что-то сделаю неправильно, поскольку я все еще изучаю angular, но это сделало то, что мне нужно;
:
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: '<span>{{ viewFrom() }} - {{ viewTo() }}</span>'
}
})
Ответ 7
Я не уверен, почему никто еще не предложил bindToController
, который удаляет все эти уродливые scopes and $watches.
Если вы используете Angular 1.4
Ниже приведен пример DOM:
<div ng-app="app">
<div ng-controller="MainCtrl as vm">
{{ vm.name }}
<foo-directive name="vm.name"></foo-directive>
<button ng-click="vm.changeScopeValue()">
changeScopeValue
</button>
</div>
</div>
Выполняет код controller
:
angular.module('app', []);
// main.js
function MainCtrl() {
this.name = 'Vinoth Initial';
this.changeScopeValue = function(){
this.name = "Vinoth has Changed"
}
}
angular
.module('app')
.controller('MainCtrl', MainCtrl);
// foo.js
function FooDirCtrl() {
}
function fooDirective() {
return {
restrict: 'E',
scope: {
name: '='
},
controller: 'FooDirCtrl',
controllerAs: 'vm',
template:'<div><input ng-model="name"></div>'
};
}
angular
.module('app')
.directive('fooDirective', fooDirective)
.controller('FooDirCtrl', FooDirCtrl);
A Fiddle, чтобы играть, здесь мы меняем значение области в controller
и автоматически directive updates on scope change
.
http://jsfiddle.net/spechackers/1ywL3fnq/