Skip to content

Commit 9f6d60d

Browse files
committed
More progress on instantiable vuejspython components
ok, multi-instance ok, just one socket per component need to fix python computed values need to watch for props change to forward to python maybe need to send changes only to the interested components (js: id->[sockets])
1 parent 99b9013 commit 9f6d60d

File tree

4 files changed

+207
-189
lines changed

4 files changed

+207
-189
lines changed

example-8.html

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8">
5-
<title>Vue-Python sample 3</title>
5+
<title>Vue-Python sample 8</title>
66
<script src="lib/vue.js"></script>
77
<link rel="stylesheet" href="lib/picnic.min.css" />
88
<style>
@@ -14,30 +14,36 @@
1414
<p>
1515
This example explores having custom components.
1616
</p>
17-
i: {{ i }} <button @click="incr(1)">+</button><button @click="incr(-1)">-</button><br/>
17+
i: {{ i }} <button @click="incr(1)">+</button><button @click="incr(-1)">-</button>
18+
-> {{ sqrd }} (py-computed value, in the application)<br/>
1819
<input v-model.number="i"/>
1920
<progress style="width: 500px" max="100" :value="i"></progress>
2021
<my-show :val="i" pre="my-show 1:"></my-show>
2122
<my-show :val="i" pre="my-show 2:"></my-show>
2223
<my-dummy></my-dummy>
2324
<my-dummy></my-dummy>
24-
<!--my-square :val="i" pre="my-square 2:"></my-square-->
25+
<my-square :val="i" pre="my-square 1:"></my-square>
26+
<my-square :val="i" pre="my-square 2:"></my-square>
2527
</div>
2628

2729
<script src="vuejspython.js"></script>
2830
<script>
2931
/* plain vuejs component */
3032
Vue.component('my-show', {
3133
props: ['val', 'pre'],
32-
template: '<div> {{pre}} {{val}}</div>'
34+
template: '<div> {{pre}} {{val}} -> {{val**2}} (in the pure-js template)</div>'
3335
})
3436
/* vuejspython component */
3537
vuejspython.component('Dummy', 'my-dummy', {
36-
template: '<div> {{i}} <button @click="incr(1)">+</button></div>'
38+
template: '<div> {{i}} <button @click="incr(1)">+</button> -> {{i**2}} (in the vjspy template)</div>',
39+
beforeCreate: () => {console.log("beforeCreate")},
40+
created: () => {console.log("created")},
41+
data: () => ({i:parseInt(Math.random()*100)})
3742
})
3843
vuejspython.component('Square', 'my-square', {
3944
props: ['val', 'pre'],
40-
template: '<div> {{pre}} {{val}} -> {{square}}</div>'
45+
template: '<div> {{pre}} {{val}} -> {{square}} (py-computed value, in the component)</div>',
46+
data: () => ({})
4147
})
4248
vuejspython.start('localhost')
4349
</script>

example-8.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,22 @@ class Comp:
1212
def __init__(self):
1313
self.i = 1
1414

15+
def computed_sqrd(self):
16+
return self.i ** 2
17+
1518
def incr(self, d):
1619
self.i += d
1720

1821
@model
1922
class Dummy:
20-
i = 12
23+
i = 3
2124
def incr(self, d):
2225
self.i += d
2326

2427
@model
2528
class Square:
29+
val = 0 # prop
30+
2631
def computed_square(self):
2732
return self.val ** 2
2833

vuejspython.js

