vue-form-generator icon indicating copy to clipboard operation
vue-form-generator copied to clipboard

concept issue: defining functions for field properties

Open cord opened this issue 5 years ago • 8 comments

Great stuff!

However there seems to be an issue with the concept of defining functions for fields properties and validators as standard JSON does not allow / support to generate JSON containing javascript functions like the following:

{
  type: "select",
  label: "Type",
  model: "type",
  values: [
    { id: "personal", name: "Personal" },
    { id: "business", name: "Business" }
   ]
},{
  type: "text",
  label: "Company name",
  model: "company.name",
  visible: function(model) {
    return model && model.type == "business";
  }
}

The workaround from #149 does not work in version 3.

Is there any know way to solve this??

cord avatar Mar 26 '19 12:03 cord

What I did was create a schema parser, which was able to map strings to methods.

For example “bind:someMethod”, would change the value from the string “bind:someMethod” and switch it over to a reference to that function. I’ve posted an example of this a couple of times in other issues, a quick search should turn them up... unfortunately I don’t have the time to find them at the moment.

zoul0813 avatar Mar 26 '19 13:03 zoul0813

I also handled this in a similar fashion. Although a bit more work but very flexible. I have a parser that finds any key with the _calc extension and converts the value to an evaluated function and added as a getter. This way it works with all keys anywhere in the schema.

eg

visible_calc = "model.sAllowed"
// or 
values_calc = "model.myArrayOfValues"
// or 
styleClasses_calc = "model.isOverdue ? 'warning' : 'information' "

My technique replaces the raw key with the getter function

DelfsEngineering avatar Mar 26 '19 16:03 DelfsEngineering

To clarify, I look for “bind:” prefix in the schema value and then lookup and attach to that, whether it be a property or a function.

zoul0813 avatar Mar 26 '19 16:03 zoul0813

ok, understand the concept but am struggling with the details (due to my lack of deeper knowledge...).

Tried to adopt the code I found in #601 but can't make it work.

I want to control the visibility of a field based on the selected value of another field.

I understand, the approach would be

{
"visible" : "bind:myToggleFunction" 
}

and than parse the bind:myToggleFunction using

`_.set(object, key, bindValue(param));``

Where / how would I define myToggleFunction to make it work?

Could you provide a working example (could be best practice as part of the documentation)?

For a future version a totally different approach to control form properties could be similar to https://github.com/ncform/ncform#dx-expression

cord avatar Mar 27 '19 07:03 cord

anyone?

cord avatar May 28 '19 13:05 cord

Sorry for delay, must have missed this.

VFG schemes are dynamic, if you update the schema VFG will update as well.

You can listen for VFG change events, watchers on the model, etc, and then update the schema visibility property.

My approach with “bind” allows for the schema to be defined remotely and use code I exposed to the component controlling VFG... you can define “myToggleFunction” anywhere the schema parser that wires up the actual binding has access to.

In my case, I did something similar to VFG validation class and attach a number of utility functions there... then my schema parser just looks for matches there.

zoul0813 avatar May 28 '19 13:05 zoul0813

ok, not really understanding what am doing the following works for me to toggle the visibility of a field depending on the value of another field w/o using the function syntax (which would force me to generate invalid json)

in app.js

var self = this;

axios
            .get(requestUrl)
            .then(function (response) {
                self.schema = response.data.data.schema;
                // https://github.com/vue-generators/vue-form-generator/issues/601

                // bind a field property to code
                let functionValue = (code, self) => {
                    return () => {
                        let fn = new Function('self', code)
                        return fn(self);
                    };
                };
                // bind a field property to `path` (can be a function)
                let bindValue = (path) => {
                    return () => {
                        return _.get(self, path);
                    };
                };
                // returns true if `path` exists, else false
                let hasValue = (path) => {
                    return () => {
                        return _.has(self, path);
                    };
                }

                let recurse = (obj) => {
                    _.forIn(obj, (value, key, object) => {
                        if (_.isString(value) && value.includes(':')) {
                            let parts = value.split(':', 2);
                            let op = parts[0];
                            let param = null;
                            if (parts.length > 1) {
                                param = parts[1];
                            }
                            switch (op) {
                                case 'function':
                                    _.set(object, key, functionValue(param, self));
                                    break;
                                case 'bind':
                                    _.set(object, key, bindValue(param));
                                    break;
                                case 'has':
                                    _.set(object, key, hasValue(param));
                                    break;
                                case 'ValidateJs':
                                    // ValidateJs is a custom validator object
                                    _.set(object, key, ValidateJs[param]);
                                    break;
                            }
                        }
                        // go deep
                        if (_.isArray(value) || _.isObject(value)) {
                            _.set(object, key, recurse(value));
                        }
                    });
                    return obj;
                };

                self.schema = recurse(self.schema);
            })

and in my schema generated through laravel:

 'visible' => 'function: return self.model.myValue == 3',

would be great to get your view on this!

thanks again

cord avatar May 29 '19 16:05 cord

I’m not a big fan of evaluating a function like that... what I did was created the functions in my code, and just passed Paramus to them from the Json schema.

A quick look ... seems your code should work though. I haven’t touched VFG in a few months though, so don’t have a project handy to test on at the moment (switched jobs back in March).

zoul0813 avatar May 29 '19 16:05 zoul0813