personalizadas directivas crear javascript angularjs angularjs-directive

javascript - crear - directivas personalizadas angular 5



Agregue ng-click dinámicamente en la función de enlace de directivas (7)

Intento crear una directiva que permita definir un elemento como clicable o no, y se definiría así:

<page is-clickable="true"> transcluded elements... </page>

Quiero que el HTML resultante sea:

<page is-clickable="true" ng-click="onHandleClick()"> transcluded elements... </page>

Mi implementación de directiva se ve así:

app.directive(''page'', function() { return { restrict: ''E'', template: ''<div ng-transclude></div>'', transclude: true, link: function(scope, element, attrs) { var isClickable = angular.isDefined(attrs.isClickable) && scope.$eval(attrs.isClickable) === true ? true : false; if (isClickable) { attrs.$set(''ngClick'', ''onHandleClick()''); } scope.onHandleClick = function() { console.log(''onHandleClick''); }; } }; });

Puedo ver que después de agregar el nuevo atributo, Angular no sabe sobre el ng-click , por lo que no está disparando. Intenté agregar una $compile después de configurar el atributo, pero causa un ciclo infinito enlace / compilación.

Sé que puedo verificar dentro de la función onHandleClick() si el valor de isClickable es true , pero tengo curiosidad de cómo se puede hacer esto al agregar dinámicamente un evento ng-click porque es posible que deba hacer esto con otros muchos ng-* directivas y no quiero agregar una sobrecarga innecesaria. ¿Algunas ideas?


Mejor solución (nueva):

Después de leer los documentos angulares, me encontré con esto:

Puede especificar la plantilla como una cadena que representa la plantilla o como una función que toma dos argumentos tElement y tAttrs (descritos en la API de la función de compilación a continuación) y devuelve un valor de cadena que representa la plantilla.

Así que mi nueva directiva se ve así: (creo que esta es la forma "Angular" adecuada para abordar este tipo de cosas)

