javascript - español - directivas personalizadas angularjs
La directiva AngularJS no se actualiza en los cambios de variables de alcance (9)
Debe crear una variable de ámbito enlazado y ver sus cambios:
return {
restrict: ''E'',
scope: {
name: ''=''
},
link: function(scope) {
scope.$watch(''name'', function() {
// all the code here...
});
}
};
Intenté escribir una pequeña directiva para envolver su contenido con otro archivo de plantilla.
Este código:
<layout name="Default">My cool content</layout>
Debería tener esta salida:
<div class="layoutDefault">My cool content</div>
Porque el diseño "Predeterminado" tiene este código:
<div class="layoutDefault">{{content}}</div>
Aquí el código de la directiva:
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));
});
}
}
});
Mi problema:
Cuando estoy usando variables de ámbito en la plantilla (en la plantilla de diseño o dentro de la etiqueta de diseño), por ej. {{whatever}}
solo funciona inicialmente. Si actualizo whatever
variable, la directiva ya no se actualiza. Toda la función de enlace se activará una vez.
Creo que AngularJS no sabe que esta directiva utiliza variables de ámbito y, por lo tanto, no se actualizará. Pero no tengo ni idea de cómo solucionar este comportamiento.
Debe decirle a Angular que su directiva usa una variable de ámbito:
Debe vincular alguna propiedad del alcance a su directiva:
return {
restrict: ''E'',
scope: {
whatever: ''=''
},
...
}
y luego $watch
:
$scope.$watch(''whatever'', function(value) {
// do something with the new value
});
Consulte la documentación angular de las directivas para obtener más información.
Debe vigilar su alcance.
Aquí sabrás como podrás hacerlo:
<layout layoutId="myScope"></layout>
Su directiva debe verse como
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)
De forma similar, puede incluir modelos en su directiva, por lo que si el modelo se actualiza automáticamente, su método de observación actualizará su directiva.
Encontré una solución mucho mejor:
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;
});
La única desventaja que veo actualmente es que las plantillas transclusas tienen su propio alcance. Obtienen los valores de sus padres, pero en lugar de cambiar el valor en el elemento primario, el valor se almacena en un ámbito propio propio, nuevo. Para evitar esto, ahora estoy usando $parent.whatever
lugar de whatever
.
Ejemplo:
<layout name="Default">
<layout name="AnotherNestedLayout">
<label>Whatever:</label>
<input type="text" ng-model="$parent.whatever">
</layout>
</layout>
Necesitaba una solución para este problema también y usé las respuestas en este hilo para llegar a lo siguiente:
.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
};
}])
;
Incluya en su html:
<tp-report data=''data''></tp-report>
Esta directiva se usa para cargar dinámicamente plantillas de informes basadas en el conjunto de datos recuperados del servidor.
Establece un control sobre la propiedad scope.data y cada vez que se actualiza (cuando los usuarios solicitan un nuevo conjunto de datos del servidor) carga la directiva correspondiente para mostrar los datos.
No estoy seguro de por qué nadie ha sugerido bindToController
que elimina todos estos horribles scopes and $watches.
Si está utilizando Angular 1.4
A continuación se muestra un DOM de muestra:
<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>
Sigue el código del 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>'',
bindToController: true
};
}
angular
.module(''app'')
.directive(''fooDirective'', fooDirective)
.controller(''FooDirCtrl'', FooDirCtrl);
Un Fiddle para jugar, aquí estamos cambiando el valor del alcance en el controller
y automáticamente las directive updates on scope change
. http://jsfiddle.net/spechackers/1ywL3fnq/
Podemos intentar esto
$scope.$apply(function() {
$scope.step1 = true;
//scope.list2.length = 0;
});
Sé que este es un tema antiguo, pero en caso de que alguien lo encuentre como yo:
Usé el siguiente código cuando necesitaba mi directiva para actualizar los valores cuando se actualizaba el "alcance principal". Por favor, corríjanme si estoy haciendo algo mal ya que todavía estoy aprendiendo angular, pero esto hizo lo que necesitaba;
directiva:
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>''
}
})
Una solución simple es hacer que el objeto variable de ámbito. Luego acceda al contenido con {{ whatever-object.whatever-property }}
. La variable no se actualiza porque JavaScript pasa tipo Primitivo por valor . Mientras que los Objetos se pasan por referencia lo cual resuelve el problema.