Mithril JS + Quasar CSS + mithril stream + UI (pages) -> real app
Tags: mithril, quasar, javascript, app
Published: 2023-10-28 11:42:26

_This is my 9th write up on dev.to site. Example of a real app using Mithril JS, Quasar CSS, mithril stream and UI based on more pages with dynamic switching. Full source code is included as a flems.io link._

My first attempt and example of both Mithril JS and Quasar CSS is described here: 
https://dev.to/pablo_74/mithril-js-quasar-css-here-is-the-proof-4e7c, I bring an example of a real app today.

## App layout (mobile app) ##
My example of the app is based on Quasar layout; it consists of three parts. The top one is a **header** with color background and title, the central one is **page content** (dynamically switched by user interaction) and the bottom one is a **footer** with 'buttons' (not real HTML buttons) for switching content.

### App's header ###
The header is a simple component with Quasar's classes and a title.

```js
var header = {
  view: function(vnode){
    return m("header", {class: "q-header q-layout__section--marginal fixed-top", style: {"background-color":"#FF9900", color:"white"}}, 
      [
        m("div", {class: "q-toolbar row no-wrap items-center", role: "toolbar"}, 
          m("div", {class: "q-toolbar__title ellipsis"}, 
            m("div", {class: "text-big absolute-center text-bold"}, 
              vnode.attrs.title
            )
          )
        ), 
        m("div", {class: "q-layout__shadow absolute-full overflow-hidden no-pointer-events"})
      ]
    )
  }
}
```

### App's footer ###
The footer is the simple component too with Quasar's classes and a row with columns (count depends of number of pages). Each page has a title and this is shown on the **footer** component. Each of them has an onclick event and assigns name of page to a mithril stream variable named showPage.

```js
var showPage = stream("page1");
```

```js
var footer = {
  view: function(){
    return m("footer", {class: "q-footer q-layout__section--marginal fixed-bottom q-px-none", style: {"background-color": "#FF9900", color: "black"}}, 
      m("div", {class: "q-toolbar row no-wrap items-center", role: "toolbar"}, 
        m("div", {class: "q-toolbar__title ellipsis"}, 
          m("div", {class: "row q-pa-none"}, 
            pages.map(function(page){
              return m("div", {class: "col center q-pa-none"}, 
                m("div", {class: showPage() == page.name ? "page-active text-center text-bold " : 'text-center text-bold ', onclick: function(){showPage(page.name)}}, 
                  m("span", {}, page.title)
                )
              )
            })
          )
        )
      )
    )
  }
}
```

### App's page content ###
The most important part of the app. It is a container, the requested page (selected by user in **footer**) shows it's content inside it.

```js
var pageContent = {
  view: function(){
    return m("div", {class: "q-layout q-layout--standard", tabindex: "-1"}, 
      m("div", {class: "q-page-container q-pa-none", style: {"padding-top":"50px", "padding-bottom": "50px"}}, 
        m("div", {class: "q-pa-md"}, 
          pages.map(function(page){
            if (page.name == showPage()) return m(page.comp)
          }),           
        )
      )
    )
  }
}
```

The magic part of switching content is here:
```js
pages.map(function(page){
  if (page.name == showPage()) return m(page.comp)
}),
```

### App's pages ###
Pages are simple components like:
```js
var page4 = {
  view: function(){
    return m("div", [
        m("h4", "This is 'about' page"),
        m("p", "Lorem ipsum dolor sit amet, ...")
    ])
  }
}
```
It is not all of definitions, we need something more. An array of objects describing each page with **name,** **title,** and **comp** (=component) keys.
```js
var pages = [
  {
    name: "page1",
    title: "theory",
    comp: page1,        // component
  },
  {
    name: "page2",
    title: "exerc.",
    comp: page2,        // component
  },
  {
    name: "page3",
    title: "sett.",
    comp: page3,        // component
  },
  {
    name: "page4",
    title: "about",
    comp: page4,        // component
  },  
]
```
This array is used by **footer** component (number of columns and their names) and **pageContent** component (switching content).

