vue-generators / vue-form-generator

:clipboard: A schema-based form generator component for Vue.js
MIT License
2.99k stars 532 forks source link

Adding Vue Directives to Inputs #601

Closed illcrx closed 5 years ago

illcrx commented 5 years ago

Note: for support questions, please use stackoverflow. This repository's issues are reserved for feature requests and bug reports.

I need to add a v-on:change to each input and am unable to find a way to add vue directives to the inputs. I have combed the docs and asked on SO, asked on Vue Discord to no avail. So I'm not sure if this is possible currently. It should be an option.

zoul0813 commented 5 years ago

Have you looked into the “model-updated” that is fired? It should cover any scenario that Onchange would be used for

illcrx commented 5 years ago

No, I have not, is that in the documentation? I ended up just using a Watcher on my model component to accomplish what I wanted, but I can see scenarios where being able to call directives through the schema would be valuable. It's Vue after all so abstracting some core features of Vue away limits what could be done. If there are other ways to accomplish similar things it would still be more simple and readable to be able to call v-if or v-on directly. For instance I have been using the visible option, but I had to search far and wide for it because it's not in the docs that Icebob started and has apparently stopped maintaining. FYI that is the GitBook.

zoul0813 commented 5 years ago

Are you referring to the latest docs referenced in the README? These docs are available as a public repo, and PR requests with updates are welcome.

As for the directives, it's somewhat impossible to add these as the VFG field components are generated dynamically using a schema object, so there is no Vue component to attach the directives to. It's also important to note that VFG components can have fairly complex layouts and markup, especially when building your own or using third-party components for things such as tables, dropdowns, calendars, etc.

For the "onchange" you mentioned, the docs for this can be found here. This should have everything you need to handle "input changed" events.

v-if can be done by attaching functions to the various things you'd like to do, for example, visible or disabled (documented here). The visible property of the field schema can be a function, which can then return true or false, essentially mimicking the functionality of a v-if statement. If the field is not visible, it will not be added to the layout (this is NOT a CSS visibility toggle).

A simple "schema builder" can be implemented, which can take a standard JSON object from a remote source and then replace known values with reusable functions, if needed. These usually tend to be project specific, and VFG does not provide anything "out of the box" for this.

Here's an example of a method I wrote for one of my apps, which retrieves a static JSON schema for a form and then has the ability to convert bind:XXXXX, has:XXXXX or ValidateJs:XXXXX values within the schema into something else, allowing me to bind the disable, visible, validator and other fields to methods or functions while storing the schema as a valid JSON file. Additional prefixes that perform other functionalities can easily be added to the switch statement.

// uses lodash for `_`
getSchema(SCHEMA_URI) {
    // use vue-resource to retrieve the JSON schema
    return this.$http.get(SCHEMA_URI).then((response) => {
        response.json().then((schema) => {
            // bind a field property to `path` (can be a function)
            let bindValue = (path) => {
                return () => {
                    return _.get(this, path);
                };
            };
            // returns true if `path` exists, else false
            let hasValue = (path) => {
                return () => {
                    return _.has(this, 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 '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;
            };

            this.schema = recurse(schema);

            // override VFG default validator messages with custom messages by key (ref: src/utils/validators.js)
            _.forEach(_.get(this.schema, 'messages', []), (message, key) => {
                VueFormGenerator.validators.resources[key] = message;
            });
        });

    }, (err) => {
        console.error('getSchema', err);
    });
},