A talk given by Stefan Haslinger
at the Vienna.js Meetup July 30th, 2014
online at http://at.mittenin.at/36
@informatom
stefan.haslinger@informatom.com
I'm 50% of http://mittenin.at
I'm 100% of http://informatom.com
...not
Router - Model - Controller
or even ...
Router - Model - Controller - View - Layout - Template - Components -Template
Contracting.Router.map(function() {
this.resource("contracts", { path: "/" });
});
App.Router.map(function() {
this.resource('posts', { path: '/posts' }, function() {
this.route('new');
});
});
Contracting.Router.map(function() {
this.resource("contracts", { path: "/" }, function() {
this.resource("contract", { path: "/contract/:contract_id" }, function() {
this.resource("contractitem", { path: "/item/:contractitem_id" }, function() {
this.resource("consumableitem", { path: "/consumable/:consumableitem_id" });
});
});
});
});
App.Person = Ember.Object.extend({
say: function(thing) {
alert(thing);
}
});
App.Person = Ember.Object.extend({
// these will be supplied by `create`
firstName: null,
lastName: null,
fullName: function() {
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName', 'lastName')
});
var ironMan = App.Person.create({
firstName: "Tony",
lastName: "Stark"
});
ironMan.get('fullName'); // "Tony Stark"
App.Person = DS.Model.extend({
firstName: DS.attr(),
lastName: DS.attr(),
birthday: DS.attr()
});
App.SongController = Ember.ObjectController.extend({
soundVolume: 1
});
App.PostController = Ember.ObjectController.extend({
isExpanded: false,
actions: {
expand: function() {
this.set('isExpanded', true);
},
contract: function() {
this.set('isExpanded', false);
}
}
});
App.SongController = Ember.ObjectController.extend({
soundVolume: 1
});
App.SongsController = Ember.ArrayController.extend({
sortProperties: ['name', 'artist'],
sortAscending: true // false for descending,
itemController: 'song'
});
adds template features like
{{#each controller}}
{{fullName}}
{{/each}}
var view = Ember.View.create({
templateName: 'say-hello',
name: "Bob"
});
{{#if person}}
Welcome back, {{person.firstName}} {{person.lastName}}!
{{else}}
Please log in.
{{/if}}
Ember.run.queues
// => ["sync", "actions", "routerTransitions", "render", "afterRender", "destroy"]
Person = Ember.Object.extend({
firstName: null,
lastName: null,
fullName: function() {
var firstName = this.get('firstName');
var lastName = this.get('lastName');
return firstName + ' ' + lastName;
}.property('firstName', 'lastName'),
fullNameChanged: function() {
}.observes('fullName').on('init')
});
var person = Person.create({
firstName: 'Yehuda',
lastName: 'Katz'
});
person.set('firstName', 'Brohuda'); // observer will fire
App.ClickableView = Ember.View.extend({
click: function(evt) {
alert("ClickableView was clicked!");
this.get('controller').send('turnItUp', 11);
}
});
{{#view App.ClickableView}}
This is a clickable area!
{{/view}}
wife = Ember.Object.create({
householdIncome: 80000
});
husband = Ember.Object.create({
wife: wife,
householdIncome: Ember.computed.alias('wife.householdIncome')
});
husband.get('householdIncome'); // 80000
husband.set('householdIncome', 90000);
wife.get('householdIncome'); // 90000
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
{{log record}}
var words = ["goodbye", "cruel", "world"];
var emphaticWords = words.map(function(item) {
return item + "!";
});
// ["goodbye!", "cruel!", "world!"]
rewritten to use ES6 modules
1.6+ builds are now transpiled by the es6-module-transpiler into AMD
App.Store = DS.Store.extend();
this.store.find('blogPost');
this.store.find('blogPost', 142);
App.BlogPost = DS.Model.extend({
title: DS.attr(),
createdAt: DS.attr('date'),
comments: DS.hasMany('comment')
});
App.Comment = DS.Model.extend({
body: DS.attr(),
username: DS.attr(),
post: DS.belongsTo('blogPost')
});
var post = store.find('post', 1);
App.FamousPerson = DS.Model.extend({
firstName: DS.attr('string'),
lastName: DS.attr('string'),
occupation: DS.attr('string')
});
expects JSON in the form of
{
"famous_person": {
"first_name": "Barack",
"last_name": "Obama",
"occupation": "President"
}
}
class PostSerializer < ActiveModel::Serializer
attributes :title, :body
end
class PostsController < ApplicationController
def index
@posts = Post.all
render json: @posts
end
end
creates JSON suitable for Active Model Adapter
{
"posts":
[
{ "title": "Post 1", "body": "Hello!" },
{ "title": "Post 2", "body": "Goodbye!" }
]
}
Plays nicely with other libraries
(e.g. Bootstrap)
Contracting.Contract = DS.Model.extend
contractitems: DS.hasMany("contractitem",
async: true
)
term: DS.attr("number")
startdate: DS.attr("date")
createdAt: DS.attr("date")
updatedAt: DS.attr("date")
enddate: (->
moment(@get("startdate")).add("months", @get("term"))
.subtract "days", 1
).property("startdate", "term")
positions: Ember.computed.mapBy('contractitems', 'position')
maxposition: Ember.computed.max('positions')
class PostsController extends Ember.ArrayController
trimmedPosts: ~>
@content?.slice(0, 3)
+observer content.@each
postsChanged: ->
console.log('changed')
p Introducing Emblem.js: a new templating language
that compiles to Handlebars.js
ul
each person in people
li = person.name
li Indentation-based (like Slim, Jade, HAML, etc.)
li Compiles to Handlebars; full compatibility with
both custom and built-in Handlebars helpers
section.ember-features
h1 class=foo The class name of this element is bound to `foo`
p class=isActive:flashing:inactive Ember.js bound css classes
a click="doIt" href="#" Hello, how are you doing, #{name}?
#footer: ul.menu-items: each menu_items: li: a.menu-link href=url: link_text
if something
p something was true!
else
p something was false!
App.SomeThing = Ember.Object.extend({
foo: 'bar',
computedFoo: function(){
return 'computed ' + this.get('foo');
}.property('foo')
});
can be unit tested in a few lines of code
module('Unit: SomeThing');
test('computedFoo correctly concats foo', function() {
var someThing = App.SomeThing.create();
someThing.set('foo', 'baz');
equal(someThing.get('computedFoo'), 'computed baz');
});
var promise = fetchTheAnswer();
promise.then(fulfill, reject);
function fulfill(answer) {
console.log("The answer is " + answer);
}
function reject(reason) {
console.log("Couldn't get the answer! Reason: " + reason);
}
Em.I18n.translations = {
'user': {
'edit': {
'title': 'Edit User'
},
'followers': {
'title': {
'one': 'One Follower',
'other': 'All {{count}} Followers'
}
}
},
'button': {
'add_user': {
'title': 'Add a user',
'text': 'Add',
'disabled': 'Saving...'
}
}
};
Requires minimal Webspace 'cause of Cloudflare
YouTube Video: Building Chrome Extensions With EmberJS
... since Ember 1.0 is out
Goes forward to ...
{{bar}}
but this is what we have to do in Ember today
{{bar}}
Will be compatible to ...
... can be built already
can be called:
My Blog
{{#each}}
{{blog-post}}
{{/each}}
... any more
Contracting.Contract = DS.Model.extend
contractitems: DS.hasMany("contractitem",
async: true
)
term: DS.attr("number")
startdate: DS.attr("date")
createdAt: DS.attr("date")
updatedAt: DS.attr("date")
enddate: (->
moment(@get("startdate")).add("months", @get("term"))
.subtract "days", 1
).property("startdate", "term")
positions: Ember.computed.mapBy('contractitems', 'position'),
maxposition: Ember.computed.max('positions')
Member of ...
Lots of videos already up,
e.g.
Emberconf 2014 Videos
irc.freenode.net #emberjs
Through use and support: Ember Users
Yahoo! - Living Social - Zendesk - Groupon - Travis CI - Boxee - Thoughtbot - Runtastic - Code SchoolThe first books are out
You can talk to me about Ember!
A whole lot of envy from the Angular Community ;-)
... with lots of ressources can be found at
http://at.mittenin.at/36