Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to build a SPA / Saas with Flexx #734

Open
NolanG241 opened this issue Nov 15, 2022 · 1 comment
Open

Trying to build a SPA / Saas with Flexx #734

NolanG241 opened this issue Nov 15, 2022 · 1 comment

Comments

@NolanG241
Copy link

NolanG241 commented Nov 15, 2022

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

@NolanG241
Copy link
Author

NolanG241 commented Nov 16, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant