2

I've created a dynamic templateUrl for form fields and I'm trying to attach ng-model within ng-repeat. The parent directives and form field directive all work and are generated, but when I use ng-model it doesn't seem to be working, the pre-output never changes? Is there a trick to apply ng-model in this use case? It works if I just use hard-code the form inputs. I've been following the example in the AngularJS docs.

Surrounding markup around form fields:

<form role="form" ng-controller="FormController as formCtrl" novalidate>

    <div ng-repeat="field in panel.form_fields">

        <form-field field="field"></form-field>

    </div>

    <fieldset class="form-group clearfix">
        <button type="submit" class="btn btn-primary pull-right">Save Progress</button>
    </fieldset>

    <pre>form   = {{models | json}}</pre>
    <pre>master = {{master | json}}</pre>
</form>

Form field directive:

angular.module('formField.directives', [])

.directive('formField', [ '$http', '$compile', function( $http, $compile ) {

    var getTemplateUrl = function( field ) {

        var type = field.field_type;
        var templateUrl = '';

        switch( type ) {
            case 'textfield':
                templateUrl = 'components/form-field/fields/textfield.html';
                break;
            case 'email':
                templateUrl = 'components/form-field/fields/email.html';
                break;
            case 'currency':
                templateUrl = 'components/form-field/fields/currency.html';
                break;
            case 'date':
                templateUrl = 'components/form-field/fields/date.html';
                break;
            case 'dropdown':
                templateUrl = 'components/form-field/fields/dropdown.html';
                break;
            case 'textarea':
                templateUrl = 'components/form-field/fields/textarea.html';
                break;
            case 'hidden':
                templateUrl = 'components/form-field/fields/hidden.html';
                break;
            case 'password':
                templateUrl = 'components/form-field/fields/password.html';
                break;
            case 'checkbox':
                templateUrl = 'components/form-field/fields/checkbox.html';
                break;
            case 'radio':
                templateUrl = 'components/form-field/fields/radio.html';
                break;
        }

        return templateUrl;
    }

    var linker = function( scope, element ) {

        var templateUrl = getTemplateUrl( scope.field );
        $http.get( templateUrl ).success(function( data ) {
            element.html(data);
            $compile(element.contents())(scope);
        });
    }

    return {
        restrict: 'E',
        replace: true,
        scope: {
            field: '='
        },
        link: linker
    }
}]);

Form field template used:

<fieldset class="form-group">

    <label for="{{field.field_name}}">{{field.field_label}}</label>
    <input type="text" 
           class="form-control"
           id="{{field.field_id}}"
           name="{{field.field_name}}"
           value="{{field.field_value}}"
           placeholder="{{field.field_prompt}}"
           ng-required="field.field_required"
           ng-disabled="field.field_disabled"
           ng-model="models[field.field_name]"> // model.test also doesn't work, and need to be able to reference the model dynamically

</fieldset>

Controller used from example in AngularJS docs:

.controller('FormController', ['$scope', function( $scope ) {

    $scope.master = {};
    $scope.models = {};

    $scope.update = function( models ) {
        console.info('Update');
        $scope.master = angular.copy( models );
    };

    $scope.submit = function() {
        console.info('Form Submitted');
    };

    $scope.cancel = function() {
        console.info('Form Cancelled');
    };

    $scope.clear = function() {
        console.info('Form Clear');
        $scope.models = {};            
    }

    $scope.reset = function() {
        console.info('Form Reset');
        $scope.models = angular.copy( $scope.master );
    };

    $scope.reset();

}]);
3
  • Can you prepare a demo? how your directive look like Commented Sep 3, 2014 at 1:10
  • Could you please add the declaration of the "form-field" directive? Commented Sep 3, 2014 at 1:12
  • 3
    You are using isolated scope, it does not know what is models outside the context. See it now plnkr.co/edit/HOxGGw?p=preview Commented Sep 3, 2014 at 1:21

1 Answer 1

3

Your directive works on isolated scope (.$new(true)) which means the changes that you make inside your directive will not be available in the outside scope directly (unless you use 2-way bindings etc..). So ng-model="models[field.field_name]" the models inside your directive is not the models object on your scope in the parent controller. So you can fix it by passing the model as well using 2-way binding.

While consuming:-

 <form-field field="field" model="models[field.field_name]"></form-field>

In your directive template:-

   <input type="text" 
       class="form-control"
       id="{{field.field_id}}"
       name="{{field.field_name}}"
       value="{{field.field_value}}"
       placeholder="{{field.field_prompt}}"
       ng-required="field.field_required"
       ng-disabled="field.field_disabled"
       ng-model="model">  <!-- Here just set the model on the scope 2-way B -->

and in your directive isolated scope declaration do:-

  scope:{field:'=', model:'='},
  //or set a reference to the object on scope holding models in the field property itself.

Plnkr

Note that in your directive template when you specify value="{{field.field_value}}" and ng-model it is not going to set the default value at all. You would need to default it in the ngModel itself.

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

4 Comments

Great answer! @mtpultz you may want to have a look at this: docs.angularjs.org/guide/scope . Also, the semantics that you are using for the 'fieldset' tag are a bit akward. The 'fieldset' tag should be used for grouping fields, so rather than creating a 'fieldset' tag for each field, maybe you should use it for holding them
@Josep That's a great point. I didn't notice a fieldset there in the template myself.. :)
Thanks @PSL, that was a great explanation, and thanks also for pointing out the issue regarding the default value. Cheers :)
Hi @Josep, I'll definitely change the fieldset to hold the fields as a group, thanks for pointing that out.

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.