@ahref
Listening for events makes your apps more brittle because you’re increasing the coupling of your application without getting any added benefit from it.
The relationship between the view (markup if you will) and the view model (code) is the same as that of an application and the services it consumes. They’re bound by a contract which only one party is obliged to honor. The view model and the back end services have to be the adults and ensure they’re not breaking the contract. On the other hand, the view and the application are these frivolous teenagers who can change all they want. All they need to know is that the view model and services will have the functionality they expect, should they decide to use it.
So when you declare events the view depends on the view model for it to work correctly but the view model doesn’t depend on the view. When you listen for events in the view model, the view still depends on the view model (it’s still the engine of the view) and now the view model itself depends on the view too.
One of the assertions of ViewModel is that it makes testing a whole lot easier so lets defend that claim =)
Given the following view model:
Template.main.viewmodel({
name: '',
canEnter: function() {
return !!this.name();
}
});
Here’s how you test it:
describe("Main View Model", function() {
var vm;
beforeEach(function () {
vm = Template.main.createViewModel();
});
it("should have default properties", function () {
assert.equal(vm.name(), '');
});
describe("canEnter", function() {
it("should return false when name is empty", function () {
vm.name('');
assert.isFalse(vm.canEnter());
});
it("should return true when name is not empty", function () {
vm.name('Alan');
assert.isTrue(vm.canEnter());
});
});
});
This way you can easily test every single part of your code, even the methods which execute because of events.
You can also test your views without end to end tests. If you have the following template:
<template name="main">
Name: <input type="text" {{b "value: name"}}>
<button {{b "enabled: canEnter"}}>Enter</button>
</template>
This is how you test it:
describe("Main Bindings", function() {
it("should bind name input", function () {
var bind = Template.main.elementBind("input");
assert.isTrue(_.isEqual(bind, { value: "name" }));
});
it("should bind enter button", function () {
var bind = Template.main.elementBind("button");
assert.isTrue(_.isEqual(bind, { enabled: "canEnter" }));
});
});
We’re testing the view but you have a lot of wiggle room to modify it. You can add and remove elements and move them around, so as long as there’s an input bound to name and a button that triggers canEnter.
The idea is that if your code is working and the bindings are in place, then you don’t have to go through all permutations when you perform your user tests with Selenium.