Open jgunstone opened 3 years ago
That looks like a great library!
I tried to load it from CDN, and it works!
in try_vjsf.vue
:
<template>
<div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@koumoul/vjsf@latest/dist/main.css">
<div v-if="vjsf_loaded">
<v-form v-model="valid">
<v-jsf v-model="form_data" :schema="schema"></v-jsf>
</v-form>
</div>
</div>
</template>
<script>
module.exports = {
async created() {
const [VJsf] = await this.import(['https://cdn.jsdelivr.net/npm/@koumoul/vjsf@latest/dist/main.js']);
this.$options.components['v-jsf'] = VJsf.default;
this.vjsf_loaded = true;
},
methods: {
import(deps) {
return this.loadRequire()
.then(() => new Promise((resolve, reject) => {
requirejs(deps, (...modules) => resolve(modules));
}));
},
loadRequire() {
/* Needed in lab */
if (window.requirejs) {
console.log('require found');
return Promise.resolve()
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
}
}
</script>
In a notebook:
# uncomment next lines to enable hot reloading of vue template(s). (needs the watchdog package)
# import ipyvue
# ipyvue.watch('.')
import ipyvuetify as v
import traitlets
class TryVjsf(v.VuetifyTemplate):
template_file = "try_vjsf.vue"
vjsf_loaded = traitlets.Bool(False).tag(sync=True)
form_data = traitlets.Dict(default_value={}).tag(sync=True)
schema = traitlets.Dict().tag(sync=True)
valid = traitlets.Bool(False).tag(sync=True)
schema = {
"type": "object",
"properties": {
"stringProp": { "type": "string" },
"colorProp": { "type": "string", "x-display": "color-picker" },
}
}
my_form = TryVjsf(schema=schema)
my_form
hi there,
sorry for the delay responding -
thanks so much for putting together the boiler-plate code above, it works great!
I was interested to see if it works with the schema's that are generated from pydantic - and it does!
from typing import List
from pydantic import BaseModel, Field
class PetCls:
def __init__(self, *, name: str, species: str):
self.name = name
self.species = species
class PersonCls:
def __init__(self, *, name: str, age: float = None, pets: List[PetCls]):
self.name = name
self.age = age
self.pets = pets
class Pet(BaseModel):
name: str
species: str
class Config:
orm_mode = True
class Person(BaseModel):
name: str
age: float = Field
pets: List[Pet]
class Config:
orm_mode = True
bones = PetCls(name='Bones', species='dog')
orion = PetCls(name='Orion', species='cat')
anna = PersonCls(name='Anna', age=20, pets=[bones, orion])
p_form = TryVjsf(schema=Person.schema())
p_form
I think that this creates a really nice workflow for rapidly creating UI's in python / jupyter:
p_form.form_data
.thanks again!
Hi there, I've started to use the functionality described above and its working well.
vjsf implements a markdown editor which would be great to include.
https://koumoul-dev.github.io/vuetify-jsonschema-form/latest/examples#markdown-editor
where they describe as follows:
You can edit markdown content using the x-display=markdown annotation.
To do this VJsf integrates EasyMDE. But to prevent creating a larger distributable it is not declared as a dependency, you need to load it yourself and make it available in global.EasyMDE (or window.EasyMDE). For example:
import EasyMDE from 'easymde/dist/easymde.min.js'
import 'easymde/dist/easymde.min.css'
global.EasyMDE = EasyMDE
I tried installing EasyMDE into the conda environment i'm using and then adding the snippet above to the try_vjsf.vue boilerplate code you put together - but that didn't work. Is there a way to simply integrate this functionality?
try_vjsf.vue
<template>
<div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@koumoul/vjsf@latest/dist/main.css">
<div v-if="vjsf_loaded">
<v-form v-model="valid">
<v-jsf v-model="form_data" :schema="schema"></v-jsf>
</v-form>
</div>
</div>
</template>
<!-- // <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.css">
// <script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script> -->
<script>
import EasyMDE from 'easymde/dist/easymde.min.js'
import 'easymde/dist/easymde.min.css'
global.EasyMDE = EasyMDE
module.exports = {
async created() {
const [VJsf] = await this.import(['https://cdn.jsdelivr.net/npm/@koumoul/vjsf@latest/dist/main.js']);
this.$options.components['v-jsf'] = VJsf.default;
this.vjsf_loaded = true;
},
methods: {
import(deps) {
return this.loadRequire()
.then(() => new Promise((resolve, reject) => {
requirejs(deps, (...modules) => resolve(modules));
}));
},
loadRequire() {
/* Needed in lab */
if (window.requirejs) {
console.log('require found');
return Promise.resolve()
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
}
}
</script>
many thanks,
I was interested to see if it works with the schema's that are generated from pydantic - and it does!
yeah, it's really cool :)
I tried installing EasyMDE into the conda environmen
Did you try getting if from npm/jsdelivr like vjsf in thie example, using the import?
hi - apologies - missed your response.
i think this is where my lack of understanding of javascript kicks in -
do you mean importing EasyMDE in a similar way to how it is done here:
async created() {
const [VJsf] = await this.import(['https://cdn.jsdelivr.net/npm/@koumoul/vjsf@latest/dist/main.js']);
this.$options.components['v-jsf'] = VJsf.default;
this.vjsf_loaded = true;
},
?
cheers
Stumbled on this issue while trying to get another library wrapped (AGGrid-Vue), really fantastic capability!
For @jgunstone 's question - I think you will need something in the pattern of what's shown for VJsf... I dug through the ipyvuetify and ipyvue code a bit, I think any code you put before the first { gets stripped, so all of your "import EasyMDE" isn't making it through to the final component: https://github.com/widgetti/ipyvue/blob/23e3a0fdfa6c20b8a9efbc8cef13e3a17f6b00fc/js/src/VueTemplateRenderer.js#L316-L324
That should successfully load your library (e.g. if you're watching page sources, you should see it get added), but it may/may not satisfy the dependencies... would love @maartenbreddels or @mariobuikhuizen 's input here as I'm pretty new to javascript, just feeling my way. Depending on the exact artifact you're importing from npm, they may or may not be usable it seems with the client-side dynamic import (is that true?). The "umd"/"amd" formats seem to work well, but I'm finding that plain javascript seems really to need to be wrapped. Will need to use a tool like https://browserify.org to make it importable.
Here's the code I have so far (adapted from example: https://www.ag-grid.com/vue-data-grid/vue2/)... aggrid-vue had been giving errors about no aggrid dependency, that's satisfied now, but giving error about Vue being undefined... I can import the Vue common js library, but they don't offer amd/umd, and I'd think that ipyvuetify/ipyvue must already have vue imported, not sure how to expose that to aggrid-vue...
try_aggrid.vue
<template>
<div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@28.1.1/styles/ag-grid.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@28.1.1/styles/ag-theme-alpine.css">
<div v-if="aggrid_vue_loaded">
<ag-grid-vue
style="width: 500px; height: 200px"
class="ag-theme-alpine"
:columnDefs="columnDefs"
:rowData="rowData"
>
</ag-grid-vue>
</div>
</div>
</template>
<script>
module.exports = {
async created() {
await this.import(['https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.runtime.js']);
await this.import(['https://cdn.jsdelivr.net/npm/ag-grid-community@28.1.1/dist/ag-grid-community.amd.js']);
const [AgGridVue] = await this.import(['https://cdn.jsdelivr.net/npm/ag-grid-vue@28.1.1/dist/ag-grid-vue.umd.min.js']);
this.$options.components['ag-grid-vue'] = AgGridVue;
this.aggrid_vue_loaded = true;
},
methods: {
import(deps) {
return this.loadRequire()
.then(() => new Promise((resolve, reject) => {
requirejs(deps, (...modules) => resolve(modules));
}));
},
loadRequire() {
/* Needed in lab */
if (window.requirejs) {
console.log('require found');
return Promise.resolve()
}
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
}
}
}
</script>
class TryAGGrid(v.VuetifyTemplate):
template_file = "try_aggrid.vue"
aggrid_loaded = traitlets.Bool(False).tag(sync=True)
aggrid_vue_loaded = traitlets.Bool(False).tag(sync=True)
columnDefs = traitlets.List(traitlets.Dict()).tag(sync=True)
rowData = traitlets.List(traitlets.Dict()).tag(sync=True)
def __init__(self, **kwargs):
self.columnDefs = [
{"headerName": "Make", "field": "make"},
{"headerName": "Model", "field": "model"},
{"headerName": "Price", "field": "price"},
]
self.rowData = [
{"make": "Toyota", "model": "Celica", "price": 35000},
{"make": "Ford", "model": "Mondeo", "price": 32000},
{"make": "Porsche", "model": "Boxster", "price": 72000},
]
super().__init__(**kwargs)
my_grid = TryAGGrid()
my_grid
javascript console error:
tslib.es6.js:25 Uncaught TypeError: Object prototype may only be an Object or null: undefined
at setPrototypeOf (<anonymous>)
at i (tslib.es6.js:25:5)
at AgGridVue.ts:16:32
at Module.fae3 (AgGridVue.ts:16:1)
at n (bootstrap:19:22)
at 8bbf (bootstrap:83:10)
at ag-grid-vue.umd.min.js?v=20220916220758:1:1288
at Object.execCb (require.js?v=d37b48bb2137faa0ab98157e240c084dd5b1b5e74911723aa1d1f04c928c2a03dedf922d049e4815f7e5a369faa2e6b6a1000aae958b7953b5cc60411154f593:1693:33)
at Module.check (require.js?v=d37b48bb2137faa0ab98157e240c084dd5b1b5e74911723aa1d1f04c928c2a03dedf922d049e4815f7e5a369faa2e6b6a1000aae958b7953b5cc60411154f593:881:51)
at Module.enable (require.js?v=d37b48bb2137faa0ab98157e240c084dd5b1b5e74911723aa1d1f04c928c2a03dedf922d049e4815f7e5a369faa2e6b6a1000aae958b7953b5cc60411154f593:1173:22)
I think this module has a misconfiguration in its build, making it not work with requirejs. I had a similar issue with https://github.com/jbaysolutions/vue-grid-layout for which I made a custom version with this change: https://github.com/jbaysolutions/vue-grid-layout/commit/370b0159c2e7c42f954a6a84eefc3325add811aa (I should make a PR to get in the original project).
So the solution would be to make this change for ag-grid-vue too.
Thanks, @mariobuikhuizen ! I looked at aggrid-vue, it looks like they are indeed missing vue as an externals definition, will work with them to see if can be added: https://github.com/ag-grid/ag-grid/blob/latest/community-modules/vue/vue.config.js
I've uploaded a patched version of the library to s3 and made a few changes to the template to make it work as a POC. (If you're using Jupyter Lab, you'll need ipyvue >= 1.8.0):
<template>
<div>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@28.1.1/styles/ag-grid.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@28.1.1/styles/ag-theme-alpine.css">
<div v-if="aggrid_vue_loaded">
<ag-grid-vue
style="width: 500px; height: 200px"
class="ag-theme-alpine"
:columnDefs="columnDefs"
:rowData="rowData"
>
</ag-grid-vue>
</div>
</div>
</template>
<script>
module.exports = {
async created() {
await this.import(['https://cdn.jsdelivr.net/npm/ag-grid-community@28.1.1/dist/ag-grid-community.amd.js']);
// const [{AgGridVue}] = await this.import(['https://cdn.jsdelivr.net/npm/ag-grid-vue@28.1.1/dist/ag-grid-vue.umd.js']);
/* Load patched version of ag-grid-vue.umd.js */
const [{AgGridVue}] = await this.import(['https://s3.us-east-2.amazonaws.com/mario.pub/ag-grid-vue.umd.js']);
this.$options.components['ag-grid-vue'] = AgGridVue;
this.aggrid_vue_loaded = true;
},
methods: {
import(deps) {
return this.loadRequire()
.then(() => new Promise((resolve, reject) => {
this.defineVue();
requirejs(deps, (...modules) => resolve(modules));
}));
},
loadRequire() {
if (window.requirejs) {
console.log('require found');
return Promise.resolve()
}
/* Needed in lab */
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
},
defineVue() {
if (require.defined("vue")) {
return;
}
if (window.jupyterVue) {
/* Since Lab doesn't use requirejs, jupyter-vue is not defined. Since ipyvue 1.8.0 it is added to
* window, so we can use that to define it in requirejs */
define('vue', [], () => window.jupyterVue.Vue);
} else {
define('vue', ['jupyter-vue'], jupyterVue => jupyterVue.Vue);
}
}
}
}
</script>
I recently came across this: https://github.com/koumoul-dev/vuetify-jsonschema-form which generates a UI from a JSON schema.
I was wondering whether it might be possible to use this with ipyvuetify... maybe using the JSON schema to create a vuetify template?
cheers