justpy-org / justpy

An object oriented high-level Python Web Framework that requires no frontend programming
https://justpy.io
Apache License 2.0
1.22k stars 96 forks source link

Request: Make JustPy more reactive #60

Closed mjmare closed 4 years ago

mjmare commented 4 years ago

Please forgive my ramblings below. I know what I'd like to see but don't know whether they are feasible.

First some shortcomings in the current implementation of models/bindings in JustPy:

Then, a wish: One thing I liked about using Vue (+Quasar) was the way to have the components adjust themselves to changes in data. For example, say I want a list of items and I want to be able to filter that list of items. The user enters some filter criteria (through a search box or check boxes. Then I'd construct the list like so:

 <q-list>
          <q-item v-for="service in filteredServices" :key="service.uuid"
                  :to="{name: 'service_details', params: { uuid: service.uuid }}"
                  separator
          >
            <q-item-main>
              {{ service.path }}
            </q-item-main>
          </q-item>
        </q-list>

where the filtered list would be a computed data property:

  export default {
    data () {
      return {
        services: [],
        filterText: ''
      }
    },

    computed: {
      filteredServices () {
        return this.services.filter(el => {
          const path = el.path.toLowerCase()
          return path.indexOf(this.filterText.toLowerCase()) !== -1
        })
      }
    },

(the services data property would be fetched from the server).

The big advantage of all this is that the list would automatically adjust if any of the underlying data changes (ie the filtered list changed due to the user entering filter criteria). With JustPy I can't see another way of detecting the change in filter criteria, remove the old list components, construct a new component list based on the newly filtered data, insert that component list into the page. It works but feels clumsy because you have to manually manipulate the components (old and new).

Anyway I hope the above makes any sense.

elimintz commented 4 years ago

Please see below an example of a program that does what you would like:

import justpy as jp

def check_box_clicked(self, msg):
    wp = msg.page

    def my_filter(var):
        for letter in self.value:
            if var.startswith(letter):
                return True
        return False

    filtered_list = list(filter(my_filter, wp.list_item_text))

    for c in wp.q_list.components:
        if c.components[0].text not in filtered_list:
            c.show = False
        else:
            c.show = True

def reactive_list_test():
    wp = jp.QuasarPage()
    d = jp.Div(classes="q-pa-md", style="max-width: 400px", a=wp)
    wp.list_item_text = ['apple', 'ad', 'aardvark', 'again', 'bean', 'bath', 'beauty', 'can', 'corner', 'capital']
    wp.q_list = jp.QList(dense=True, bordered=True, padding=True, classes="rounded-borders", a=d)
    for word in wp.list_item_text:
        q_item = jp.QItem(clickable=True, v_ripple=True, a=wp.q_list)
        jp.QItemSection(text=word, a=q_item)

    option_group = jp.QOptionGroup(type='checkbox', color='green', a=d, input=check_box_clicked, value=['a', 'b', 'c'],
                                   inline=True, classes='q-ma-lg',
                    options=[{'label': 'A words', 'value': 'a'}, {'label': 'B words', 'value': 'b'}, {'label': 'C words', 'value': 'c'}])

    return wp

jp.justpy(reactive_list_test)

You don't need to remove the old components or insert new ones, just change their show attribute and the framework does the rest. So I think JustPy is reactive. Because the show attribute changes, what is displayed changes (you can also accomplish this by changing classes).

I am interested in this discussion because it shows me that I have not been explaining the framework well. Please let me know your thoughts on the issue and I hope we can continue this conversation. Also, if any aspect of the program above is not clear, please let me know.

mjmare commented 4 years ago

I think that would work but it feels less clean as the Vue way of doing things. I think it leads to spaghetti code in real programs.

Since I posted this issue I have experimented with custom components, and that cleans up the code immensely. One of the advantages of the Vue style is that one does not have to keep references to the components around in order to manipulate them later. Especially when one use deeply nested structures (eg a List with Items Which contain Sections which contain Text and Avatars etc etc etc). When one uses custom components the state is "local" (available through self) and its representation (the HTML components) is also "local". By using the react method makes the representation "follow" the state. Much more elegant. (Don't know if this paragraph makes any sense to you). So, in conclusion, IMHO JustPy is not reactive in the Vue sense, but the need is less due to the ease of use of custom components. But this needs a very different way of thinking. More articles on how to structure programs in a JustPythonic way would be very welcome.

giodegas commented 3 years ago

I found that measuring the network performance between the browser and my justpy app server , after few interactions the used bandwidth rises up to 4-5 Mbps and stays constant, reducing the response time of the GUI, worsening the user experience. This arises more evident when the user is not is the same LAN of the server.

I have read about VueJs being a bottle neck in other issues, and indeed I found it the most CPU hungry module with the browser performance analyzer.

Is there any smart way to reduce the impact of VueJs to the browser (Firefox) performance? I Would it be possible to disable the usage of VueJs? Maybe avoiding to use some kind of page class and/or widgets?

elimintz commented 3 years ago

Sorry, I don't know. Maybe someone else reading this has expertise on this subject

giodegas commented 3 years ago

I improved the efficiency of my app now, reducing the amount of data exchanged, The first prototype was quite abusive. But I am afraid that scaling the application, the problem may come back. Thank you, anyway.