How to use a Quasar form in StippleUI?

Typically, in a Quasar form the client-side validations, defined as rules in each of the child elements (like QInput, QSelect or your QField wrapped components), get checked when a type=submit button gets pressed with the immediate display of the error message of the first failed validation. This is great UX because the user gets informative error messages immediately without needing to consult with the server at all. Then, if indeed the client side validations clear, the form data is sent to the server and the server can run its own validations.

While this “just works” in the Basic example in the Quasar documents (give it a try! press Submit and see that the name validation trips), I can’t get it to work via the StippleUI syntax.

@hhaensel do you know how to get that to work? Perhaps with Stipple.quasar? See related issue on Stipple.

with the latest versions of Stipple and StippleUI we can now do

using Stipple, StippleUI

const NO = StippleUI.NO_WRAPPER

Stipple.@kwdef mutable struct Example <: ReactiveModel
    name::R{String} = ""
    age::R{Int} = 0
end

model = Stipple.init(Example())

myform() = xelem(:div, class="q-pa-md", style="max-width: 400px", [
    StippleUI.form([
        textfield("Your name *", :client_name,
            :filled,
            hint="Name and surname",
            "lazy-rules",
            rules = "[val => val && val.length > 0 || 'Please type something']"
        ),
        numberfield("Your age *", :client_age,
            :filled,
            :lazy__rules,
            rules="""[
                val => val !== null && val !== '' || 'Please type your age',
                val => val > 0 && val < 100 || 'Please type a real age'
            ]"""
        ),
        toggle("I accept the license and terms", :accept, wrap=NO),
        Stipple.Html.div([
            btn("Submit", type="submit", color="primary", wrap=NO)
            btn("Reset", type="reset", color="primary", :flat, class="q-ml-sm", wrap=NO)
        ])
    ], @on(:submit, "onSubmit"), @on(:reset, "onReset"), class="q-gutter-md", wrap=NO)
])

import Stipple.js_methods
js_methods(m::Example) = raw"""
    onSubmit () {
      if (this.accept !== true) {
        this.$q.notify({
          color: 'red-5',
          textColor: 'white',
          icon: 'warning',
          message: 'You need to accept the license and terms first'
        })
      }
      else {
        this.$q.notify({
          color: 'green-4',
          textColor: 'white',
          icon: 'cloud_done',
          message: 'Submitted'
        });
        this.name = this.client_name;
        this.age = this.client_age;
      }
    },

    onReset () {
      this.client_name = null
      this.client_age = null
      this.accept = false
    }
  """

import Stipple.client_data
client_data(m::Example) = client_data(client_name = js"null", client_age = js"null", accept = false)

function ui()
    page(vm(model), class="container", title="Hello Stipple", 
        myform()
    )
end

route("/", ui)
up(open_browser=true)
2 Likes

How to collect form data in a df?

Could you be more specific in what you are trying to achieve?

Nice tutorial! It is a very informative tutorial. It was extremely helpful.

Hi @hhaensel

I want to use this solution to create a GUI for a local application. I need to collect the information entered in “myForm” to call a function

The Stipple way to achieve this is to build the df on a click of a button from the model fields that are automatically filled., along these lines (without testing)

Stipple.@kwdef mutable struct Example <: ReactiveModel
  name::R{String} = "Stipple!"
  surname::R{String} = "Stipple!"
  street::R{String} = "Stipple!"
  city::R{String} = "Stipple!"
  send::R{Boolean}
end

function restart()
    model = Stipple.init(Example())
    onbutton(model.send) do
        df = DataFrame(
            name=model.name[],
            surname=model.surname[],
            street=street[],
            city=model.city[]
        )
    end
end

restart()

Is that a direction that sounds useful to you? You can, of course, also post a form and then do something with the form data. To do so you could have a further read in the quasar docs. But this is rather Stipple-unspecific.

1 Like

Hi @hhaensel

The problem is that when I click “Sumit”, the data entered in the browser doesn’t fill the model.

Hi @hhaensel

I was able to collect the filled information in Quasar and call functions in my REPL. So I can use Stipple as a GUI for my apps. The example is in the code below. Now all that’s missing is a way for the RELP to notify Quasar when the task is complete

using Stipple, StippleUI

using Genie, Genie.Router, Genie.Requests, Genie.Renderer.Json

const NO = StippleUI.NO_WRAPPER

Stipple.@kwdef mutable struct Example <: ReactiveModel

    name::R{String} = ""

    age::R{Int} = 0

    send::R{Bool} = false #  Lets you know when a button was clicked

end

model = Stipple.init(Example())

myform() = xelem(:div, class="q-pa-md", style="max-width: 400px", [

    StippleUI.form([

        textfield("Your name *", :client_name,

            :filled,

            hint="Name and surname",

            "lazy-rules",

            rules = "[val => val && val.length > 0 || 'Please type something']"

        ),

        numberfield("Your age *", :client_age,

            :filled,

            :lazy__rules,

            rules="""[

                val => val !== null && val !== '' || 'Please type your age',

                val => val > 0 && val < 100 || 'Please type a real age'

            ]"""

        ),

        toggle("I accept the license and terms", :accept, wrap=NO),

        Stipple.Html.div([

            btn("Submit", type="submit", color="primary", wrap=NO)

            btn("Reset", type="reset", color="primary", :flat, class="q-ml-sm", wrap=NO)

        ])

    ], @on(:submit, "onSubmit"), @on(:reset, "onReset"), class="q-gutter-md", wrap=NO)

])

import Stipple.js_methods

# The row: this.send = true; ==>>  Lets you know when a button was clicked

js_methods(m::Example) = raw"""

    onSubmit () {

      if (this.accept !== true) {

        this.$q.notify({

          color: 'red-5',

          textColor: 'white',

          icon: 'warning',

          message: 'You need to accept the license and terms first'

        })

      }

      else {

        this.$q.notify({

          color: 'green-4',

          textColor: 'white',

          icon: 'cloud_done',

          message: 'Submitted'

        });

        this.name = this.client_name;

        this.age = this.client_age;

        this.send = true;    

      }

    },

    onReset () {

      this.client_name = null

      this.client_age = null

      this.accept = false

    }

  """

import Stipple.client_data

client_data(m::Example) = client_data(client_name = js"null", client_age = js"null", accept = false)

function ui()

    page(vm(model), class="container", title="Hello Stipple", 

        myform()

    )

end

onbutton(model.send) do # Allows you to call some function and collect data

    println("Do something")

    model.send[] = false

end

route("/", ui)

up(open_browser=true)

What do you want to do, if it is ready?
If you just want to indicate that the backend is doing something, then set the property of your submit button loading=:send

Hi @hhaensel, It worked. Thanks

I want to use Quasar and Stipple to build GUI for desktop apps. I’m not a programmer, but I work with public health data. I don’t have a lot of time to study, and there is a lack of documentation, but Stipple is awesome for me. The question now is with using logic (if) in StippleUI.form :sweat_smile:

I propose that you open a new issue with a short MWE what you are going to achieve.
The Stipple command for conditional embedding of elements is @iif, e.g. @iif(:showdialog)