Custom Plugin Settings UI using Vue

Solved!
tiensng
Level 2
Custom Plugin Settings UI using Vue

Hi All,

I've read the DSS docs on creating a custom settings UI using an Angular-based UI https://doc.dataiku.com/dss/latest/plugins/reference/other.html#custom-settings-ui and was wondering if it's possible to use a different UI framework like Vue. I've read other posts on how to create Vue-based webapps in DSS but a key additional requirement for the plugin settings UI is accessing and modifying the config variable appropriately so user selections can be conveyed to the underlying plugin recipe's python runtime (i.e. in get_recipe_config()). The docs indicate how $scope.config is used to access this in Angular but I don't think this is possible using a different UI framework like Vue or React. If anyone has additional or different information on this topic, it would be greatly appreciated! Thanks in advance.

2 Solutions
Andrey
Dataiker Alumni

Hi @tiensng 

DSS provides a native support of AngularJS custom UIs, that's why users have direct access to config object. Moreover, any change to this object is also detected by DSS and the save button activates.

If needed, the custom UI could also be created using other frameworks, including Vue, however, you'd need to take care of synchronization between Vue's config object and DSS's one in AngularJS.

Here's an example of a small custom UI in Vue:

 

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script type="text/javascript">
    var mySimpleApp = new Vue({
        el: '#mainapp',
        data: { config: undefined },
        template: `
  <div id="app-wrapper">
        <h2 id="app-title">Tiny vue app</h2>
        Config: <code>{{config}}</code>
        <input v-model="config.parameter2" placeholder="parameter2 value">
  </div>
  `,
        mounted() {
            this.config = angular.element(this.$el).scope().config; // sync changes from DSS's AngularJS to Vue
        },

        watch: {
            config: {
                handler: function (val, oldVal) {
                    angular.element(this.$el).scope().$apply() // sync changes from Vue to DSS's AngularJS
                },
                deep: true
            }
        }
    })
</script>

<section id="mainapp"></section>

 

it's stored in resource/form.html

 

the recipe.json should include this line:

"paramsTemplate" : "form.html"

 

the rest of json config is typical, in my example I used a parameter called "parameter2", which can be declared as:

"params": [
        ...
        {
            "name": "parameter2",
            "type": "INT",
            "defaultValue": 42
        }
...
}

 

Hope this helps

 

Best Regards,

Andrey Avtomonov

R&D Engineer

Andrey Avtomonov
R&D Engineer @ Dataiku

View solution in original post

tiensng
Level 2
Author

Hi @Andrey ,

That's very helpful to know that we could use a different framework for the custom settings UI if needed. That being said, it does sound like using Angular will provide a potentially easier and less error-prone integration so we'll probably try that first. Thank you very much for the guidance!

- Tien

View solution in original post

4 Replies
Andrey
Dataiker Alumni

Hi @tiensng 

DSS provides a native support of AngularJS custom UIs, that's why users have direct access to config object. Moreover, any change to this object is also detected by DSS and the save button activates.

If needed, the custom UI could also be created using other frameworks, including Vue, however, you'd need to take care of synchronization between Vue's config object and DSS's one in AngularJS.

Here's an example of a small custom UI in Vue:

 

<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.js"></script>
<script type="text/javascript">
    var mySimpleApp = new Vue({
        el: '#mainapp',
        data: { config: undefined },
        template: `
  <div id="app-wrapper">
        <h2 id="app-title">Tiny vue app</h2>
        Config: <code>{{config}}</code>
        <input v-model="config.parameter2" placeholder="parameter2 value">
  </div>
  `,
        mounted() {
            this.config = angular.element(this.$el).scope().config; // sync changes from DSS's AngularJS to Vue
        },

        watch: {
            config: {
                handler: function (val, oldVal) {
                    angular.element(this.$el).scope().$apply() // sync changes from Vue to DSS's AngularJS
                },
                deep: true
            }
        }
    })
</script>

<section id="mainapp"></section>

 

it's stored in resource/form.html

 

the recipe.json should include this line:

"paramsTemplate" : "form.html"

 

the rest of json config is typical, in my example I used a parameter called "parameter2", which can be declared as:

"params": [
        ...
        {
            "name": "parameter2",
            "type": "INT",
            "defaultValue": 42
        }
...
}

 

Hope this helps

 

Best Regards,

Andrey Avtomonov

R&D Engineer

Andrey Avtomonov
R&D Engineer @ Dataiku
tiensng
Level 2
Author

Hi @Andrey ,

That's very helpful to know that we could use a different framework for the custom settings UI if needed. That being said, it does sound like using Angular will provide a potentially easier and less error-prone integration so we'll probably try that first. Thank you very much for the guidance!

- Tien

tiensng
Level 2
Author

Hi Andrey,

I've made some progress implementing a custom Recipe Plugin settings UI using a Vue 2.x web component initialized from a html page specified via the paramsTemplate attribute in recipe.json. I'm embedding the Vue web component JS code (compiled using vue-cli-service) inline in one of the <script> elements of the html fragment and then following your example, I'm initializing the Vue instance as follows:

var mySimpleApp = new Vue({
        el: '#cctapp',
        data: { config: undefined },
        template: '<cct-vue v-on:config-set="updateConfig"></cct-vue>',
        mounted() {
            this.config = angular.element(this.$el).scope().config; // sync changes from DSS's AngularJS to Vue
            console.log("Set Angular config in Vue")
        },
        methods: {
          updateConfig: function(event) {
            console.log("Updating config from " + event);
            for (var attrname in event) { this.config[attrname] = event[attrname]; }
            angular.element(this.$el).scope().$apply();
            console.log(this.config);
          }
        }
});
</script>
<section id="cctapp"></section>

This seems to work and I can use the Vue UI to dynamically configure the recipe and see the expected console output as the angular scope is updated with each change made in the custom UI. However when I click Run on the recipe, I see an exception thrown in the console with a "cyclic object value" error. I've included a screenshot of the console output to give you an idea of the exception being thrown and what is in the config object (the only property I'm setting is "CCT_PROTOCOL_ID") just before clicking Run:

Web console messagesWeb console messages

I also have another question on how to get the list of dataset columns selected as input by the user so I can display them in the custom UI but resolving this error is probably more critical at this stage. If you can offer any advice or suggestions on how to proceed further, I would be most appreciative. Thanks!

0 Kudos
Andrey
Dataiker Alumni

Hi @tiensng ,

 

Regarding the dataset schema retrieval, you can use the "callPythonDo" functionality described here:

https://doc.dataiku.com/dss/latest/plugins/reference/other.html

 

So my previous example could become something like this:

 

mounted() {
        this.scope = angular.element(this.$el).scope();
        this.config = this.scope.config;
        this.scope.callPythonDo({}).then(function(data) {
            // success
        }, function(data) {
            // failure
        });
    },

 

to implement the required function on python side you'd have to add a following key to your recipe.json:

"paramsPythonSetup": "pythonsetup.py"

 

pythonsetup.py (the name could be anything) should be created in the "resource" directory and should contain a python function called "do":

def do(payload, config, plugin_config, inputs):
    return {'example' : ["hello", "world"]}

 

you can do anything in this method, including importing dataiku module and retrieving the list of columns of a given dataset.

 

As for the cyclic object error, It seems that your config object cannot be serialized before being sent to DSS. Try calling

console.log(JSON.stringify(this.config))

 as the last step of your updateConfig method, if it throws the same exception, you'd need to make sure that your config object is serializable.

 

Regards

Andrey Avtomonov
R&D Engineer @ Dataiku