Component puzzle using ViewModel and Blaze


#1

In the ViewModel documentation there’s quite a few examples of building a select element with ViewModel.

Today, I was trying to build a similar component, but in a reusable and self-aware fashion, so that the parent template would know (or get notified) when the value has changed and act accordingly (for example, save the value to database).

That way, I could have these kind of “mini-forms” that would update a single value when it’s changed by the user. Also I could use the same template & markup for the select and just keep changing the saving-function and options&value provided for the select.

(I remember solving a similar puzzle in some way when I built a component for a text element, that could be changed to an inline input by clicking)

Without thinking much, I built an autorun function in the parent ViewModel, much like this:

<template>
   {{>select ref='select' value=originalDataSource.someData options=otherOptions}}
</template>

template.viewmodel({
    saveData() { ... },
    otherOptions() { ... },
    autorun() {
        const selection = this.select.value(); // this.select is the child template
        if (selection && selection !== this.originalDataSource().someData)
            this.saveData(selection);
        }
    }
...

but now I’m wondering, if there would be a better way to achieve this in the MVVM / ViewModel world… I recall somebody, somewhere, saying that there’s always a better way, than using autorun.

This works, but causes a lot of repeating function calls being made: I had to add safeguards to distinguish when the value has actually changed. I realized I find this particular problem difficult, even though I feel it should be quite simple.

Maybe callbacks would work better?

Another idea that followed up to my mind, was having a separate notification-property on the select-viewmodel, that would be changed when the value is changed by user action, and then reset this property when the change gets picked up by the parent component.

Or maybe implement a function on the parent that would get called when the value is changed, probably the simplest solution? What if I have lots of instances?

Or maybe…

I started to feel like I having a blackout in the part of my brain that handles logic, so I thought I’d ask here before coming up with another cumbersome way of achieving the same thing. Would be nice to have some best practices and common patterns in ViewModel’s documentations. I think I’ll start collecting some.


#2

After fiddling with too many different options, I’m starting to feel like coupling the parent and child templates would be the best idea.

Though it seems with ViewModel, to hook into the select-element’s changed event, I have to resort to the manual events handling.

From the docs :smiley:


Note: If by some mysterious reason you need the jQuery event you can use the 'on' helper instead. For example:
{{on "change: function" }}

#3

Doesn’t the change binding work for you?


#4

change-binding is triggered every time the value it’s bind to changes. I wanted to only have something called when the user makes a selection. Therefore, I basically added this in ViewModel:

events: {
   'change select': function() { 
      this.update(); // checks if parent has save function and calls it
   }
}