Lines changed: 106 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -13,109 +13,30 @@ vuejspython.start = function(wsurl, opt={}) {
1313
wsurl = wsurl + ':' + vuejspython.defaultPort
1414
}
1515
vuejspython.wsurl = wsurl
16-
var wsInit = new WebSocket(wsurl+'/init')
17-
wsInit.addEventListener('message', function(a) {
18-
a = JSON.parse(a.data)
19-
var ws = new WebSocket(wsurl+'/');
20-
let computed = {...opt.computed}
21-
let methods = {...opt.methods}
22-
let watch = {...opt.watch}
23-
let optdata = opt.data
24-
let optel = opt.el || '#main'
25-
for (let k of ['el', 'data', 'watch', 'methods', 'computed']) delete opt[k]
26-
let valuesWhere = {}
27-
for (let k in a.state) {
28-
let watchk = function (v, old) {
29-
if (valuesWhere[k] == v) return
30-
delete valuesWhere[k]
31-
ws.send('UPDATE')
32-
ws.send(k)
33-
ws.send(JSON.stringify(v))
34-
}
35-
if (watch[k] === undefined) {
36-
watch[k] = watchk
37-
} else {
38-
let owk = watch[k]
39-
watch[k] = function (...args) { watchk(...args); owk.bind(this)(...args) }
40-
}
41-
42-
}
43-
for (let k of a.methods) {
44-
methods[k] = function(...args) {
45-
ws.send('CALL')
46-
ws.send(k)
47-
ws.send(JSON.stringify(args))
48-
}
49-
}
50-
let vm = new Vue({
51-
el: optel,
52-
data: () => ({
53-
...a.state,
54-
...optdata()
55-
}),
56-
computed,
57-
methods,
58-
watch,
59-
...opt,
60-
})
61-
ws.addEventListener('message', function(a) {
62-
a = a.data
63-
if (a.startsWith('UPDATE ')) {
64-
let parts = a.split(' ', 2)
65-
let k = parts[1]
66-
let v = a.substr(parts.join(' ').length)
67-
v = JSON.parse(v)
68-
valuesWhere[k] = v
69-
vm.$set(vm, k, v)
70-
}
71-
})
72-
window.vuejspython_vm = vm // for console-based introspection
16+
var ws = new WebSocket(wsurl)
17+
let valuesWhere = {}
18+
let vm = null
19+
ws.addEventListener('open', function(a) {
20+
ws.send('INIT')
21+
ws.send('ROOT')
7322
})
74-
}
75-
76-
let instanceId = 1
77-
78-
vuejspython.component = function(pyClass, name, opt={}) {
79-
// later, consider refactoring if the two are really similar
80-
if (opt.data === undefined) opt.data = ()=>({})
81-
if (opt.props === undefined) opt.props = []
82-
// async component
83-
Vue.component(name, function (resolve, reject) {
84-
let id = instanceId
85-
instanceId++
86-
let wsurl = vuejspython.wsurl
87-
// TODO watch properties below to send notif
88-
var wsInit = new WebSocket(wsurl+'/init:'+pyClass+':'+id)
89-
90-
91-
92-
93-
94-
95-
96-
/// TODO WIP, it seems the promise is called only once
97-
/// the async is more for loading the component, not the instance
98-
/// should consider how to do the creation of instances
99-
/// maybe everything is too single-instance in the code...
100-
/// so above we cannot just connect to init:Truc:1
101-
/// it might well be easy still, as below we don't really access the js object
102-
/// maybe beforeCreate .... .__id = instanceId++ .... etc
103-
wsInit.addEventListener('message', function(a) {
104-
a = JSON.parse(a.data)
105-
var ws = new WebSocket(wsurl+'/');
106-
let props = {...opt.props}
23+
ws.addEventListener('message', function(a) {
24+
a = a.data
25+
console.log("rcv root", a)
26+
if (a.startsWith('INIT ')) {
27+
a = JSON.parse(a.substr('INIT '.length))
10728
let computed = {...opt.computed}
10829
let methods = {...opt.methods}
10930
let watch = {...opt.watch}
11031
let optdata = opt.data
111-
for (let k of ['props', 'data', 'watch', 'methods', 'computed']) delete opt[k]
112-
let valuesWhere = {}
113-
32+
let optel = opt.el || '#main'
33+
for (let k of ['el', 'data', 'watch', 'methods', 'computed']) delete opt[k]
11434
for (let k in a.state) {
11535
let watchk = function (v, old) {
11636
if (valuesWhere[k] == v) return
11737
delete valuesWhere[k]
118-
ws.send('UPDATE:'+id)
38+
ws.send('UPDATE')
39+
ws.send('ROOT')
11940
ws.send(k)
12041
ws.send(JSON.stringify(v))
12142
}
@@ -129,37 +50,113 @@ vuejspython.component = function(pyClass, name, opt={}) {
12950
}
13051
for (let k of a.methods) {
13152
methods[k] = function(...args) {
132-
console.log('CALL:'+id, ...args)
133-
ws.send('CALL:'+id)
53+
ws.send('CALL')
54+
ws.send('ROOT')
13455
ws.send(k)
13556
ws.send(JSON.stringify(args))
13657
}
13758
}
138-
resolve({
59+
vm = new Vue({
60+
el: optel,
13961
data: () => ({
14062
...a.state,
14163
...optdata()
14264
}),
143-
props,
14465
computed,
14566
methods,
14667
watch,
14768
...opt,
14869
})
70+
window.vuejspython_vm = vm // for console-based introspection
71+
} else if (a.startsWith('UPDATE ')) {
72+
let parts = a.split(' ', 3)
73+
let upid = parts[1]
74+
let k = parts[2]
75+
if (upid === 'ROOT') {
76+
let v = a.substr(parts.join(' ').length)
77+
v = JSON.parse(v)
78+
valuesWhere[k] = v
79+
vm.$set(vm, k, v)
80+
}
81+
}
82+
})
83+
}
84+
85+
86+
let instanceId = 1
87+
88+
vuejspython.component = function(pyClass, name, opt={}) {
89+
90+
// later, consider refactoring if the two are really similar
91+
if (opt.props === undefined) opt.props = []
92+
93+
let created = opt.created || (()=>{})
94+
for (let k of ['created']) delete opt[k]
95+
96+
// TODO make a first call to know what is in data (what is reactive)
97+
// same for python-computed I guess
98+
// ... and this will make all this registration async, so async component
99+
// currently, we are forced to declare it also in js
100+
101+
// TODO watch properties below to send notif
102+
Vue.component(name, {
103+
created: function() {
104+
let wsurl = vuejspython.wsurl
105+
let ws = new WebSocket(wsurl)
106+
let valuesWhere = {}
107+
ws.addEventListener('open', function(a) {
108+
ws.send('INIT')
109+
ws.send(pyClass)
110+
ws.send(JSON.stringify(opt.props))
111+
})
112+
113+
let vm = this
114+
vm.__id = 'NOT-SET-YET'
149115
ws.addEventListener('message', function(a) {
150116
a = a.data
151-
if (a.startsWith('UPDATE:'+id+' ')) {
152-
let parts = a.split(' ', 2)
153-
let k = parts[1]
154-
let v = a.substr(parts.join(' ').length)
155-
v = JSON.parse(v)
156-
valuesWhere[k] = v
157-
vm.$set(vm, k, v)
117+
vm.__ws = ws
118+
console.log("rcv", name, a)
119+
if (a.startsWith('INIT ')) {
120+
a = JSON.parse(a.substr('INIT '.length))
121+
vm.__id = a.id
122+
console.log("RECEIVED INIT ID", pyClass, a.id, Object.keys(a.state))
123+
124+
for (let k in a.state) {
125+
vm.$set(vm, k, a.state[k])
126+
vm.$watch(k, function (v, old) {
127+
if (this.__id === 'NOT-SET-YET') return
128+
if (valuesWhere[k] == v) return
129+
delete valuesWhere[k]
130+
ws.send('UPDATE')
131+
ws.send(vm.__id)
132+
ws.send(k)
133+
ws.send(JSON.stringify(v))
134+
})
135+
}
136+
for (let k of a.methods) {
137+
vm[k] = function(...args) {
138+
ws.send('CALL')
139+
ws.send(vm.__id)
140+
ws.send(k)
141+
ws.send(JSON.stringify(args))
142+
}
143+
}
144+
} else if (a.startsWith('UPDATE ')) {
145+
let parts = a.split(' ', 3)
146+
let upid = parts[1]
147+
let k = parts[2]
148+
if (upid === vm.__id) {
149+
let v = a.substr(parts.join(' ').length)
150+
console.log(v)
151+
v = JSON.parse(v)
152+
valuesWhere[k] = v
153+
vm.$set(vm, k, v)
154+
}
158155
}
159156
})
160-
})
161-
wsInit.addEventListener('open', function(a) {
162-
wsInit.send(JSON.stringify(opt.props))
163-
})
157+
158+
created.bind(this)() // not sure when it is best to call it, or whether we should accept it at all
159+
},
160+
...opt
164161
})
165162
}

0 commit comments

Comments
 (0)