formly-js / angular-formly

JavaScript powered forms for AngularJS

Home Page:http://docs.angular-formly.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Backwards compatibility for templateOptions?

gboersma opened this issue · comments

I am finding formly very helpful. Thanks.

In the latest 3.0.0 checkin, the property templateOptions was introduced. Unfortunately, this breaks my code.
Any way you can introduce a backwards-compatible option, perhaps a helper that will create the templateOptions from the field object?
Just curious, what was the reason for the change?

Keep up the great work!

@gboersma, I'm glad to see you keeping up with what I'm doing with the latest stuff. It helps me get feedback when people try to use stuff that I'm building :-)

The reason I've decided to create templateOptions (which is such a big breaking change) is because it became confusing to determine what (in the field config object) is used in formly core (ie common regardless of template) and what is used in the template only. By restricting what properties you can put in the field config, it makes formly's api more consistent and easier to use. Also, formly is now able to warn you if you use it incorrectly (currently, it throws an error, but I plan on changing that to a simple warning).

Please note! I have a huge application with a LOT of field configurations. I'm not looking forward to updating all of these. So, to ease things a little bit, I just barely created this tool which I hope should be helpful when updating your form fields :-) cheers! http://jsbin.com/ruwoke

Oh, sorry, and to answer your actual question, unfortunately I don't think that I'm going to be able to add a backwards compatible option. Hopefully the little tool I created will suffice. I believe it should be everything you need to update your fields to the new standard. If you're using custom templates, that's a bit of a different story. You'll need to update those manually. Good luck!

I like the fact that formly can now catch errors in setting the options. However, for my app, I generate the options dynamically from the server (Java), which maps the JSON objects to / from Java classes. So I either have to rejig the Java implementation, or write some Javascript to munge the JSON into the correct format. Either one is not very pretty. Since the JSON is generated automatically from Java classes, I am not really that concerned about template options getting mixed up with formly options, since I control the naming on both sides (JSON and field templates).

Would it be possible to provide an option to leave it as is? One way to implement it is, if the option is set, to have formly create the templateOptions object automatically by moving properties that are not options from the options map into the templateOptions map. It can be done right before calling each of the form fields, so the core formly call should not have to change. I could take a stab at this, but my JavaScript Fu is probably not up to it at this point.

I will not include this logic in formly core, but it should be very reasonable to do yourself before you pass the options to formly. Depending on how things are set up for you, it may be easiest to use an http interceptor and do the conversion there. Obviously, this wouldn't allow you to keep your templates as they are. But it is likely that the template update bit is just a half hour change max.

As for the actual conversion function, because you're just dealing with json (rather than having to worry about functions) then something like this should probably do it:

// test fields using the old method
var oldFields = [
  {
    label: 'Field 1',
    required: true,
    key: 'field1',
    type: 'text',
    expressionProperties: {
      data: {
        dataProp1: '$viewValue.length > 4'
      },
      hide: 'model.field2',
      disabled: '!model.field3'
    }
  },
  {
    label: 'Field 2',
    key: 'field2',
    type: 'checkbox'
  },
  {
    label: 'Field 3',
    key: 'field3',
    type: 'checkbox'
  }
];

console.log('before', oldFields);
var newFields = convert(oldFields);
console.log('after', newFields);

// here's the actual convert function
function convert(fields) {
  var copy = fields;
  var allowedProperties = [
    'type', 'template', 'templateUrl', 'key', 'model',
    'expressionProperties', 'data', 'templateOptions',
    'wrapper', 'modelOptions', 'watcher', 'validators',
    'noFormControl', 'hide'
  ];
  angular.forEach(copy, function(field) {
    angular.forEach(field, function(val, prop) {
      if (prop === 'expressionProperties') {
        for (var eProp in val) {
          if (allowedProperties.indexOf(eProp) === -1) {
            val['templateOptions.' + eProp] = val[eProp];
            delete val[eProp];
          }
        }
        if (val.data) {
          for (var dProp in val.data) {
            val['data.' + dProp] = val.data[dProp];
          }
          delete val.data;
        }
      }
      if (allowedProperties.indexOf(prop) !== -1) {
        return;
      }
      field.templateOptions = field.templateOptions || {};
      field.templateOptions[prop] = val;
      delete field[prop];
    });
  });
  return copy;
}

@gboersma, do you think this will be sufficient? Do we need to keep this issue open?

I can live with this. Another option that I may be able to go with is to write a custom Jackson serializer / deserializer on the Java server-side, which will convert the JSON structure to the Java object structure and vice-versa. This will make things more robust for any future naming / structure changes on the JSON.

Thanks for being flexible. I'm trying to make this project the very best that it can be and that required a few breaking changes. Let me know if there's anything I can do to help.