2

I'm writing a custom field directive which dynamically creates an <input> field (or <select> or <textarea> etc.) based on a 'custom field' object. I only want the directive to contain the form field, not any validation or label markup. This has worked fine up until validation.

It looks like the input field isn't added to the parent scope's form when $compiled. Is there a way to add it manually? I tried FormController.$addControl() (doc), which caused the form to start listening to changes on the input model, but the form states (dirty, valid, etc.) still weren't being updated.

Markup:

<div ng-controller="FieldController">
  <form name="customFieldForm">
    <label class="control-label" for="{{ field.name }}">{{ field.name }}:</label>
    <input-custom-field model="field"></input-custom-field>
    <span class="input-error" ng-show="customFieldForm.field.$error.required">
        Required</span>
  </form>
</div>

Controller:

myApp.controller("FieldController", function ($scope) {
  $scope.field =  {
    name: "Pressure in",
    required: true,
    readOnly: false,
    type: "decimal",
    value: null
  };
})

Directive (abridged):

.directive('inputCustomField', ['$compile', function ($compile) {
  var buildInput = function (field, ignoreRequired) {
    var html = '';
    var bindHtml = 'name="field" ng-model="field.value"';
    if (!ignoreRequired && field.required) {
        bindHtml += ' required';
    }
    switch (field.type) {
        case "integer":
            html += '<input type="number" ' + bindHtml + ' ng-pattern="/^\\d*$/">';
            break;
        case "decimal":
            html += '<input type="number" ' + bindHtml + ' class="no-spinner">';
            break;
    }
    return html;
  };

  return {
    restrict: 'E',
    require: '^form',
    scope: {
        field: "=model"
    },
    link: function (scope, elm, attrs, formController) {
        var fieldModel;
        var replacedEl;
        var renderInput = function (html) {
            replacedEl = $compile(html)(scope);
            elm.replaceWith(replacedEl);
            fieldModel = replacedEl.controller('ngModel');
            if (!fieldModel) fieldModel = replacedEl.find("input").controller('ngModel');
        };

        if (scope.field && scope.field.type) {
            var html = buildInput(scope.field, attrs.hasOwnProperty("ignoreRequired"));
            renderInput(html);
        }
    }
  };
}])

Also see the fiddle.

1
  • The issue is you're running $compile before the directive is in the DOM, thus Angular doesn't know it's nested under the form. See my answer to this same problem here: AngularJS: Dynamic inputs with form validation Commented Mar 2, 2017 at 0:39

0

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.