instalar ejemplos angularjs

ejemplos - instalar angularjs



¿Puedo usar ng-model con un alcance aislado? (4)

Estoy creando una directiva simple ui-datetime. Divide el objeto Date de JavaScript en las partes _date, _hours y _minutes. _date usa jquery ui datepicker, _hours y _minutes - entradas numéricas.

angular.module("ExperimentsModule", []) .directive("uiDatetime", function () { return { restrict: ''EA'', replace: true, template: ''<div class="ui-datetime">'' + ''<input type="text" ng-model="_date" class="date">'' + ''<input type="number" ng-model="_hours" min="0" max="23" class="hours">'' + ''<input type="number" ng-model="_minutes" min="0" max="59" class="minutes">'' + ''<br />Child datetime1: {{datetime1}}'' + ''</div>'', require: ''ngModel'', scope: true, link: function (scope, element, attrs, ngModelCtrl) { var elDate = element.find(''input.date''); ngModelCtrl.$render = function () { var date = new Date(ngModelCtrl.$viewValue); var fillNull = function (num) { if (num < 10) return ''0'' + num; return num; }; scope._date = fillNull(date.getDate()) + ''.'' + fillNull(date.getMonth() + 1) + ''.'' + date.getFullYear(); scope._hours = date.getHours(); scope._minutes = date.getMinutes(); }; elDate.datepicker({ dateFormat: ''dd.mm.yy'', onSelect: function (value, picker) { scope._date = value; scope.$apply(); } }); var watchExpr = function () { var res = scope.$eval(''_date'').split(''.''); if (res.length == 3) return new Date(res[2], res[1] - 1, res[0], scope.$eval(''_hours''), scope.$eval(''_minutes'')); return 0; }; scope.$watch(watchExpr, function (newValue) { ngModelCtrl.$setViewValue(newValue); }, true); } }; }); function TestController($scope) { $scope.datetime1 = new Date(); }

jsfiddle

En github: https://github.com/andreev-artem/angular_experiments/tree/master/ui-datetime

Por lo que yo entiendo, la mejor práctica al crear un nuevo componente es usar un alcance aislado.

Cuando traté de usar el alcance aislado, nada funciona. ngModel. $ viewValue === undefined.

Cuando traté de usar el nuevo ámbito (mi ejemplo, no tan bueno variante imho) - ngModel utiliza el valor en el ámbito recién creado.

Por supuesto, puedo crear directivas con alcance aislado y trabajar con ngModel value a través de "= expression" ( example ). Pero creo que trabajar con ngModelController es una mejor práctica.

Mis preguntas:

  1. ¿Puedo usar ngModelController con alcance aislado?
  2. Si no es posible, ¿qué solución es mejor para crear dicho componente?

Creo que tuve el mismo problema y encontré una solución parcial pero utilizable.

Entonces, el problema tiene varias partes:

  1. su directiva personalizada quiere algunas propiedades privadas, es decir, alcance aislado
  2. El nodo DOM solo puede tener un alcance, todas las directivas lo comparten
  3. ngModel = "algo" se une a "algo" en ese ámbito compartido (aislado), y este es el problema real

Entonces, mi primer paso fue reescribir mi directiva para usar scope:true lugar de scope:{...} (en realidad, eso era un requisito, porque quería usar algunas propiedades de alcance global dentro del contenido transcluido de mi directiva): cosas como attrs.$observe() , $scope.$parent.$watch() , etc. ayudado.

Luego, en compile() reinvierto ngModel para la propiedad del alcance principal: attrs.$set(''ngModel'', ''$parent.'' + attrs.ngModel, false) . Y eso es todo.

Aquí está mi directiva, con código no esencial despojado:

angular.module(''App'', []).directive(''dir'', function () { return { /* This one is important: */ scope:true, compile:function (element, attrs, transclude) { /* The trick is here: */ if (attrs.ngModel) { attrs.$set(''ngModel'', ''$parent.'' + attrs.ngModel, false); } return function ($scope, element, attrs, ngModel) { // link function body }; } }; });


Haga que su directiva se ejecute con una prioridad mayor que ngModel y corrija el enlace del modelo para su alcance aislado. Elegí una prioridad de ''100'' que es el mismo nivel que la directiva de entrada, después de manipulaciones de plantilla de alta prioridad como ngRepeat pero antes del valor predeterminado de 0, que es lo que usa ngModel.

Aquí está el código de ejemplo:

myDirective = function() { return { compile: function(tElement, tAttrs, transclude) { // Correct ngModel for isolate scope if (tAttrs.ngModel) { tAttrs.$set(''model'', tAttrs.ngModel, false); tAttrs.$set(''ngModel'', ''model'', false); } return { post: function(scope, iElement, iAttrs, controller) { // Optionally hook up formatters and parsers controller.$formatters.push(function(value) { // ... }) // Render return controller.$render = function() { if (!controller.$viewValue) { return; } angular.extend(scope, controller.$viewValue); }; } }; }, priority: 100, require: ''^ngModel'', scope: { model: ''='' }, }; }

Durante la compilación, la directiva verifica si el atributo ngModel está presente. Esta comprobación funciona en el valor normalizado utilizando los Attributes de Angular. Si el atributo está presente, se reemplaza por ''modelo'' (no ''ngModel''), que es el nombre vinculado a datos en nuestro aislado. Sin embargo, también debemos crear un atributo para que Angular pueda realizar el enlace de datos para nosotros. Ambos atributos pueden modificarse (a su elección) con un parámetro false que no modifica el DOM.


Prueba una versión de esto:

.directive(''myDir'', function() { return { restrict: ''EA'', scope: { YYY: ''=ngModel'' }, require: ''ngModel'', replace: true, template: function render(element, attrs) { var type = attrs.type || ''text''; var required = attrs.hasOwnProperty(''required'') ? " required=''required''" : ""; return "<input ng-model=''YYY'' type="'' + type + ''" + required + '' />''; } }; });


Reemplazar el scope: true con el scope: { datetime1: ''=ngModel''} en su primer violín parece funcionar bien - fiddle . Lamentablemente, el enlace a su violín de "ejemplo" está roto, por lo que no estoy seguro de lo que ha intentado allí.

Por lo tanto, parece que ngModelController se puede usar con un alcance aislado.

Aquí hay un violín más pequeño que usa ng-model en el HTML / vista, un alcance aislado y $ setViewValue en la función de enlace: fiddle .

Actualización : Acabo de descubrir algo bastante interesante: si a la propiedad del alcance del aislamiento se le asigna un nombre diferente, por ejemplo, dt1 en vez de datetime1- scope: { dt1: ''=ngModel''} , ¡ya no funciona! Supongo que cuando require: ''ngModel'' , ngModelController usa el nombre en el HTML / vista (es decir, el valor del atributo ng-model) para crear una propiedad en el alcance aislado. Entonces, si especificamos el mismo nombre en el hash del objeto, todo está bien. Pero si especificamos un nombre diferente, esa nueva propiedad de ámbito (p. Ej., Dt1) no está asociada con el ngModelController que necesitamos.

Aquí hay un violín actualizado .