ViewModel 2 - A new level of simplicity

Today I’m confused about reactivity concept behind VM…
Docs say: All view model properties and methods are available as template helpers.
Meteor docs say: Under the hood, each helper starts a new Tracker.autorun. When its reactive dependencies change, the helper is rerun. So, helpers are reactive computations.
And then VM docs say: ViewModels are reactive **data sources**. That means you can use the properties and methods anywhere you would use a reactive source.
Whoa…
So, am I right saying that

  1. VM props (and meths) can be used in Spacebars as helpers, but they do not substitute helpers.

  2. VM meths don’t rerun even if they have a call to reactive data source inside.

And I don’t understand how can a function (VM method) be a reactive data source, not computation? What does that mean?

VM props (and meths) can be used in Spacebars as helpers, but they do not substitute helpers.

They do substitute helpers. So for <div>User Id: {{userId}}</div>, instead of doing:

Template.example.helpers({
  userId: function() {
    return Meteor.userId();
  }
})

You can use a view model:

Template.example.viewmodel({
  userId: function() {
    return Meteor.userId();
  }
})

VM meths don’t rerun even if they have a call to reactive data source inside.

They do rerun when the reactive data source inside changes. This is as long as there’s an autorun that uses the VM method. If no one uses the VM method then it won’t rerun, even if the reactive sources inside change.

And I don’t understand how can a function (VM method) be a reactive data source, not computation? What does that mean?

“reactive source” is the shorthand used to mean a function that at some point calls .depend()/.changed() on a Tracker dependency object. You can have a “reactive source” without a Tracker dependency object but you rarely, if ever, see it in practice.

A computation is basically a function that keeps track of other functions (usually added with autorun) that need to re-run when the computation is invalidated (for practical purposes think of .changed() called on one of the dependencies.

Check out this screen cast I made a while back: https://meteorcasts.net/ep/11

3 Likes

Thank you. I guess, I’ll need more practice with reactivity to get my head around all this.

Btw, who is this Alan Brito person anyway? :sunglasses:

Just want to say a big thank you to @manuel. I really enjoy working with ViewModel, especially version 2. Compared to “normal Blaze” it gives me a real structure and a more “component orientated” way of doing this. I’ve developed my last 2 projects with it and yeah, never thought that I could do it so fast and without any big struggle.

3 Likes

Well, there is a ‘support viewmodel’ button in Subscription section of the docs now :wink:

Yeah, I got it. I’ve to say that this “donation” function is a little bit hidden. I only got it because I wanted to see a video and there was a comment from Manuel that we need to subscribe.

Well, most of freeware authors seem to be timid when it comes to asking for donations :blush:

@manuel, when I tell people about Viewmodel, I often notice them experiencing same issue with documentation.

As it is now, Viewmodel features are divided into three categories. But when you open any of them, you don’t see the others. You only see the link back which doesn’t really tell people anything.

So imagine that I talk with somebody on the net and this person struggles with state. What I do is I link them to https://viewmodel.meteor.com/docs/viewmodels#share and explain how it can help in a particular use case. But then we talk about Viewmodel and after some time or even many days I realize that they don’t even know that such features as bindings exist in Viewmodel. Because from the place where they entered documentation, there’s no list of them available, or even the link to bindings from the existing category.

It’s certainly better than it was before the last layout changes. But I believe it still could be more convenient to navigate, like f.e. on http://astronomy.jagi.io or at http://guide.meteor.com.

1 Like

I don’t know if that was discussed earlier, but still worth asking - what are the possibilities that ViewModel will get official endorsement from MDG? I for one liked Blaze more than React/Angular and Blaze + ViewModel seems like an ideal solution for Meteor to me, because it was built specifically for Meteor, unlike React/Angular.

Can we get a comment about this from @sashko, @sacha or another MDG member? I personally think it would be very cool of Meteor officially endorsed this view layer and didn’t abandon Blaze altogether.

3 Likes

Also, I would like to take this moment to thank @manuel for his awesome work (I did subscribe to videos, least I can do). I’m really impressed about the pace of development of this package, he is closing Github issues much faster than they appear, which is outstanding. Please keep up the good work!

4 Likes

Thanks. I’ll update the navigation system…

1 Like

Note that @sacha doesn’t work at MDG, he is the editor of the popular book Discover Meteor.

Anyway, there is a thread here:

Hey, Manuel! Could you advise a simple pattern for a state machine using Viewmodel?

Same way you would with a normal JS object. Here’s the proverbial turnslide:

Template.turnslide.viewmodel({
  locked: true,
  push() {
    if (!this.locked()) {
      // Enter (do something)
      this.locked(true);
    }
  },
  insertCoin() {
    this.locked(false);
  }
});

The docs have another example using the enter binding.

2 Likes

@manuel I’m having an issue when a template has the same name like a VM property and I do something like this:

Answer.html

<div class="comments">
        {{#if comments.count}}
            {{#each comments}}

                {{>comment commentData=this}}
            {{/each}}
        {{/if}}

     <div class="write">
            <textarea placeholder="Kommentar eingeben..." {{b "value:comment"}}></textarea>
            <i class="ion-chatbubble-working" {{b "click:addComment,class:{text-primary:canSave}"}}></i>
        </div>
    </div> 

VM:

Template.answer.viewmodel({


comment: null,
commentAstro: null,
canSave:false,
astro:{},


comments() {
    return Comments.find({answerId: this.astro()._id}, {sort: {createdAt: 1}});
 }
}

In this example the problem is {{>comment}}, it doesn’t include the template “comment”, because I’m having a property defined that is also called “comment”. So I’m running into an context issue. Is there any way to solve this? I only knew Template.dynamic but I also want to commit the commentData property to the comment template, without writing a helper for it (which I would need when I’ve to do Template.dynamic template=‘comment’ data=commentObjectHelper.

There is no way around it as you have a name collision. You have to rename one or the other. I rename the templates.

2 Likes

Docs say: Properties themselves can have properties and methods of their own. And: view model method 'this' always refers to the current view model.
But this code doesnt work

template(name='tpl')
  .ui.dropdown($b= "ref: regionDropdown")

Template.tpl.viewmodel
  region:
    reset: -> @regionDropdown.dropdown('set text', 'some text')
  onRendered: -> @region().reset()

Logs Cannot read property 'dropdown' of undefined

reset: => @regionDropdown.dropdown('set text', 'some text') doesnt work neither, as it passes document context instead of viewmodel.

In your case reset isn’t a method of the view model, it’s a method of region. this.regionDropdown fails because this is the region object, which doesn’t have the regionDropdown property.

Yeah, I get this. So is there a way to make it work? Or there is no such notion as a child property for a viewmodel property?

I’d just put the reset in the view model, not in an object of a VM property. If you must then make a function out of region:

Template.tpl.viewmodel
  region: ->
    that = this
    reset: -> that.regionDropdown.dropdown('set text', 'some text')