ViewModel 2 - A new level of simplicity

@trusktr

How do you preserve the UI state when a hot code push occurs?

While we’re on examples, how would you do the following in React. That is, display a list of names, be able to select one item and set its class to active, and have another component show information about the selection. I imagine you would use Redux (in this example they’re not that far from each other, but they could be).

<body>
  {{> selectionInfo}}
  {{> peopleList }}
</body>

<template name="peopleList">
  <ul>
    {{#each people}}
      {{> personItem}}
    {{/each }}
  </ul>
</template>

<template name="personItem">
  <li {{b "text: name, click: selectedName(name), class: { active: selectedName === name }"}}></li>
</template>

<template name="selectionInfo">
  Person Selected: {{selectedName}}
</template>
ViewModel.share({
  personSelection: {
    selectedName: ''
  }
});

Template.peopleList.viewmodel({
  people: [
    { name: 'Tom' },
    { name: 'Dick' },
    { name: 'Harry'}
  ]
});

Template.personItem.viewmodel({
  share: 'personSelection'
});

Template.selectionInfo.viewmodel({
  share: 'personSelection'
});

I do it constantly for checking if LMB\Space\Enter was pressed on an element:

a($b= "click: signOut, keypress: signOut") Sign out

signOut: (event)->
	#check for click\space\enter 
	if event.type is 'click' or event.which is 32 or event.which is 13
		event.preventDefault()
		Meteor.logout()
a($b= "click: signOut, keypress: signOut")

Isn’t it… just a string? So in theory, you could do:

ViewModel.mixin({
  bindings: {
    myAwesomeBinding: "click: whatever, enter: whatever"
  }
});

and then

a($b=myAwesomeBinding)

I have to think about this one a bit more.

btw, have you tried a($b= "'click keypress': signOut") Sign out? I think it should work…

I get InvalidCharacterError: String contains an invalid character on 4.0.4.

@brajt @avalanche1

We’re talking about events, not true bindings. You should have been able to use multiple events in the first place.

Update viewmodel-debug to 2.6.2. So a($b= "'click keypress': signOut") Sign out now works.

Now please make the case for multiple bindings using the same predicate (be it a function or a value/object). I don’t want to do the lazy thing and just add the feature “because it’s not going to break anything if it isn’t used”.

Thanks

1 Like

Confirmed for this case. Thanks!

input($b="'click keypress': test")
input($on="'mouseover mouseout keydown': test")
input($b="'input blur': test")

Works fine

input($b="'hover enter': test")
input($on="'hover enter': test")

Both return message:
"There isn't a bind or event called 'hover' on template 'layout'
"There isn't a bind or event called 'enter' on template 'layout'

input($on="'keyup focusout': test")

Surprise, even with same message as above, focusout still worked.

input($b="value: chomik, 'focus change': test")
input($b="value: chomik, 'change click': test")

Change doesn’t work, focus and click do.

input($b="value: chomik, 'change': test")

In this case, change works.

1 Like

Collection and Session hold the UI state across hot code pushes. Don’t get me wrong though, I think this is a really nice shortcut for Blaze templates! I bet there’s a different solution for React…

I remembered why I used the reactive-dict in the example as soon as I created a demo without it and updated it. I was like “wait a sec, where’s my input?”

I want to know what that is =)

btw, when I ask for examples in React (or any other framework for that matter), I don’t do it in a feisty way, I honestly want to know how others do things so I can compare and maybe copy something. ViewModel is so simple because of that.

I always say that if someone can point to something React/Angular/whatever does in a simpler manner than ViewModel, it won’t be for long because I’ll copy it. I don’t get that very often though, the last one was with elm, which I copied the way it handles event streams like mouse position.

Right now I’m working to bring that simplicity to React. By the time I’m done I hope you’ll be able to write this:

GreetingBox({
  firstName: '',
  lastName: '',
  agree: false,
  fullName() {
    return this.firstName() + ' ' + this.lastName();
  },
  greet() {
    alert( "Hello " + this.fullName() );
  },
  canGreet() {
    return this.firstName() && this.lastName() && this.agree();
  },
  render() {
    <div>
      <label>First Name</label>
      <input b="value: firstName" type="text" />

      <label>Last Name</label>
      <input b="value: lastName" type="text" />

      <input b="check: agree" type="checkbox" />
      <label b="text: 'My name is: ' + fullName" />

      <button b="enable: canGreet, click: greet">Greet</button>
      <button b="click: reset">Clear</button>
    </div>
  }
});

One thing to note is that, while I strongly believe doing more with less ceremonies is important, I think the biggest advantage of using ViewModel is the way components communicate with each other. It makes it super easy for two components to share a small part of their state and also to use child components as controls, among other things. That’s what I want to bring to React.

I wrote a post on another thread (can’t remember which one) that I felt it should be in the documentation. This where I’m coming from with ViewModel.

4 Likes

I thought about this and I’m adding the functionality so it accepts multiple bindings (not just events). The reason is that I can’t predict what custom bindings people are going to be creating or combining. I guess you could say I took the lazy route in the end.

I’m also removing the check to see if the event is supported because it’s not foolproof (as you can see with focusout). It’s a shame because I like the nice message to tell you if you misspelled a binding or event. Can’t win them all.

Pick up 4.0.5

2 Likes

The moment you release it, I switch to React. :slight_smile:

4 Likes

Hi @manuel, that looks great. I understand VM is on top of Meteor now. Is there any chance to make VM work without Meteor? I ask because I am currently forced to work in an n-tier corporate stack (APIs + Sails JS + React) which is quite remote from embracing Meteor. Just wondering :slight_smile:

That’s exactly what I’m working on. A ViewModel that uses React instead of Blaze. It will be separate from Meteor so you’ll be able to use it wherever you use React.

3 Likes

Awesome! When would the first release?

Also , Is it correct to say we don’t need other state management libs like Redux anymore?

Do you need Flux/Reflux/Redux/Delux with ViewModel right now? =)

I’d say it will probably take me 3 months to get something out there.

3 Likes

Hi @manuel,
I can’t seem to pass in the @index value of an each loop.
The value passed into the method below returns undefined.

{{#each array}}
    {{value}}
    <button type="button" {{b "click: method(@index)"}}>click</button>
{{/each}}

Is this a limitation of VM or have I missed something.

Cheers :slight_smile:

It’s a limitation of Blaze. You have to add the index on the method that returns the array.

Cheers @manuel! Thanks for replying :slight_smile:

Got a ViewModel for React prototype working!

The code is transpiled to plain React code so it can be used for SSR. It uses hot module reload. Notice the absence of anything that isn’t your code.

9 Likes