If you’re not familiar with the ViewModel library you can think of it as Angular without the ceremonies.
Version 2 is all around nicer and more polished. It’s just out of the oven so I expect a few wrinkles to pop up as people start using it but it should be ok. Topics of interest include:
- State is automatically saved for you across hot code pushes
- You can save the state on the url
- Components can be used as controls
- You can share state between components
- You can compose view elements via mixins
- Inline/Scoped Styles
Go to viewmodel.meteor.com for more information.
And of course it still takes half the code to get things done…
Here’s a greeting box with ViewModel:
<template name="greeting">
<label>First Name</label>
<input {{b "value: firstName"}} type="text">
<label>Last Name</label>
<input {{b "value: lastName"}} type="text">
<input {{b "check: agree"}} type="checkbox" >
<label {{b "text: 'My name is: ' + fullName"}}></label>
<button {{b "enable: canGreet, click: greet"}}>Greet</button>
<button {{b "click: clear"}}>Clear</button>
</template>
Template.greeting.viewmodel({
firstName: '',
lastName: '',
agree: false,
fullName: function() {
return this.firstName() + ' ' + this.lastName();
},
greet: function() {
alert( "Hello " + this.fullName() );
},
canGreet: function() {
return this.firstName() && this.lastName() && this.agree();
},
clear: function() {
this.reset();
}
});
Here’s the same component with Blaze:
<template name="greeting">
<label>First Name</label>
<input id="firstName" type="text" value="{{firstName}}">
<label>Last Name</label>
<input id="lastName" type="text" value="{{lastName}}">
<input id="agree" type="checkbox" checked={{agree}} >
<label>My name is: {{fullName}}</label>
<button id="greet" disabled="{{greetDisabled}}">Greet</button>
<button id="clear">Clear</button>
</template>
Template.greeting.onCreated(function() {
var state = new ReactiveDict("greeting");
this.state = state;
this.fullName = function() {
return state.get("firstName") + ' ' + state.get("lastName");
}
});
Template.greeting.helpers({
firstName: function() {
return Template.instance().state.get("firstName");
},
lastName: function() {
return Template.instance().state.get("lastName");
},
fullName: function() {
return Template.instance().fullName();
},
agree: function(){
return Template.instance().state.get("agree");
},
greetDisabled: function () {
var state = Template.instance().state;
return !state.get("firstName") || !state.get("lastName") || !state.get("agree");
}
});
Template.greeting.events({
'input #firstName': function(event, template) {
template.state.set("firstName", $("#firstName").val());
},
'input #lastName': function(event, template) {
template.state.set("lastName", $("#lastName").val());
},
'change #agree': function(event, template) {
template.state.set("agree", $("#agree").is(":checked"));
},
'click #clear': function(event, template) {
template.state.set("firstName", '');
template.state.set("lastName", '');
template.state.set("agree", false);
},
'click #greet': function(event, template) {
alert( "Hello " + template.fullName() );
}
});
And the same component with React:
Greeting = React.createClass({
dict: new ReactiveDict("greeting"),
getInitialState: function() {
return {
firstName: '',
lastName: '',
agree: false
}
},
componentDidMount: function() {
this.setState({
firstName: this.dict.get("firstName"),
lastName: this.dict.get("lastName"),
agree: this.dict.get("agree")
});
},
updateFirstName: function (e) {
var firstName = e.target.value;
this.dict.set("firstName", firstName);
this.setState({ firstName: firstName });
},
updateLastName: function (e) {
var lastName = e.target.value;
this.dict.set("lastName", lastName);
this.setState({ lastName: lastName });
},
updateAgree: function (e) {
var agree = e.target.checked;
this.dict.set("agree", agree);
this.setState({ agree: agree });
},
clear: function(e) {
e.preventDefault();
this.setState({
firstName: '',
lastName: '',
agree: false
});
},
fullName: function () {
return (this.state.firstName || '') + ' ' + (this.state.lastName || '');
},
greet: function(e){
e.preventDefault();
alert( "Hello " + this.fullName() );
},
greetDisabled: function() {
return !this.state.firstName || !this.state.lastName || !this.state.agree;
},
render() {
return (
<form className="ui form">
<label>First Name</label>
<input type="text" onChange={this.updateFirstName} value={this.state.firstName} />
<label>Last Name</label>
<input type="text" onChange={this.updateLastName} value={this.state.lastName} />
<input type="checkbox" onChange={this.updateAgree} checked={this.state.agree}/>
<label>My name is: {this.fullName()}</label>
<button onClick={this.greet} disabled={this.greetDisabled()} >Greet</button>
<button onClick={this.clear} >Clear</button>
</form>
);
}
});