Trying to build a SPA / Saas with Flexx
hi,
i'm relative new to flexx, but what i tried / seen so far is really nice 👍 .
for a little project of mine, i'm trying to build a single page application or a software as a service.
currently i'm researching/testing form<->model handling/validation. is there an how-to or a guide about two-way-data-binding?
what i have seen/tried so far is relative cumbersome.
class MyForm(flx.Widget):
data_model = flx.DictProp(
{'no': '1', 'name': 'Test', 'desc': 'Desc', 'dyn': 'Dyn'})
def init(self, data=None):
super().init()
with flx.FormLayout():
self.form_no = flx.LineEdit(title='No.:')
self.form_name = flx.LineEdit(title='Name:')
self.form_desc = flx.MultiLineEdit(title='Description:')
with flx.HBox():
self.save_btn = flx.Button(text='Save')
flx.Widget(flex=1) # Add a spacer
self.update_model(data)
@flx.reaction('form_no.user_done')
def update_no(self, *events):
data = {
'no': events[-1]['new_value']
}
self.update_model(data)
@flx.action
def update_model(self, data):
if data is None:
return
self._mutate_data_model(data, 'replace', -1)
@flx.action
def model_to_form(self):
self.form_no.set_text(self.data_model['no'])
self.form_name.set_text(self.data_model['name'])
self.form_desc.set_text(self.data_model['desc'])
@flx.reaction('save_btn.pointer_click')
def save_form(self, *events):
print(repr(self.data_model))
@flx.reaction('data_model')
def print_model(self, *events):
print(repr(self.data_model))
self.model_to_form()
class MyUI(flx.Widget):
def init(self):
super().init()
with flx.HBox():
self.form = MyForm(flex=1)
class MyApp(flx.PyComponent):
def init(self):
super().init()
self.ui = MyUI()
if __name__ == '__main__':
app = flx.App(MyApp)
app.launch('browser')
flx.start()
having to map each field of the form to the model and back... not very user friendly.
greetings nolan
hi,
i think somehow i found a solution for the form data binding. not pretty, but it works.
first i created a model js component which stores the data of the model:
` class DataModel(flx.JsComponent):
data = flx.DictProp({}, settable=True)
def init(self):
super().init()
@flx.action
def update_data(self, data):
self._mutate_data(data, 'replace', -1)
def dispose(self):
self.data.clear()
super().dispose()
`
next a single binding component to handle the changes in both ways:
` class DataBinding(flx.JsComponent):
model = flx.ComponentProp()
field = flx.ComponentProp()
key = flx.StringProp('', settable=True)
on_done = flx.BoolProp(False, settable=True)
def init(self, model, key, field, on_done=False):
super().init()
self._mutate_model(model)
self._mutate_field(field)
self._mutate_key(key)
self._mutate_on_done(on_done)
@flx.reaction('model.data')
def on_model(self, *events):
self.update_field()
@flx.action
def update_field(self):
self.field.set_text(self.model.data[self.key])
@flx.reaction('field.user_done')
def on_field_done(self, *events):
if self.on_done:
self.update_model(events[-1]['new_value'])
@flx.reaction('field.user_text')
def on_field_text(self, *events):
if not self.on_done:
self.update_model(events[-1]['new_value'])
@flx.action
def update_model(self, value):
data = {
self.key: value,
}
self.model.update_data(data)
def dispose(self):
self.model = None
self.field = None
super().dispose()
`
and at last the form with the bindings:
` class Form(flx.Widget):
model = flx.ComponentProp()
bindings = flx.ListProp([], settable=True)
def init(self):
super().init()
self._mutate_model(DataModel())
with flx.FormLayout():
self.form_no = flx.LineEdit(title='No.:')
self.form_name = flx.LineEdit(title='Name:')
self.form_desc = flx.MultiLineEdit(title='Description:')
with flx.HBox():
self.reset_btn = flx.Button(text='Reset')
self.save_btn = flx.Button(text='Save')
flx.Widget(flex=1) # Add a spacer
self.bind(self.model, 'no', self.form_no, True)
self.bind(self.model, 'name', self.form_name, True)
self.bind(self.model, 'desc', self.form_desc, True)
data = {
'no': '1',
'name': 'Name',
'desc': 'Desc',
}
self.model.update_data(data)
@flx.action
def bind(self, model, key, field, on_done=False):
bd = DataBinding(model, key, field, on_done)
self._mutate_bindings([bd], 'insert', len(self.bindings))
@flx.reaction('reset_btn.pointer_click')
def reset_form(self, *events):
data = {
'no': '1',
'name': 'Name',
'desc': 'Desc',
}
self.model.update_data(data)
@flx.reaction('save_btn.pointer_click')
def save_form(self, *events):
print(repr(self.model.data))
def dispose(self):
for entry in self.bindings:
entry.dispose()
self.bindings.clear()
super().dispose()
class UI(flx.Widget):
def init(self):
super().init()
with flx.HBox():
self.form = Form(flex=1)
class App(flx.PyComponent):
def init(self):
super().init()
self.ui = UI()
if name == 'main': app = flx.App(App) app.launch('browser') flx.start() `
what is not working having a manager to handle the bindings if i use the below manager instead of having the bind method on the form, i'm getting "RuntimeError: DataBinding15 needs a session!"
` class DataManager(flx.JsComponent):
bindings = flx.ListProp([], settable=True)
def init(self, model=None):
super().init()
# self._mutate_model(model)
@flx.action
def bind(self, model, key, field, on_done=False):
bd = DataBinding(model, key, field, on_done)
self._mutate_bindings([bd], 'insert', len(self.bindings))
@flx.action
def clear(self):
self._mutate_bindings([])
def dispose(self):
for entry in self.bindings:
entry.dispose()
self.bindings.clear()
super().dispose()
`
greetings nolan