Daniel
Daniel

Reputation: 1420

Using a variable (from service) with ng-model

I am having a problem with angular that I just cannot seem to get around.

I built a CMS that enabled product owners to design a dynamic registration form that is then output as JSON through an api.

I am using angular on the app side to take this JSON and build the dynamic form. I would like to use ng-model and ng-show to show/hide certain elements when checkboxes are checked.

For instance, I have a checkbox that says "This is also my mailing address" and when unchecked, an additional address form will show so that the user can input their shipping information.

In the JSON response, for each field I define the "ruleKey" which should act as the ng-model for that field (eg: isSameAddress). Then on the shipping address form I setup ng-show="!isSameAddress".

When I hardcode this "ruleKey" into the ng-model and ng-show, everything works as expect... however I need this to be dynamic and come from the api values that are returned. This is where I have the problem and I have no idea how to fix it.

Here is a basic fiddle I setup to demonstrate my issue: http://jsfiddle.net/tdanielcox/st6Lw90x/

And here are the directives in question:

myApp.directive('textField', function ($compile) {
    return {
        restrict: 'E',
        scope: {
            toggler: '='
        },
        template: '<input type="text" value="Test text" />',
        transclude: false,
        link: function (scope, element, attr) {
            element[0].getElementsByTagName('input')[0].setAttribute('ng-show', scope.toggler);
            $compile(element.contents())(scope);
        }
    };
});

myApp.directive('checkboxField', function ($compile) {
    return {
        restrict: 'E',
        scope: {
            toggler: '='
        },
        template: '<input type="checkbox" />',
        transclude: false,
        link: function (scope, element, attr) {
            element[0].getElementsByTagName('input')[0].setAttribute('ng-model', scope.toggler);
            $compile(element.contents())(scope);
        }
    };
});

If anyone has any idea how I can achieve this, I would really appreciate it. I have googled and googled but have not found anything that will help me.

Thanks for reading :)

EDIT:

Here is a sample of the JSON that the API is returning, notice the ruleKey and the rules in the two fielset objects. The ruleKey should be used as for the ng-model and the rules are what the ng-model should control.

**Please note that the example I provided above does not fit with this service response. The example is to illustrate a simple version of what I am trying to achieve. In reality I already have my controller and directives setup to loop through the service response and build the form correctly.

{
    ... //  OTHER REGISTRATION FIELDS
},
{  //  FIELDSET 1
    rules: {},
    fields: [
        {
            type: 'checkbox',
            name: 'sameMailingAddr',
            label: 'This is also my mailing address',
            value: true,
            checked: false,
            ruleKey: 'isSameAddress'  //  BINDS TO NG-MODEL
        },
    ],
},
{  //  FIELDSET 2's visible is controlled by the checkbox in FIELDSET 1
    rules: {
        show: '!isSameAddress'  //  BINDS TO NG-SHOW
    },
    fields: [
        {
            type: 'text',
            name: 'address',
            label: 'Street address', 
            labelLocation: 'placeholder',
        },
        {
            type: 'text',
            name: 'address2',
            label: 'Street address 2', 
            labelLocation: 'placeholder',
        },
        {...}
    ]
},

Upvotes: 0

Views: 1189

Answers (1)

hon2a
hon2a

Reputation: 7214

Either you're really constructing the form in a completely dynamic way based on your JSON. Then you can hard-code the property name when constructing the template and compile that.

Or you have a set structure like "it's always going to be one checkbox and one text input", but the properties may have different names. (This is hard to imagine, since your data would also have to contain the meta-data telling you which property belongs to the checkbox and so forth, effectively allowing to adress a set-able property from the template.) In that case, you can set up a two-way binding (using $watch(expression, handler, true)) between your data object and another object with well-known properties, which you are again able to address from the template.


To illustrate the second case: If you have all the meta-data ready, you can always do something like:

<div ng-repeat="field in fields">
    <label>{{field.label}}</label>
    <span ng-switch="field.type">
        <input ng-switch-default type="text" ng-model="data[field.name]">
        <!-- ... (ng-switch-when cases for other field types) -->
    </span>
</div>

Upvotes: 1

Related Questions