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.

alalonde
  • 1,823
  • 1
  • 16
  • 27
  • 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](http://stackoverflow.com/a/42544806/1799146) – julian soro Mar 02 '17 at 00:39

0 Answers0