ViewModel 2 - A new level of simplicity

Exciting news, and looks really clean!

Still experimenting with ViewModel, and was wondering how to get the result of a Meteor.call in a template variable. What is the preferred way for this?

Examples include @nathantreid’s fix explained in his post below.

Template.example.viewmodel
  test: ''
  getData: ->
    Meteor.call 'myMethod', (e, r) =>
      unless e then @test(r)

or

Template.example.viewmodel({
  test: '',
  getData: function() {
    Meteor.call('myMethod', (e, r) => {
      if (!e) {
        this.test(r);
      }
    });
  }
});
3 Likes

For JavaScript you’ll need to preserve the “this” context:

Template.example.viewmodel({
    test: '',
    getData() {
      Meteor.call('myMethod', (e, r) => {
        if (!e) this.test(r);
      });
    }
  });
3 Likes

Right, I got too lazy and forgot about it. :slight_smile:

Actually, in Coffee you have to do the same, but I wrote the example fast and made thin arrow instead of fat, that’s why the same mistake appeared when I converted it into JS.

1 Like

Thanks guys! Was already trying to use templateInstance etc. The fat arrow did it…

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();
 }
});