Angular Formal error when using fieldgroups

Hopefully this will be a more general Angular JS question and not something Angular Formly specific.

I followed the framework provided here for generating an Angular form error summary. Everything works well ..... but!

In this example, their model looks like this:

vm.fields = [
  {
    key: 'picky',
    type: 'customInput',
    templateOptions: {
      label: 'Picky field...',
      placeholder: 'This is required and has a maxlength of 5 and minlength of 3',
      required: true,
      maxlength: 5,
      minlength: 3
    }
  },
  .....
  {
    key: 'ip',
    type: 'customInput',
    validators: {
      ipAddress: {
        expression: function(viewValue, modelValue) {
          var value = modelValue || viewValue;
          return /(\d{1,3}\.){3}\d{1,3}/.test(value);
        },
        message: '$viewValue + " is not a valid IP Address"'
      }
    },
    templateOptions: {
      label: 'IP Address',
      required: true,
      type: 'text',
      placeholder: '127.0.0.1',
    }
  }
];

      

Then, if we look at the HTML, we can see that these fields are passed into the error summary as such:

<formly-error-summary form="vm.form" fields="vm.fields"></formly-error-summary>

      

For a simple form structure, this works great, but if you want to use the Bootstrap layout as described here , then your model ends up looking like something mine:

vm.rentalFields = [
{
    template: '<div class="row"><div class="col-xs-12"><h3>About You</h3></div></div>'  
},
{
    className: 'row',
    fieldGroup: [
    {
        className: 'col-xs-6',
        type: 'customInput',
        key: 'first_name',
        templateOptions: {
            type: 'text',
            label: 'First Name',
            placeholder: 'Enter your first name',
            required: true
        }
    },
    {
        className: 'col-xs-6',
        type: 'customInput',
        key: 'last_name',
        templateOptions: {
            type: 'text',
            label: 'Last Name',
            placeholder: 'Enter your last name',
            required: true
        },
        expressionProperties: {
            'templateOptions.disabled': '!model.first_name'
        }   
    }
    ]
},
{
    template: '<div class="row"><div class="col-xs-12"><h3>License and Insurance Details</h3></div></div>',
    hideExpression: '!model.email'
}
.....

      

Now when we pass vm.rentalFields to the error summary, instead of accessing the fields, we just check each object instead. I can get around this by doing something like:

<formly-error-summary form="vm.rentalForm" fields="vm.rentalFields[1].fieldGroup"></formly-error-summary>

      

This is of course not ideal, as there will be fields in other field groups that I want to test to prove the problem, although this is fine now. I've tried just going to vm.rentalFields.fieldGroup, but as I suspected it returns nothing.

So, is there a way that I can recursively pass across all fieldgroups in the vm.rentalField object or is this something I should be handling in the code of the Directive itself.

angular.module("formlyApp").directive('formlyErrorSummary', function() {
    return {
      scope: {},
      bindToController: {
        form: '=',
        fields: '='
      },
      templateUrl: 'js/Directives/formly-error-summary.html',
      controllerAs: 'vm',
      controller: function() {
        var vm = this;

        vm.getErrorAsList = getErrorAsList;
        console.log(vm.fields);
        function getErrorAsList(field) {
          return Object.keys(field.formControl.$error).map(function(error) {
            // note, this only works because the customInput type we have     defined.
            return field.data.getValidationMessage(error);
          }).join(', ');
        }
      }
};
});

      


EDIT

So, after I got the advice from Ken below, I was able to change my formlyErrorSummary directive so that now it can at least get errors for the model. This has numerous problems in it with $ scope. $ Watch does a deep level comparison and even on the first page load, it all gets fired 3 times! I added in some rudimentary escape sequences to try and fight this and for now at least I have errors, the next problem I have is in the HTML where I am callingng-repeat="field in vm.fields"

which is actually the same problem, so how could I work around this? Part of me is thinking about an anonymous object that will contain a message with fields and is it true and then parse it inside HTML, but I'm not sure if this way of thinking applies to Angular?

controller: function($scope) {

var vm = this;

$scope.$watch('vm.fields', function(){
    for(var i = 0; i < vm.fields.length; i++)
        if(vm.fields[i].fieldGroup) {
            for(var j = 0; j < vm.fields[i].fieldGroup.length; j ++)
                if(vm.fields[i].fieldGroup[j].formControl) {
                    var err = getErrorAsList(vm.fields[i].fieldGroup[j]); 
                    if(err)
                        vm.getErrorAsList = err;
                }
        }
}, true);

      


SOLUTION - POSSIBLE

After a lot of hacking, I think finally this works so that the error messages are now displayed both in the row and in the summary at the top.

My final directive function now creates an array on every run that will contain all error messages, it needs to be cleared within $watch

otherwise when the field is valid, the error message will be stored inside the array so we just rebuild the whole thing every time .... Considering I'm already using deep level here, I hope any performance hits will be minor.

vm.errs = [];

$scope.$watch('vm.fields', function(){
    vm.errs = [];
    for(var i = 0; i < vm.fields.length; i++)
        if(vm.fields[i].fieldGroup) {
            for(var j = 0; j < vm.fields[i].fieldGroup.length; j ++)
                if(vm.fields[i].fieldGroup[j].formControl) {
                    var err = getErrorAsList(vm.fields[i].fieldGroup[j]); 
                    if(err)
                        if(vm.errs.indexOf(err) === -1)
                          vm.errs.push(err);
                }
        }
}, true);  

      

Then, in the directive template, I had to remove the link vm.fields

, since obviously it won't work in this approach. Since I knew that this summary would only be displayed if the form was submitted, I could have removed it from other validations that were done and ended up with the following HTML:

<div class="row">
    <div class="col-xs-12">
        <div class="formly-error-summary bg-danger" ng-if="vm.form.$invalid">
            <div ng-repeat="err in vm.errs" class="color-error">
                <i class="glyphicon glyphicon-remove"></i>
                <span>
                    {{err}}
                </span>
            </div>
        </div>
    </div>
</div>

      

I'm still not 100% happy with it, it gets the job done, but I'm not sure if this is the "Angular" way and the fact that I'm using $scope.$watch

the field object is a little annoying for my OCD developer, but the solution is still.

If anyone has any clarifications or suggestions for improving this, let me know please, still get around Angular, but it was quite a fun learning!

+3


source to share





All Articles