ViewModel 2 - A new level of simplicity

Manuel, could you clarify: why this code works (logs route name on route change):

Tracker.autorun: ->
  console.log FlowRouter.getRouteName()

And this code doesnt:

Template.tpl.viewmodel
  autorun: ->
    console.log FlowRouter.getRouteName()

What’s even more perplexing is that this code doesnt work as well!:

Template.tpl.onRendered ->
    Tracker.autorun: ->
      console.log FlowRouter.getRouteName()

Maybe the template is destroyed when the route changes (that would stop all computations used by the view model).

Oh, I’m sorry, that was a mistake on my side! Actually, vm’s autorun works just fine :blush:

I was looking for an alternative to the defunct Blaze that would both be component-based and allow the use of Jade templates. After struggling with React and Vue I’m discovering ViewModel and I’m amazed how simple and powerful it is. Great work @manuel !

But I really hope that it will still be possible to use Jade templates with the future ViewModel using React instead of Blaze. Can you confirm this?

ViewModel for React is basically a Babel plugin that transpiles “ViewModel flavored” JSX into “regular” JSX. The code is still valid JSX so in theory you can transpile Jade into JSX and then use the Babel plugin. You would have to set up your pipeline correctly to make it work. IMHO it’s not worth it. The markup tends to be small with ViewModel so you get diminishing returns.

The JSX part in render() could be written in JS. If you prefer Jade style, then you might want to write that part in Coffeescript or Livescript. See the related information here: For those who don't like or even hate JSX. Only need to figure out how to properly use the VM bindings. Should be a matter of looking up at the generated JS file.

Probably @manuel could confirm if that I said above is possible? Is there anyway the “ViewModel flavoured” JSX could be written in CS or LS ? Anyway to put CS/LS between the transpilation process ?

It’s possible. You write JSX but the Babel plugin works with the JavaScript AST. For Jade you would have to compile Jade to JS and feed it to the Babel plugin.

1 Like

I was afraid of that. :disappointed: It really hurts my brain to write JSX, because for me hot reloads and marginal speed increase are not as important as code readability and maintainability Jade templates give me.

I think it would be worse than simple html…

1 Like

I wish I knew how to do that. :sweat:

How can I update property value non-reactively, in order not to trigger infinite loop in autorun?

value: 0
autorun: ->
  if @value() isnt 1 then @value(@value()++)
  else log('value is 1!')

Not sure if this applies to ViewModel, but you could try wrapping it in a nonreactive block.

3 Likes

Something’s got to give here.

You can’t depend on a reactive value and update it reactively at the same time without causing an infinite loop.

You have to either stop depending on the value (use @value.value) or stop reactivity (don’t notify any other computation about the change) while you update the value (Tracker.nonreactive(=> @value(@value()++) like @M4v3R suggests).

2 Likes

In light of this question I’ve found out that autorun triggers even when the value supplied to value is the same that was before. Isn’t autorun redundant in that case? When the value of the property remains the same.

@manuel I’m just trying to improve my code pattern. Would you recommend to use ViewModel properties and functions only for state and logic, that’s directly shown to to the template? Just for example, we have a notification sound on every new message, which way would be the better one?

Template.test.viewmodel( {
lastPlay: new Date(),

playSound() {
 var current = new Date();

	if((current - this.lastPlay())/1000 > 2)
	{
	 var audio = new Audio('/new_message.mp3');
         audio.play();
         this.lastPlay(date);
	}
 }
});

vs

Template.test.viewmodel( {

playSound() {
   var current = new Date();

	if((current - this.lastPlay)/1000 > 2)
	{
	    var audio = new Audio('/new_message.mp3');
           audio.play();
           this.lastPlay = date;
	}
}

onCreated() {
//Defining all props that aren't directly needed for "template state/logic"
 this.lastPlay = new Date();
 }
});

The first one is a lot cleaner and simpler so I definitely prefer it.

Yeah, that is my common pattern, too. Are there any performance differences between these two? If I got it right, the second one isn’t reactive, but the first one is. So I’m wondering if I have some more of this properties which aren’t needed for any template displaying issues, would they slow down my app on low performance devices?

In theory yes, in practice no. It falls under the category of “it just doesn’t matter”.

2 Likes

I don’t understand. If the dependencies don’t change then the autorun shouldn’t execute.

@manuel just btw, I’m getting this in my browser console

Don't have debug information for [T#createViewModel]. Please report it at https://viewmodel.org/help. In the mean time you can turn off checks with `ViewModel.ignoreErrors = true`. See https://viewmodel.org/docs/misc#ignoreErrors for more information.

I’m using 2.9.3 and set persists to false.

Don’t update viewmodel-debug. It’s for the latest ViewModel.