Commit b6980288 authored by Thomas Mignot's avatar Thomas Mignot

reactive_divs, history compatibility, view, pages, routes

parent a18c2262
No preview for this file type
......@@ -9,20 +9,23 @@ class Component():
attr=None,
events=None,
parent='body',
_id=uuid.uuid1().hex):
self._id = _id
_id=None):
self._id = _id or uuid.uuid1().hex
self.parent = parent
self.tag = 'HTML' if parent is None else tag
self.attr = {} if attr is None else attr
self.events = {} if events is None else events
self.content = [] if content is None else content
self.preparecontent()
def preparecontent(self):
# handle text node as content
if isinstance(content, list):
if isinstance(self.content, list):
for c in self.content:
c.parent = self._id
elif isinstance(content, str) and tag is not 'text':
self.content = [Text(content)]
elif isinstance(self.content, str) and self.tag is not 'text':
self.content = [Text(self.content)]
def addchild(self, component):
component.parent = self._id
......
from project.component import Div
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
class ReactiveDiv(Div):
def __init__(self, name, view, content):
super().__init__(content=content, _id=f'reactive_div_{name}')
self.name = name
self.view = view
self.view.addReactiveDiv(self)
def setcontent(self, content):
self.content = content
self.preparecontent()
channel_name = self.view.channel_name
channel = get_channel_layer()
async_to_sync(channel.send)(channel_name, {
'type': 'handle.ddp',
'params': {
'type': 'changed',
'instance': self.to_obj()
}
})
from project.component import Div, Ul, Li, Span, Input, Button
from project.component import Nav, H1
from project.reactivediv import ReactiveDiv
from todos.models import Tasks
......@@ -64,16 +65,27 @@ class Taskform(Div):
super().__init__(content, attr, _id='task_form')
class Base(Div):
class Welcome(Div):
def __init__(self):
content = [
H1('WELCOME !'),
Button('todos', events={
'click': 'route("todos")'
})]
super().__init__(content)
class Base(Div):
def __init__(self, view):
attr = {
'class': 'container'
}
content = [
Nav([H1('Todos')], {'class': 'navbar navbar-light bg-light'}),
Div([
ReactiveDiv('main-container', view, [
Tasklist(),
Taskform()
])
]) if view.url == 'todos'
else ReactiveDiv('main-container', view, [Welcome()])
]
super().__init__(content, attr, _id='tasklist_container')
......@@ -7,6 +7,7 @@ from django.contrib.auth.models import User
from asgiref.sync import async_to_sync
from todos.methods import Methods
from todos.ddp_routing import ddp_urlpatterns
from todos.models import Clients, Subscriptions
......@@ -70,13 +71,29 @@ class Consumer(JsonWebsocketConsumer, object):
}))
def recv_geturl(self, data):
base = importlib.import_module('.base', 'todos.components')
data = {
'_id': data['_id'],
'type': 'Result',
'params': base.Base().to_obj()
}
self.send(json.dumps(data))
to_url = data['params']['url']
for url in ddp_urlpatterns:
if url.pattern.match(to_url):
cview = getattr(self, 'view', None)
if cview and isinstance(cview, url.callback):
if (cview.onurl(to_url)):
self.send(json.dumps({
'_id': data['_id'],
'type': 'Result',
'params': []
}))
else:
if cview:
cview.ondestroy()
self.view = url.callback(self.channel_name)
self.view.oncreate(to_url)
data = {
'_id': data['_id'],
'type': 'Result',
'params': self.view.render()
}
self.send(json.dumps(data))
break
def recv_method(self, data):
to_send = {'_id': data['_id']}
......@@ -182,3 +199,4 @@ class Consumer(JsonWebsocketConsumer, object):
'name': params['name']
}
}))
from django.conf.urls import url
from .pages import Layout
ddp_urlpatterns = [
url(r'todos', Layout),
url(r'', Layout),
]
from .view import View
from .components.base import Base, Tasklist, Taskform, Welcome
class Layout(View):
def oncreate(self, url):
self.url = url
self.content = Base(self)
def onurl(self, url):
self.url = url
if url == 'todos':
self.setcontent('main-container', [Tasklist(), Taskform()])
else:
self.setcontent('main-container', [Welcome()])
return True
def render(self):
return [self.content.to_obj()]
......@@ -44,8 +44,6 @@
elem.setAttribute(k, component.attr[k]);
});
Object.keys(component.events).forEach(function(k) {
console.log('adding event ' + k);
console.log(component.events[k]);
elem.addEventListener(k, function(e) {
eval(component.events[k]);
});
......@@ -89,10 +87,6 @@
};
constructDOM = function(data) {
if (typeof(data) != 'Array') {
data = [data];
}
data.forEach(function(component) {
var elem = createDOMelement(component);
getElementByUuid(component.parent).appendChild(elem);
......@@ -108,7 +102,8 @@
changeDOM = function(params) {
prev_node = getElementByUuid(params._id);
cur_node = createDOMelement(params);
prev_node.innerHTML = cur_node.innerHTML;
parent = getElementByUuid(params.parent);
parent.replaceChild(cur_node, prev_node);
};
ws.onmessage = function(e) {
......@@ -139,10 +134,12 @@
ws.send(JSON.stringify(data));
};
var initialized = false;
var current_url = '{{ url }}';
var query_string = '{{ query_string }}';
route = function(url, q) {
route = function(url, q, backward=false) {
ws_send({
type: 'geturl',
params: {
......@@ -154,14 +151,25 @@
console.log(e);
else {
constructDOM(r.params);
state = '/' + url;
if (q && q.length)
state += '?' + q;
history.pushState(null, null, state)
if (initialized && !backward) {
path = url;
if (q && q.length)
path += '?' + q;
state = window.location.protocol + '//' +
window.location.hostname + ':' +
window.location.port + '/' +
path;
history.pushState({path: path}, null, state)
}
}
});
};
window.onpopstate = function(event) {
route(event.state.path, '', true);
event.preventDefault()
};
onwsready = function(cb) {
if (typeof(window.onwsready_cb) == 'undefined') {
window.onwsready_cb = []
......@@ -174,6 +182,7 @@
window.onwsready_cb.forEach(function(cb) {
cb();
});
initialized = true;
};
onwsready(function() {
......
class View():
def __init__(self, channel_name):
self.channel_name = channel_name
self.reactive_divs = {}
def addReactiveDiv(self, div):
self.reactive_divs[div.name] = div
def setcontent(self, name, content):
div = self.reactive_divs[name]
div.setcontent(content)
def onurl(self, url):
raise NotImplementedError
def render(self, url):
raise NotImplementedError
def oncreate(self, url):
pass
def ondestroy(self, url):
pass
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment