how to set the fieldname using a template in angularjs

二次信任 提交于 2019-12-22 20:05:52

问题


I want to have some interaction of the label for a field with the field validation properties. I created a plunker of a working example.

http://plnkr.co/edit/oNq5lmjtjRDaEkzKsrR2?p=preview

But in this example the fieldname is hardcoded.

I tryed to set the fieldname via scope variable into the template. This is not working:

http://plnkr.co/edit/yEl04xyFR5RWYCmI2bMH?p=preview

HTML:

<form name="myForm" ng-controller="ctrl">
  <fieldset field-name="{{model.fieldName}}" label-text="{{model.label}}" ng-model="model.value">
  <fieldset>
</form>

JavaScript:

var app = angular.module('myApp',[]);
app.directive('fieldset', function(){
  return {
    template: '<label ng-class="{invalid:$parent.myForm[{{fieldName}}].$invalid}">{{lbl}}</label>: ' +
              '<input name="{{fieldName}}" ng-model="value" required>',
    restrict : 'E',
    scope : {
              value : '=ngModel',
              lbl : '@labelText',
              fieldName : '@'
            },

    link : function(scope, elem, attrs) {
      console.log(scope.$parent.myForm);
    }        
  }
})

app.controller('ctrl', function($scope) {
  $scope.model = {
    fieldName : 'myField',
    label : 'Label for Field',
    value : 6
  } 
});

The fieldname in scope.$parent.myForm is '{{fieldName}}' instead of 'myField'. But in DOM the fieldname is assigned as expected. How can I solve this problem?


回答1:


Stewie is absolutely right. If the ng-model directive is created it will immediately be registered at the form. If you have a look at the angular sources you will find that the ngModels are registered by their current name in the array form:

if (control.$name) {
  form[control.$name] = control;
}

In your case this is '{{fieldName}}'. This name can not be changed, because it is the key in the array. So what can we do to change this? If we have a look at the FormController API we can see there are functions to remove ($removeControl) or add ($addControl) controls. Both functions require the NgModelController as input parameter. E.g. the NgModelController that angular has created for our input element. We have access to this controller by:

var modelController = inputElement.controller('ngModel');

I think we have now all information together to write the directive:

app.directive('fieldset', function(){
  return {
    template: '<label ng-class="{invalid:form[fieldName].$invalid}">{{lbl}}</label>: ' +
              '<input name="{{fieldName}}" ng-model="value" required>',
    restrict : 'E',
    require: '^form', // we need the parent NgFormController 
    scope : {
              value : '=ngModel',
              lbl : '@labelText',
              fieldName : '@'
         },

    link : function(scope, elem, attrs, formCtrl) {
       // get the NgModelController for the input element
       var modelCtrl = elem.find('input').controller('ngModel');

       // remove the ModelController from the FormController
       formCtrl.$removeControl(modelCtrl); 
       // set the right name
       modelCtrl.$name = scope.fieldName; 
       // add the namend ModelController to the FormController
       formCtrl.$addControl(modelCtrl); 

       // publish the FormController to the scope - so we don't need to mess around with the parent scope.
       scope.form = formCtrl;
    }
  }
})

I would also suggest publish the NgFormController to the directives scope. So you may write your css condition:

form[fieldName].$invalid

instad of:

$parent.myForm[fieldName].$invalid

It is more generic, because you don't need to know the name of your form.

PLUNKR




回答2:


ng-form directive is linked before the view expressions on child elements are interpolated. This is why you can't have dynamic field names if you want to make use of angular form validation.

But, when creating the form fields dynamically (through ng-repeat), the fields don't really need to have unique name attribute. That's because you're allowed to wrap each form element into it's own private ng-form scope:

PLUNKER

app.controller('MainCtrl', function($scope) {
  $scope.fields = [
    {label: 'First Name', required: false},
    {label: 'Last Name', required: false},
    {label: 'Email', required: true}
  ];

});
<body ng-controller="MainCtrl">

  <form name="form" class="form-horizontal" ng-submit="save()" novalidate>

    <div class="form-group" ng-repeat="field in fields" ng-form="form">
      <label class="control-label" ng-class="{required: field.required}">{{field.label}}</label>
      <input 
        type="text" 
        name="field" 
        ng-model="field.value" 
        class="form-control" 
        ng-required="field.required"
      />
      <span 
        class="help-block error"
        ng-show="form.field.$dirty && form.field.$error.required"
      >{{field.label}} is required</span>
    </div>

    <div class="form-group">
      <button type="submit" class="btn btn-primary">Save changes</button>
      <button type="button" class="btn">Cancel</button>
    </div>

  </form>

</body>


来源:https://stackoverflow.com/questions/21201087/how-to-set-the-fieldname-using-a-template-in-angularjs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!