<html>
<head></head>
<body>
Hello Web
</body>
</html>
Hello Web
<body>
<script src="jquery.js"/>
<script src="handlebars.js"/>
<script src="ember.js"/>
<script>
var App = Ember.Application.create();
</script>
<script type="text/x-handlebars">
Hello Ember!
</script>
<body>
Hello Ember!
Tip: use zepto for mobile
var App = Ember.Application.create({ name: "Ember App!" })
+
<script type="text/x-handlebars"> Hello {{App.name}} </script>
Hello Ember!
Tip: naming convention #1: uppercase means global (App)
var MyClass = Ember.Object.extend({
x: 5,
bestMethodEva: function() {
return this._super() + ":" + this.get('x')
}
})
var myInstance = MyClass.create()
console.log(myInstance.get('x'))
console.log( myInstance.bestMethodEva() );
Tip: naming convention #2: An application extends Namespace, which is why it's uppercase
var obj = Ember.Object.create({
a: {
b: {
c: 5
}
}
})
var c = obj.get('a.b.c'); // getPath in ember < 1.0
if (c) {...} // c is undefined if 'a' or 'b' are falsy
// never again!
if (obj.a && obj.a.b && obj.a.b.c) {
...
}
App.TodosController = Ember.Object.extend({
todos: []
})
App.todosController = App.TodosController.create()
{{App.todosController.todos.length}} TODOs
0 TODOs
setTimeout(function(){
var newTodo = Ember.Object.create({
description: "best todo ever"
})
var todos = App.todosController.get('todos')
todos.pushObject(newTodo)
// console.log(todos.get('lastObject'))
}, 5000)
{{App.todosController.todos.length}} TODOs
1 TODOs
App.TodosController = Ember.Object.extend({
todos: [],
appNameBinding: 'App.name'
// appName field will be added "in-memory", and updated
})
{{App.todosController.appName}}
{{App.name}}
Tip: Ember generates objects in-memory, rather than files
App.TodosController = Ember.Object.extend({
appNameBinding: 'App.name',
todosName: function() {
return 'todos controller in ' + this.get('appName')
}.property('appName')
})
{{App.todosController.todosName}}
todos controller in Ember App!
Tip: Add console.log/alert to check it only runs once.
App.TodosController = Ember.Object.extend({
todos: [],
remaining: function() {
var todos = this.get('todos');
var completed = todos.filterProperty('isDone');
return todos.length - completed.length;
}.property('todos.@each.isDone')
});
{{App.todosController.remaining}} remaining
2 remaining
Tip: 'remaining' field will be recalculated only if a todo's isDone is changed, todo is added/removed, or the entire array replaced.
App.TodosController = Ember.Object.extend({
selectedTodo: undefined,
todosChanged: function() {
var selected = this.get('selectedTodo')
if (!todos.contains(selected)) {
this.set('selected', undefined)
}
}.observes('todos.@each') //.beforeObserver()
})
Tip: can be notified for only removal
var MyClass = Ember.Object.Extend({
people: [],
add: function(name) {
var people = this.get('people')
people.push(name)
console.log(people)
}
});
var obj1 = MyClass.create()
var obj2 = MyClass.create()
obj1.add('Ran Tavory')
obj2.add('Ori Lahav')
Tip: javascript uses prototype inheritance
<div>
{{name}} is fastest programmer eva!
</div>
<div>
<script id="metamorph-3-start" type="text/placeholder"/>
Itay Maman
<script id="metamorph-3-end" type="text/placeholder"/>
is fastest programmer eva!
</div>
Itay Maman is fastest programmer eva!
<ul id="todo-list">
<li>
Take over the universe
</li>
<li>
Have the penne arrabiata (without a tray)
</li>
</ul>
<ul id="todo-list">
{{#each todo in App.todosController.todos}}
<li>
{{todo.description}}
</li>
{{/each}}
</ul>
section {
background: rgba(0,0,0, 0.5);
}
section:hover {
background: #BADA55;
}
{{#if todos.isLoaded}}
<div> spinner </div>
{{#else}}
<ul>
<!-- some list -->
</ul>
{{end}}
Tip: using ember-data, you'll get the 'isLoaded' for free!
<script type="text/x-handlebars" template-name="todo">
todo description: {{description}}
</script>
{{helperName}} or {{helperName X}} or {{helperName X=Y A=B}}
Tip: You can write custom helpers
MyView.create().append('#container')
{{view path.to.class}}
App.TodoView = Ember.View.extend({
templateName: 'todo',
description: "best TODO eva!"
});
<script type="text/x-handlebars" template-name="todo">
todo description: {{description}}
</script>
{{view App.TodoView}}
=
todo description: best TODO eva!
App.TodoView = Ember.View.create({
todo: {..},
a: false,
f: function() {
if(sky.contains('sun') {
this.set('a', true)
}
}
isSelected: {
var selectedTodods = App.todoController.selected
return selected Todos.contains( this.get('todo') )
}.property('App.todosController.selected'),
});
<div {{bindAttr class="isSelected"}}>
<div {{bindAttr class="a:a-is-true:a-is-false"}}/>
</div>
<div class="is-selected">
<div class="a-is-false">
</div>
</div>
or<div>
<div class="a-is-true">
</div>
</div>
2 more options, depending on the weather
<div class="is-selected" bind-attr-id="18">
<div class="a-is-false" bind-attr-id="53">
</div>
</div>
or<div bind-attr-id="18">
<div class="a-is-true" bind-attr-id="53">
</div>
</div>
Top Secret: Ember uses "bind-attr-id" to find what should be updated
<div>
<div class="inner">
{{todo.description}}
</div>
</div>
Tip: choose tag using "tagName" field
App.TodoView = Ember.View.create({
templateName: 'todo',
description: "best TODO eva!",
click: function() {
alert('someone clicked inside somewhere')
}
});
var App = Em.Application.create({
customEvents: {
// jquery-event-name: method-name-in-view
mousewheel: 'mouseWheel'
}
});
Top Secret: Ember captures the events on root
<div>
<div class="inner" {{action methodName on="click"}}>
{{todo.description}}
</div>
</div>
Tip: click is default, remove redundant code (on="click")
App.TodoView = Ember.View.create({
templateName: 'todo',
didInsertElement: function() {
var wrappingElement = this.$()
var elementInside = this.$('.inner')
elementInside.click(function(){
alert('yay')
})
})
)}
Tip: perfect for jQuery plugins!
destroy: function () {
var c = this;
this.unbind();
try {
var elem = this._element
elem.pause(),
elem.removeEvent("error", this._triggerError),
elem.removeEvent("ended", this._triggerEnd),
elem.removeEvent("canplay", this._triggerReady),
elem.removeEvent("loadedmetadata",this._onLoadedMetadata)
_.each(b, function (a) {
c._element.removeEvent(a, c._bubbleProfilingEvent)
}),
_.each(a, function (a) {
c._element.removeEventListener(a, c._logEvent)
}),
elem = null,
this.trigger("destroy")
} catch (d) {}
}
{{#if todo.empty}}
<div> no todos.. call luke </div>
{{#else}}
<ul> <--loop and print them--> </ul>
{{end}}
App.TodoView = Ember.View.create({
templateName: 'todo',
init: function() {
// constructor
console.log( this.get('state') ); // "preRender"
});
});
App.TodoView = Ember.View.extend({
tagName: 'li',
classNames: [a, b]
});
{{view TodoView appNameBinding="App.name" classNames="a b"}}
var view App.TodoView.create({
classNames: "a b"
})
{{view DeathStar}}
{{#view DeathStar}}
Don't use DeathStar's template, use the one of
rebel alliance
{{/view}}
Tip: inner components are specified in the template. Great for unit tests isolation.
<script type="x-handlebars" data-template-name="sound">
<p>
<strong>Song</strong>: {{name}} by {{artist}}
</p>
<p>
<strong>Duration</strong>: {{duration}}
</p>
</script>
Top Secret: named and artist are proxied from the model
App.Store = DS.Store.extend({
revision: 11, // let you know if breaking changes happen.
adapter: 'DS.RESTAdapter', // built-in REST adapter
// adapter: 'DS.FixtureAdapter' // run solo
// adapter: 'App.MyCustomAdapter' // custom
});
var attr = DS.attr;
App.Person = DS.Model.extend({
firstName: attr('string'),
lastName: attr('string'),
birthday: attr('date'),
fullName: function() {
return this.get('firstName') +' '+ this.get('lastName');
}.property('firstName', 'lastName')
});
Tip: Computed properties won't be serialized
App.Person.FIXTURES = [
{
id: 1, // this is a MUST
firstName: 'Ferris',
lastName, 'Bueller'
},
{
id: 2,
firsName: 'Moshe',
lastName: 'Oofnik',
}
]
var attr = DS.attr;
App.User = DS.Model.extend({
profile: DS.belongsTo('App.Profile')
});
App.Post = DS.Model.extend({
comments: DS.hasMany('App.Comment')
});
App.BlogPost = DS.Model.extend({
tags: DS.hasMany('App.Tag')
});
var posts = App.Post.find();
var post = App.Post.find(1);
var people = App.Person.find({ name: "Peter"});
person.get('isDirty');
//=> false
person.set('isAdmin', true);
person.get('isDirty');
//=> true
{
"post": {
"id": 1,
"title": "Rails is omakase",
"comments": [1, 2, 3]
},
"comments": [{
"id": 1,
"body": "But is it _lightweight_ omakase?"
},
{
"id": 2,
"body": "I for one welcome our new omakase overlords"
}]
}
App.Router.map(function() {
this.resource("about"); // { path: "/about" }); redundant
this.resource("favorites", { path: "/favs" });
})
App.AboutRoute = Ember.Route.extend({
setupController: function(controller) {
// Set the AboutController's 'title'
controller.set('title', "My App")
}
})
/
index
IndexController
IndexRoute
index
/about
about
AboutController
AboutRoute
about
/favs
favorites
FavoritesController
FavoritesRoute
favorites
App.UsersRoute = Ember.Route.extend({
model: function(){
return App.User.find()
}
})
App.Router.map(function() {
this.resource("users", function () {
this.resource("user", {path: ':user_id'})
})
})
<script type="..." template-name="application">
kiss my shiny metal ass!
</script>
<script type="..." template-name="application">
kiss my shiny metal {{outlet}}
</script>
<script type="..." template-name="index">
index template
</script>
<script type="..." template-name="users">
all users will be shown here
</script>
App.UsersRoute = Ember.Route.extend({
model: function(){ return App.User.find() }
})
Tip: This is the default implementation
App.Person.FIXTURES = [
{
id: 1,
firstName: ferris,
lastName, bueller
}
]
<script type="..." template-name="users">
{{#each user in controller}}
{{user.name}}
{{/each}}
</script>
Tip: Remember that the Controller proxies request to the model
App.Router.map(function() {
this.resource("users", function () {
this.resource("user", {path: ':user_id'})
})
})
<script type="..." template-name="user">
<header> {{firstName}} </header>
<section> {{firstName}} {{lastName}} is .. </section>
</script>
<script type="..." template-name="users">
{{#each user in controller}}
{{user.name}}
{{/each}}
<section>
{{outlet}}
</section>
</script>