app.directive(''page'', function() { return { restrict: ''E'', replace: true, template: function(tElement, tAttrs) { var isClickable = angular.isDefined(tAttrs.isClickable) && eval(tAttrs.isClickable) === true ? true : false; var clickAttr = isClickable ? ''ng-click="onHandleClick()"'' : ''''; return ''<div '' + clickAttr + '' ng-transclude></div>''; }, transclude: true, link: function(scope, element, attrs) { scope.onHandleClick = function() { console.log(''onHandleClick''); }; } }; });

Observe la nueva función de plantilla. Ahora estoy manipulando la plantilla dentro de esa función antes de que se compile.

Solución alternativa (antigua):

Se agregó replace: true para deshacerse del problema del bucle infinito al volver a compilar la directiva. Y luego en la función de enlace simplemente recompilo el elemento después de agregar el nuevo atributo. Una cosa a tener en cuenta, porque tenía una directiva ng-transclude en mi elemento, necesitaba eliminar eso para que no intentara transcluir nada en la segunda compilación, porque no hay nada que transcluir.

Así es como se ve mi directiva ahora:

app.directive(''page'', function() { return { restrict: ''E'', replace: true, template: ''<div ng-transclude></div>'', transclude: true, link: function(scope, element, attrs) { var isClickable = angular.isDefined(attrs.isClickable) && scope.$eval(attrs.isClickable) === true ? true : false; if (isClickable) { attrs.$set(''ngClick'', ''onHandleClick()''); element.removeAttr(''ng-transclude''); $compile(element)(scope); } scope.onHandleClick = function() { console.log(''onHandleClick''); }; } }; });

No creo que volver a compilar la plantilla por segunda vez sea ideal, por lo que creo que todavía hay una forma de hacerlo antes de compilar la plantilla la primera vez.


Respuesta actualizada

"The Angular Way" no sería manipulación MAN manual. Entonces, debemos deshacernos de agregar y eliminar atributos.

DEMO

Cambiar la plantilla a:

template: ''<div ng-click="onHandleClick()" ng-transclude></div>''

Y en la directiva, compruebe que el atributo isClickable decida qué hacer cuando se hace clic en él:

link: function(scope, element, attrs) { var isClickable = angular.isDefined(attrs.isClickable) && scope.$eval(attrs.isClickable) === true ? true : false; scope.onHandleClick = function() { if (!isClickable) return; console.log(''onHandleClick''); }; }

También puede poner el atributo isClickable en el alcance de la directiva para que pueda cambiar dinámicamente su comportamiento.

Respuesta anterior (incorrecta)

link se ejecuta después de compilar la plantilla. Use el controller para las alteraciones en la plantilla antes de compilar:

app.directive(''page'', function() { return { restrict: ''E'', template: ''<div ng-transclude></div>'', transclude: true, controller: function(scope, element, attrs) { // your code } }; });


Creo que debería ser mejor así:

app.directive(''page'', function() { return { restrict: ''E'', template: ''<div ng-transclude></div>'', transclude: true, link: function(scope, element, attrs) { var isClickable = angular.isDefined(attrs.isClickable) && scope.$eval(attrs.isClickable) === true ? true : false; if (isClickable) { angular.element(element).on(''click'', scope.onHandleClick); } scope.onHandleClick = function() { console.log(''onHandleClick''); }; } }; });


Esta es mi versión de la solución @DiscGolfer donde agregué soporte para atributos también.

.directive("page", function() { return { transclude: true, replace: true, template: function(tElement, tAttr) { var isClickable = angular.isDefined(tAttrs.isClickable) && eval(tAttrs.isClickable) === true ? true : false; if (isClickable) { tElement.attr("ng-click", "onHandleClick()"); } tElement.attr("ng-transclude", ""); if (tAttr.$attr.page === undefined) { return "<" + tElement[0].outerHTML.replace(/(^</w+|/w+>$)/g, ''div'') + ">"; } else { tElement.removeAttr(tAttr.$attr.page); return tElement[0].outerHTML; } } };

Se proporciona una muestra más genérica y completa http://plnkr.co/edit/4PcMnpq59ebZr2VrOI07?p=preview

El único problema con esta solución es que replace está en desuso en AngularJS.


Siempre puedes modificar tu ng-click para que se vea así:

ng-click="isClickable && someFunction()"

No se requiere directiva personalizada :)

Aquí hay una demostración de JSFiddle: http://jsfiddle.net/robianmcd/5D4VR/


HTML

<div page is-clickable="true">hhhh</div>

JS

app.directive(''page'', function($compile) { return { priority:1001, // compiles first terminal:true, // prevent lower priority directives to compile after it template: ''<div ng-transclude></div>'', transclude: true, compile: function(el,attr,transclude) { el.removeAttr(''page''); // necessary to avoid infinite compile loop var contents = el.contents().remove(); var compiledContents; return function(scope){ var isClickable = angular.isDefined(attr.isClickable)?scope.$eval(attr.isClickable):false; if(isClickable){ el.attr(''ng-click'',''onHandleClick()''); var fn = $compile(el); fn(scope); scope.onHandleClick = function() { console.log(''onHandleClick''); }; } if(!compiledContents) { compiledContents = $compile(contents, transclude); } compiledContents(scope, function(clone, scope) { el.append(clone); }); }; }, link:function(scope){ } }; });

crédito a Erstad.Stephen e Ilan Frumer

Por cierto con restringir: ''E'' el navegador se colgó :(


module.factory("ibDirectiveHelpers", ["ngClickDirective", function (ngClick) { return { click: function (scope, element, fn) { var attr = {ngClick: fn}; ngClick[0].compile(element, attr)(scope, element, attr); } }; }]);

utilizar:

module.controller("demoController",["$scope","$element","ibDirectiveHelpers",function($scope,$element,ibDirectiveHelpers){ $scope.demoMethod=function(){console.log("demoMethod");}; ibDirectiveHelpers.click($scope,$element,"demoMethod()");//uses html notation //or ibDirectiveHelpers.click($scope,$element,function(){$scope.demoMethod();});//uses inline notation }]