4

In AngularJS, is there any way to declare ng-model directly on the <form> element instead of having to do it on every single control/input of that form and then be able to access the values of the controls in the controller through their names?

Specifically, if you have a form like this,

<form>
  <input type="text" name="email">
  <input type="text" name="age">
</form>

typically, you'd do something like this,

<form>
  <input type="text" ng-model="user.email">
  <input type="text" ng-model="user.age">
<form>

which then gives you access to the user object and its properties in the controller:

$scope.user
$scope.user.email
$scope.user.age

I would like to do something like this instead:

<form ng-model="user">
  <input type="text" name="email">
  <input type="text" name="age">
</form>

and then be able to access the values in the controller:

$scope.user.email
$scope.user.age

The reason I'm asking is that I'm angularizing an existing web project and some of the forms have easily 20 or 30 controls and defining the ng-model individually seems like an overkill.

All the form examples that I'm able to find declare the ng-model on the individual controls. I was also able to dig up this ticket which basically says that something like this would require a major AngularJS overhaul, so I suspect it may not be possible. But the ticket is from a year ago and perhaps things have changed since then.

0

2 Answers 2

10

Out of the box; no. But you can quite easily write your own directive that does the trick.

app.directive('lazyFormModel', function() {
  return {
    require: ['form'],

    compile: function compile(tElement, tAttrs) {

      var modelName = tAttrs.lazyFormModel;

      angular.forEach(tElement.find('input'), function(e) {
        var $e = angular.element(e);
        var name = $e.attr('name');
        $e.attr('ng-model', modelName + '.' + name);
      });
    }
  };
});

The above will create a directive that will loop through all the containing input elements and stamp an ng-model attribute on them based on the name value. Attach this to a form element and you're good to go:

<form lazy-form-model="user">
  <input type="text" name="email">
  <input type="text" name="age">
</form>

See it in action: http://plnkr.co/edit/O7ais3?p=preview

Edit

As a side note; you can extend this to fit other manipulations too: like custom validations and/or html layout.

Edit

Would you know how I could extend this to work with multiple checkboxes of the same name but different values (a checklist type of input)?

Something like this should handle checkboxes:

angular.forEach(tElement.find('input'), function(e) {
  var $e = angular.element(e);
  var name = $e.attr('name');

  var modelProperty = modelName + '.' + name;

  if($e.attr('type') == 'checkbox') {
    modelProperty += '.' + $e.attr('value');
  }

  $e.attr('ng-model', modelProperty);
});

Updated the plunker.

Sign up to request clarification or add additional context in comments.

4 Comments

This is great, thank you! I'm choosing this as the answer as the angular-form-model from jackypan1989's answer works very similarly. Would you know how I could extend this to work with multiple checkboxes of the same name but different values (a checklist type of input)?
Updated the answer to handle checkboxes
is there a way to do this with dynamic inputs via ng-repeats?
@ryeballar It is possible, but you'll need something that will fire after the ng-repeat has fired, which AFAIK requires some setup. Take a look at this if you want dynamic forms without any hassle.
2

There is a custom directive called angular-form-model which overcomes this problem. It makes every model inside the form automatically bind to the ng-model of form.

For example:

<form jfb-form-model="user">
    <input name="email" />
    <input name="age" />
</form>

will transfer to

<form jfb-form-model="user">
    <input name="email" ng-model="user.email"/>
    <input name="age" ng-model="user.age/>
</form>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.