erin c
erin c

Reputation: 1345

dynamically naming input controls in angularjs

I have a input element which I am trying to name dynamically, but I had no luck so far using angular js.

neither

<input type="text" name="resource.Name" ng-model="resource.Value" ng-required="resource.IsRequired">

nor

<input type="text" name="{{resource.Name}}" ng-model="resource.Value" ng-required="resource.IsRequired">

works for me. When I try to access name attribute it always comes back as resource.Name instead of the value it contains. The main reason I am trying to name this input control is validation, if user does not enter text in the field, I would like to tell them which textbox is required. If there is any angular directive I can use for that purpose I am ready to use them as well.

Can anyone tell me how to achieve this.

My validation function is as follows:

 window.resourcesValidation = function ($scope, $rootScope, $alert) {
        var subscriptions = $scope.scopeData.Subscriptions;
        var isValidated = true;

        if ($scope.resourceDataForm && $scope.resourceDataForm.$error && $scope.resourceDataForm.$error.required) {
            var missingFields = [];
            angular.forEach($scope.resourceDataForm.$error.required, function (value, key) {
                isValidated = false;
                missingFields.push("-" + value.$name);
            });

            $alert.error("Please fill in the all required fields.\r\n" + missingFields.join("\r\n"));
        }

        if (isValidated)
            $scope.saveChanges(subscriptions);

        return isValidated;
    };

Upvotes: 3

Views: 6562

Answers (5)

SoEzPz
SoEzPz

Reputation: 15912

I could not find the answer that satisfied some or all of these needs. This is what I came up with.

There may be a better way, so please share your thoughts.
I am using Angularjs 1.3.0-beta.8

I have a form with multi-nested directives that all contain input(s), select(s), etc... These elements are all enclosed in ng-repeats, and dynamic string values.

This is how to use the directive:

<form name="myFormName">
  <nested directives of many levels>
    ex: <input ng-repeat=(index, variable) in variables" type="text"
               my-name="{{ variable.name + '/' + 'myFormName' }}"
               ng-model="variable.name" required />
    ex: <select ng-model="variable.name" ng-options="label in label in {{ variable.options }}"
                my-name="{{ variable.name + '/' + 'myFormName' }}"
        </select>
</form>

Note: you can add and index to the string concatenation if you need to serialize perhaps a table of inputs; which is what I did. However, dynamic name inputs means you may not know the name of the form input, so how would you call $scope.formName.??????. You could iterate of the $scope.formName object to get keys that match a certain value. That means string concatenation like this:

my-name="{{ dynamicString + hello + '/' + 'myFormName' }}"

Then in $scope.myFormName you would find any form input name by just iterating over the object and gathering any keys that included 'hello'.

app.directive('rsName', function(){

  var rsNameError = "rsName directive error: "

  return {
    restrict:'A', // Declares an Attributes Directive.
    require: 'ngModel', // ngModelController.

    link: function( scope, elem, attrs, ngModel ){
      if( !ngModel ){ return } // no ngModel exists for this element

      // check rsName input for proper formatting ex. something/something
      checkInputFormat(attrs);

      var inputName = attrs.rsName.match('^\\w+').pop(); // match upto '/'
      assignInputNameToInputModel(inputName, ngModel);

      var formName = attrs.rsName.match('\\w+$').pop(); // match after '/'
      findForm(formName, ngModel, scope);
    } // end link
  } // end return

  function checkInputFormat(attrs){
    if( !/\w\/\w/.test(attrs.rsName )){
      throw rsNameError + "Formatting should be \"inputName/formName\" but is " + attrs.rsName
    }
  }

  function assignInputNameToInputModel(inputName, ngModel){
    ngModel.$name = inputName
  }

  function addInputNameToForm(formName, ngModel, scope){
    scope[formName][ngModel.$name] = ngModel; return
  }

  function findForm(formName, ngModel, scope){
    if( !scope ){ // ran out of scope before finding scope[formName]
      throw rsNameError + "<Form> element named " + formName + " could not be found."
    }

    if( formName in scope){ // found scope[formName]
      addInputNameToForm(formName, ngModel, scope)
      return
    }
    findForm(formName, ngModel, scope.$parent) // recursively search through $parent scopes
  }
});

This should handle many situations where you just don't know where the form will be. Or perhaps you have nested forms, but for some reason you want to attach this input name to two forms up? Well, just pass in the form name you want to attach the input name to.

What I wanted, was a way to assign dynamic values to inputs that I will never know, and then just call $scope.myFormName.$valid.

This may be an overkill, and a better solution exists in 1.3+. I couldn't find it in the time I had. This works for me now.

Good luck! Hope this helps someone!!!!

Upvotes: 0

Jerrad
Jerrad

Reputation: 5290

Try this:

<input type="text" ng-attr-name="{{resource.Name}}" ng-model="resource.Value" ng-required="resource.IsRequired">

From the docs:

If an attribute with a binding is prefixed with the ngAttr prefix (denormalized as ng-attr-) then during the binding will be applied to the corresponding unprefixed attribute.

Although name="{{resource.Name}}" should work in this case as well.

Update

I found a solution here: Dynamic validation and name in a form with AngularJS

Here's a plunker that I made.

Sounds like this issue may be fixed in Angular 1.3.

Upvotes: 2

gkalpak
gkalpak

Reputation: 48211

Quoting another answer on a very similar issue:

This happens because the control's name (the one with which it is registered on its parent form) is retrieved during the ngModelController's instantiation, which according to the docs takes place before the pre-linking phase* (so no interpolation yet).


In other words, validation relies on forms.$error object, which is bound to the controls that are registered with that form.
A control's ngModelController is responsible for registering a control with its parent form and it is instantiated at the pre-linking phase (when the name-expression is not interpolated yet).

This can be solved by a custom directive that manually registers the control with its parent form, but only after it's actual name is determined (after insterpolating the name-expression).


You can find the whole answer here.
Look for the UPDATE 2 section for a working solution.

(This is the link to the working demo.)

Upvotes: 1

ashu
ashu

Reputation: 1039

<input ng-model="resource.model" name="{{resource.name}}" ng-required="{{resource.required}}"/>

seems to be working for me. Please see this plnkr .

Upvotes: 0

Josh Beam
Josh Beam

Reputation: 19772

Can you show the rest of your code? (HTML, relevant controllers, etc.)

Sometimes the issue can center around asynchrony. For example, the HTML might be compiled by Angular's compiler before your $scope.resource object gets its data (perhaps, with an AJAX call).

Two-way data binding sometimes doesn't always work how you would expect. Therefore, after "putting" your data into $scope.resource with whatever function, you might need to call $scope.$apply().

Upvotes: 0

Related Questions