DEV Community

Cover image for Our first 'Hello World' with VanJS, the new Mithril...
artydev
artydev

Posted on • Edited on

Our first 'Hello World' with VanJS, the new Mithril...

Every UI framerwork deserves its 'counter' example...

SimpleCounter

import van from "https://vanjs.org/code/van-0.11.9.min.js"

const {button, div, h1} = van.tags

const points = van.state(0)

const inc = () => ++points.val 

function App () {
  return div (
    h1("Value : ", points),
    button({onclick : inc}, "INC") 
  )
}

van.add(app, App())
Enter fullscreen mode Exit fullscreen mode

In the following example, the state is represented by an object instead of a single value, read the doc for more informations.
Beware the 'actions' object is my own 'business' and has nothing to do with VanJS.

IncDec

import van from "https://vanjs.org/code/van-0.11.9.min.js"

const {button, div, h1} = van.tags

const State = van.state({
  value : 0
})

const actions = ((state) => {
  const s = state.val;
  return ({
    inc : () => {
      state.val = {...s, value : ++s.value}
    },
    dec : () => {
      state.val = {...s, value : --s.value}
    }    
  })
})(State)

function App (state) {
  return div (
    van.bind(state, (s) => h1("Value : ", s.value)),
    button({onclick : actions.inc}, "INC"),
    button({onclick : actions.dec}, "DEC") 
  )
}

van.add(app, App(State))

Enter fullscreen mode Exit fullscreen mode

And for the final : multiple counters :

MultipleCounters


import van from "https://vanjs.org/code/van-0.11.9.min.js"

const {button, div, h1} = van.tags

// I could have use an array of values...
// But I wanted to play with dynamic properties
const State = van.state({
  value0 : 0,
  value1 : 0,
  value2 : 0
})

const actions = ((state) => {
  const s = state.val;
  return ({
    inc : (index) => {
      state.val = {...s, [`value${index}`] : ++s[`value${index}`]}
    },
    dec : (index) => {
      state.val = {...s, [`value${index}`] : --s[`value${index}`]}
    }    
  })
})(State)

function Counter(state, index = 0) {
   return div (
    van.bind(state, (s) => h1("Value : ", s[`value${index}`])),
    button({onclick : () => actions.inc(index)}, "INC"),
    button({onclick : () => actions.dec(index)}, "DEC") 
  )
}

function App (state) {
  return div(
    [...Array(3)].map((_, index) => Counter(state, index))
  )
}

van.add(app, App(State))

Enter fullscreen mode Exit fullscreen mode

Here is an optimised version, thanks Tao OpimisedMC

import van from "https://vanjs.org/code/van-0.11.9.min.js"

const {button, div, h1} = van.tags

const State = {
  value0 : van.state(0),
  value1 : van.state(0),
  value2 : van.state(0),
  value3 : van.state(0),
}

const actions = ((state) => {
  return ({
    inc : (index) =>  ++(state[`value${index}`]).val ,
    dec : (index) =>  --(state[`value${index}`]).val
  })
})(State)


function Counter (state, index) {
  return div (
    h1("Counter_", index, " : ", state[`value${index}`]),
    button({onclick : () => actions.inc(index)}, "INC"),
    button({onclick : () => actions.dec(index)}, "DEC") 
  )  
}

function App (state) {
  return Object.keys(State)
    .map((_, index) => Counter(state, index) )
}

van.add(app, App(State))

Enter fullscreen mode Exit fullscreen mode

Top comments (6)

Collapse
 
efpage profile image
Eckehard

You can also enable a sequential workflow:

    const { button, div, pre, br } = van.tags
    const add = (x) => van.add(base, x)

    let base = div({ style: "border: 2px dashed red; padding: 5px; display: inline-block;" }, "This is a group", br())
    van.add(document.body, base)

    for (let i = 0; i < 10; i++) {
      add(button({ onclick: () => alert("Hello World") }, "This is button "+i))
      add(br())
    }

Enter fullscreen mode Exit fullscreen mode
Collapse
 
artydev profile image
artydev • Edited

Thank you Ekehard :-)

WF

Nice , I really like your 'mix' VanJS / DML :-)

Based on your tip mAdd:

const { button, h1, div, pre, br } = van.tags

const add = (base) => (elt) => van.add(base, elt)

const target = document.getElementById("app")

add (target) (
  div ( 
    div ( 
      button("A button appended to #app"),
      button("2nd")
    ),
    div ( 
      div(h1("Hello"))  
    )
  )
)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
efpage profile image
Eckehard

I think it should be possible to get the best of both worlds...

Thread Thread
 
artydev profile image
artydev

Sure, I think so.
What is really amazing, it is the size.

Don't hesitate to contact Tao, the author, he is a very nice guy too...

Collapse
 
tao_xin_23db52b1401921dcb profile image
Tao Xin (vanjs.org)

btw: it would be more efficient to use multiple primitive-valued state objects compared to using a single state object that contains everything. In other words, for your example, you can have a JavaScript object that contains multiple state-valued fields, instead of having a single state object with object-typed value.

Instead of

const State = van.state({
  value0 : 0,
  value1 : 0,
  value2 : 0
})
Enter fullscreen mode Exit fullscreen mode

you can have:

const State = {
  value0: van.state(0),
  value1: van.state(0),
  value2: van.state(0)
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
artydev profile image
artydev • Edited

Thank you for your advise Tao :-)

import van from "https://vanjs.org/code/van-0.11.9.min.js"

const {button, div, h1} = van.tags

const State = {
  value0 : van.state(0),
  value1 : van.state(0),
  value2 : van.state(0),
  value3 : van.state(0),
}

const actions = ((state) => {
  return ({
    inc : (index) =>  ++(state[`value${index}`]).val ,
    dec : (index) =>  --(state[`value${index}`]).val
  })
})(State)


function Counter (state, index) {
  return div (
    h1("Counter_", index, " : ", state[`value${index}`]),
    button({onclick : () => actions.inc(index)}, "INC"),
    button({onclick : () => actions.dec(index)}, "DEC") 
  )  
}

function App (state) {
  return Object.keys(State)
    .map((_, index) => Counter(state, index) )
}

van.add(app, App(State))
Enter fullscreen mode Exit fullscreen mode