Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS - declare ng-model directly on the <form> element

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.

like image 673
h4r1m4u Avatar asked Dec 15 '22 22:12

h4r1m4u


2 Answers

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.

like image 86
null Avatar answered Jan 04 '23 21:01

null


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>
like image 34
jackypan1989 Avatar answered Jan 04 '23 21:01

jackypan1989