## Example of real app ##
Full source code of CSS and JS (as described above) is on [flems.io](https://flems.io/#0=N4IgtglgJlA2CmIBcA2AnAOhQGhAMwgQGdkBtUAOwEMxEkQMALAFzFhFwGMB7C5+PshAACYQB4ARtygBPYZ1hUiRALwAdEFAhEqEhAFoi8BJ2YaAfGopXRYgPRTZ5jiCMnmEXiXoBGJAAYQAF9sSho6BgArEi5efkF6ADcqACdhImYU+BphFWEAdwgKKG58jDAMDKyaAG4rK2S0okZSgAUqAHN4XPTM7LAACg0AB074Hw0ASjrrCkbhCQBXZmYvAH0fHuAbYQBBJF7qwf9J7GEdgCEDqv6Bk6sg+rnUheXViiI1gCYtnf3D24nM6Xa59Gh3SYPJ54RYUUyeCjCIqJeApZgXbjcWAANSosEW8AGyVgk22iKReGERLxuTyPkmwiyzEWKUR-hmoiZLMRPhmj1m81GXU2eTJokSEHg+QOMLhHl4A1JO058GZrOEgw0WkSGjOpGVokNmpAjAALLrziAACqMbRIojCADkzEY8G4KRkjuEQvgU2wBsNGqGIGGFo0ABl3fAwEjhkRFjGSrB3ekIMxhOFmGceB94KZVQS0lQtMNtJwih1hMY0xhhABJPjwLppFLLVtgM7u8vCFEZCBLfExxawRRgHgpYao4TDzIQcvwIhnRScRYOgCOixyM5Sc8lDuknjOEuYVG6w2XqKo6cnUG6RhjRDTGdo6YEEAqwgAcrDOBnFqZ3VrOsTw3bpsnbRlUQTYRbV-eICyLdIqFLAR+GEW93FXM5lj-dMsl3AQtAdRIsUWYYTw8JYHTAKhPCyB08QgJR5C8eANyvFlcO9VE8FRQi7STd1+1XDMiEnHcowdLJJxHK8MD9AMjWDUMOEtR0pCxbIKC9JYVl4KlVg6DpiB7PECQWVV8ngARhH8DNimEekzgyK9uglKgNTTRgd1gAFwUhEBTkU0RjW1C1gAUJQiAODQUlKDQQmCpStQgHVVOAXgFDnABrGUf3lChFWAXT3k+L4MF2AZkVRdFMRxMzCRK9ZysqyY2pCeRFGUGKQB4Xy130YYUn0AAPIgEuBclA2moMNCa6x0si7rLQGiRmERVb1v0NNo2ECheEMYw8xPPRun2-RuGWWAim6TaKH0QwT2KVIoGEO6HqyUwqAoYzbv0Nb7v0Kh4V4XQEDe-Q8G4FcdFOiGWhRFIwb+gGHvOsjxM4JRulRuKRwhgG1jWHN4nTfgRuYfROFQqcdrAIgqZptIBu+zgWmGwxsogYZhEiVcPDwGRGcbFtSghqGYf0V1YHE4QYRHfRCigF0FnxboJstXYeg0YQAGpXj0j5vgqxUgqmmbhDNi3ks0VLwqW6LLT6iHz1G8bgkm63A2Nebwsy67OFyuX8oRIr5rKjALiquYaoxLFcTVgZw+NqO2smDqHZ6u6Ca2um9oOtxjuR-OLqum6c8BlznpSV73v0T6nt+iuHuBgri4GiXV3bqXuER7vUf0dHhkx7GFi2vH+v+9aiZJ1DhHJynqZFpF+Hp4X+GZoG4XZh6iC5nm+b7QX16nOL8nF6HVyl4xZfl2BFegFW9HMjWNAubWRH15PytTyFzZmv+1tAGGgALrAP5PyBoLwfQ-FFDsCUUo8pylDkqc2XJ1ShTtqpfU-8bZmjDNaW0Do7SOngCNVE5YjBeh9ApXBIVlIEMjFkGM3N4yJixCmR86ZMzZlYvmZkU5izczLBWKs11mBAUbM2RkbYEydhSN2XslFhzQWHKOccsttwEUXMIZcIl2JDlgLOec+4tDcCPGmU83oLxI2vPAW86RdpcOfKqKsFB3y1m-HCP8AEUhARAuZcC0EsgQVgvPAQCEMxIRQo2dCR1TBYWnNwnC+F5zFDtCRfE5ErzCWorRSSGZrrMRzEYdiaouLiV4lkdJDpBI7ioqJcSdEFyQRkooCRtDrbGhUsCEA6k6paR0m8fSAxDK-WIg1CyzArI2Tst9V6TleiuR7ExTyLofJ+UGAFK2FtMFpTOBFLqjtYrxQ9klehKV9nCAynCAOQdZQg0KqSb+JtqpojjvVROLzWrtWzEcnqzsBpDTdq-Oh00fbDIWgczOK0p4bThdtVe+d7qFy+nDc6l1mDXQoCjLaj15kvWbvXIuP1wZ11bgibundYZkp7n3OGdch4jyMGPe6E8K4zziHPBeJ80h0wZkvDeENWY705tzXm-MIDH0FafMWHdL4M2lrfNRD9laMFVi-D2msP56wNqVY2Pz-RgtEDsrpwYwqLX+U7LELt75jVBV7G2vt0r+xykgx5YdIURyjm82q8cGpJy9SnU26c-lRSznC5ued9ooviSdcGGKy44qJVXKAhK64N2+k3cljyqUKu7gjS8DKEWD24PoDGFDR64yxJPQmxMuV8HCRTXlK9owCqZsK7e7pd77wlUfIWMrRbn3lZLJVU476qqfmrS0HU346q-kGn+ptzmWySsA0QYCdgQKeIKMYABmX45IEHSmDsghUqDAzoMRHsi0OCzUaHwapDQNo7QkKMCsCsRBqFjE6bshhT6QBMN2qw6CdTUzcJfLw3M-DCwZhLCIn6Yiaz1ikafWRHZhBdggD2BcyjBzTlkmOd0mijE7hMUuYG+jNyGOMXuTDZiLEnjPDYq83EHH3nAy4187iPxeN-FQf8qw-H1gCWBVIwSoIxjCfBcpHkdAxLQhhY6iScICbwvAAiNSeykWycovJzSGJFIdCUtim5ZM4UqXxLTdTcmNNRAZ1pxh2nyUCgaTd5Jt0CmgWMU0h7xSShPQ8gqRUDRXtmrbK5d6-0PvNABl9xCHSOl0Ji79XRf0zW6YwqMLC4ygY4U0J8PCWLQf4AIos8GiDlkQ9WCRKH+DSNbJkORmGFHYaUcJfDaiaAaKnFo8jujKPrmowR2jLSDzmJWUx6xwNLx2PY04wrL43EeK-D+HxQn-FUFAlWcTMYQnQWkxE2T0TJSxKUwknRqmUkabSURbTWSKK2Zog5xixTWJlM4mp7iKQqn8Vqfl2zSgmkFOkk5uSUw3PgKhF5tIPoHR5Ci2KQ01BaA9R9BMDgBoPBYvgD1F0boPS6gNDwMAwwDjo7OBbOwdgWKk94KhLdRrRBI9ECj3HloYFE-NtjhAPUyEUJc0zw0JOyfejGF8SnM1qe0+GPTvgjOdgs72uENH+6ueBh5+zjQ76OmY-NiL8n+7JfTWlyLuXZgPNC6V2z1XXRYtC9EJrnqyXljq+F9wUnhu7fG8DKbj3sucXy8t6IKwICnh2AAFRQNh2MAAGn5lZiDT0eovYaMLN7XMeehxHuwO6XiumLFOOBR6AvuuC4kfat5U8qjVNe4MBfbwpHtlajQA0G9TgGooGQmKiZGEeQ9GiKQOhFBpAQchUB9CrB6b0GQvPrlzWBtlDocVYQT76u6DQSANAAGIABiu+0BoH8P4C06+Uhb5APkW0-AEqztwVFjL5qsHQpbyAAaqwsQSBeGfEu+QkY835V5QtDxi1xAA-1gC-yby1RXQzxf3DVhXAMgKJk1zEWujjG0AdUdXCwtTgOWg0B5QkAgErF0CIFIn4BbQIKxCgEwKwJ7Er3gAwCvEyCIAwE1xXRNRXXXWmlOHODBVgOuRhVb30C7x7zWGaGLDFhILIPgEhjUUw0RjwGTHyClmgFvERHOllyKA3n0HgBRD4HdiCC4I3QNChwoEgWeDSChm4CFWL38yTyCxQSV3T2DCsI3mb3gKENcI72EKoG72WF72OgRAH1SGH2oF8jH3sX+msNWBjCBRGlLRxQtAyFn3Z22BAC-0DmX0umKCpny030tD3wPyPxP1UjPx6j0EX1v09nBSfyuUOQ8Lf0nzqkgMZDFnOj-2QlbTXkHWAM0jx2aNSBoJqMuXcLwMaMQNSGQLTHBic1YQwOgONWwOfwENfx-yBSoASN9AWNoLh3KGQgGAcIVB9GrywOcJGMtQaOdkHRdg2JjS2Lv1oO9lqNGMdmaDaDGEVFpDFy6AwDZ2EAAH4OcxggZ4QUQm1F4O1KDYBXodYDhnQyEITl4oTXpHROxbk3Vk9gtSQ3j8h2gugBgfRfjwh04HjHinjtdRgoVrkOpCTNcjCsD6SgF2DhBDDOC11FIuDTDzDd0ugABhBtdMWwxPQLEOc9JwhCOvc43A45RokQnCTvXwzFXeJ6NNGuC0E6IoW8EaHqfQDHUk8kiLF4iNH0XIvgWiHFTeUYTYpI5gFIg4NI0YGACsJolSC-AAVn8GGBGgIUdK0B+iiL0jAHyI0A9K9KqN4PvUNIuLGPWP0DAGoO2K9l2JomGAONFMKmOKV2tilSpEJL+JUDyBxLxMJDakglryDEJJF0ZMNEMJ9xmnZNwU5K3Wh2jwzGHgT2PTL0cNCwlOEFIEGHbxSAOSd0tAAFkvIfIAApAAZV1QAEVNwdA0heTpzpyEoeDBgfR+TGw+ANyDjMQN5Jgw8s8zCngKgwBsjmABgSgVxaA+AMBHAZAzhkJhhpgngPYwhaAhAMBOBlAXBZ4Eh0jpA5AkcoY+BDAIAAAvdnHwAADi9L5CeAwCImRkOncHn3JEVngAkC5kplXFRDQuOgODuJqF9wj2EGnKoDwFSGwxzx2DjIZnwuGlRWYGIvp1Iqp3IrrAAFFHI5kHIeLHJNg6LyQmLCLTA2KcUOLHVI8KKVTCUiAZAzSRphARLzCrAeYkceVGIOgKADhD4BYZBELZgfyO0tKESgZrpdKDhB1jKrBWCLKpBoSMLRAwLKYrIiCWADgnKoA7KKAzQXK5Y4gILoKDgfAMA91mEOQgrwKPKOgvKFgqDorB9QiXSwqoqdgUqigAyYj0row-L5oHzp0kcMil8V8ciyiNV4BorKqr8doCrIUMBl9rJEQSrF8sjV9cjkxz9hBmqBAar8sDg6r+A-KMATSKUwS2rMjyq19BqChr9qqdgpAa4CKpBAzycVLSDrpXphrFryRlrG964hFVwwqMA3SMqzCXAWKERvAQA91YKkAfBggQFcBsVspvByAQA2chB2Ilyxq4ooAfy-zcAWR2B6AWBmA4wkBqdOAoAKAMBogMJUo-EcVmA7AKBhgwA7BfrUgAABcqHwPdDAfwOwIiNGnGvxIaaQIGmIMAmQScIQZIhAD8r68IIQSAdZQgBG2m0GoQCGqG6nWEYYJfH8j3OwDm7yQgfGjAcqL4cWicrm6IFwW0hm+gSrHcciFm76tWsECoJWkGlIMGk0FYAWuwIWkWkneWzm2AaW2WuwG4Gge23W7m5W+miIdW7mZgZ6oIIAA) I hope it inspires you :-)