AngularJS: cree una directiva que use ng-model

Intento crear una directiva que cree un campo de entrada con el mismo modelo ng que el elemento que crea la directiva.

Esto es lo que se me ocurrió hasta ahora:

HTML

    AngularJS Plunker  document.write("");      This scope value     

JavaScript

 var app = angular.module('plunker', []); app.controller('MainCtrl', function($scope) { $scope.name = "Felipe"; }); app.directive('myDirective', function($compile) { return { restrict: 'E', scope: { ngModel: '=' }, template: '
' + '
', replace: true, require: 'ngModel', link: function($scope, elem, attr, ctrl) { $scope.label = attr.ngModel; $scope.id = attr.ngModel; console.debug(attr.ngModel); console.debug($scope.$parent.$eval(attr.ngModel)); var textField = $('input', elem). attr('ng-model', attr.ngModel). val($scope.$parent.$eval(attr.ngModel)); $compile(textField)($scope.$parent); } }; });

Sin embargo, no estoy seguro de que esta sea la forma correcta de manejar este escenario, y hay un error que indica que mi control no se inicializa con el valor del campo objective ng-model.

Aquí hay un Plunker del código de arriba: http://plnkr.co/edit/IvrDbJ

¿Cuál es la forma correcta de manejar esto?

EDITAR : Después de eliminar el ng-model="value" de la plantilla, parece que funciona bien. Sin embargo, mantendré abierta esta pregunta porque quiero verificar que esta sea la manera correcta de hacerlo.

En realidad es bastante buena lógica, pero puedes simplificar las cosas un poco.

Directiva

 var app = angular.module('plunker', []); app.controller('MainCtrl', function($scope) { $scope.model = { name: 'World' }; $scope.name = "Felipe"; }); app.directive('myDirective', function($compile) { return { restrict: 'AE', //attribute or element scope: { myDirectiveVar: '=', //bindAttr: '=' }, template: '
' + '
', replace: true, //require: 'ngModel', link: function($scope, elem, attr, ctrl) { console.debug($scope); //var textField = $('input', elem).attr('ng-model', 'myDirectiveVar'); // $compile(textField)($scope.$parent); } }; });

Html con directiva

  This scope value    

CSS

 .some { border: 1px solid #cacaca; padding: 10px; } 

Puedes verlo en acción con este Plunker .

Esto es lo que veo:

  • Entiendo por qué quieres usar ‘ng-model’, pero en tu caso no es necesario. ng-model es vincular los elementos html existentes con un valor en el scope. Como usted mismo está creando una directiva, está creando un “nuevo” elemento html, por lo que no necesita ng-model.

EDITAR Como menciona Mark en su comentario, no hay ninguna razón por la que no puedas usar ng-model, solo para seguir con la convención.

  • Al crear explícitamente un scope en su directiva (un scope ‘aislado’), el scope de la directiva no puede acceder a la variable ‘nombre’ en el scope principal (por eso, creo, usted quería usar ng-model).
  • Eliminé ngModel de su directiva y lo reemplacé con un nombre personalizado que puede cambiar a lo que sea.
  • Lo que hace que todo siga funcionando es que ‘=’ inicie sesión en el scope. Verifique los documentos de docs bajo el encabezado de ‘scope’.

En general, sus directivas deberían usar el scope aislado (que lo hizo correctamente) y usar el scope del tipo ‘=’ si desea que un valor en su directiva se corresponda siempre con un valor en el ámbito principal.

Tomé un combo de todas las respuestas, y ahora tengo dos formas de hacerlo con el atributo ng-model:

  • Con un nuevo scope que copia ngModel
  • Con el mismo scope que hace una comstackción en el enlace
 var app = angular.module('model', []); app.controller('MainCtrl', function($scope) { $scope.name = "Felipe"; $scope.label = "The Label"; }); app.directive('myDirectiveWithScope', function() { return { restrict: 'E', scope: { ngModel: '=', }, // Notice how label isn't copied template: '
', replace: true }; }); app.directive('myDirectiveWithChildScope', function($compile) { return { restrict: 'E', scope: true, // Notice how label is visible in the scope template: '
', replace: true, link: function ($scope, element) { // element will be the div which gets the ng-model on the original directive var model = element.attr('ng-model'); $('input',element).attr('ng-model', model); return $compile(element)($scope); } }; }); app.directive('myDirectiveWithoutScope', function($compile) { return { restrict: 'E', template: '
', replace: true, link: function ($scope, element) { // element will be the div which gets the ng-model on the original directive var model = element.attr('ng-model'); return $compile($('input',element).attr('ng-model', model))($scope); } }; }); app.directive('myReplacedDirectiveIsolate', function($compile) { return { restrict: 'E', scope: {}, template: '', replace: true }; }); app.directive('myReplacedDirectiveChild', function($compile) { return { restrict: 'E', scope: true, template: '', replace: true }; }); app.directive('myReplacedDirective', function($compile) { return { restrict: 'E', template: '', replace: true }; });
 .some { border: 1px solid #cacaca; padding: 10px; } 
   
This scope value , label: "{{label}}"
  • With new isolate scope (label from parent):
  • With new child scope:
  • Same scope:
  • Replaced element, isolate scope:
  • Replaced element, child scope:
  • Replaced element, same scope:

Try typing in the child scope ones, they copy the value into the child scope which breaks the link with the parent scope.

Also notice how removing jQuery makes it so only the new-isolate-scope version works.

Finally, note that the replace+isolate scope only works in AngularJS >=1.2.0

no es tan complicado: en tu directorio, usa un alias: scope:{alias:'=ngModel'}

 .directive('dateselect', function () { return { restrict: 'E', transclude: true, scope:{ bindModel:'=ngModel' }, template:'' } 

en tu html, usa como siempre

  

Solo necesita ng-model cuando necesite acceder a $ viewValue o $ modelValue del modelo. Ver NgModelController . Y en ese caso, require: '^ngModel' .

Por lo demás, ver la respuesta de Roys .

Esta es una respuesta tardía, pero encontré esta publicación increíble sobre NgModelController , que creo que es exactamente lo que buscabas.

TL; DR : puede usar require: 'ngModel' y luego agregue NgModelController a su función de enlace:

 link: function(scope, iElement, iAttrs, ngModelCtrl) { //TODO } 

De esta forma, no es necesario realizar ningún corte: está utilizando el ng-model incorporado de Angular

No establecería el ngmodel a través de un atributo, puede especificarlo directamente en la plantilla:

 template: '
',

plunker : http://plnkr.co/edit/9vtmnw?p=preview

Desde Angular 1.5 es posible usar componentes. Los componentes son el camino a seguir y resuelve este problema fácilmente.

  app.component("myComponent", { templateUrl: "yourTemplate.html", controller: YourController, bindings: { ngModel: "=" } }); 

Dentro de YourController todo lo que necesitas hacer es:

 this.ngModel = "x"; //$scope.$apply("$ctrl.ngModel"); if needed 

Crear un scope aislado no es deseable. Evitaría usar el atributo de scope y hacer algo como esto. scope: true le da un nuevo scope de hijo pero no está aislado. A continuación, utilice el análisis para señalar una variable de ámbito local al mismo objeto que el usuario ha proporcionado al atributo ngModel.

 app.directive('myDir', ['$parse', function ($parse) { return { restrict: 'EA', scope: true, link: function (scope, elem, attrs) { if(!attrs.ngModel) {return;} var model = $parse(attrs.ngModel); scope.model = model(scope); } }; }]);