Emberjs 2

类和实例
http://guides.emberjs.com/v1.11.0/object-model/classes-and-instances/


要定义一个Ember类,直接在Ember.Object上调用extend() 方法即可。
Person = Ember.Object.extend({
say:function(thing){
alert(thing);
}
});


我们还可以从任何已有类的基础上调用其extend()方法创建其子类。
比如我们创建内建的Ember.View类的子类:
app/views/person.js
PersonView = Ember.View.extend({
tagName: 'li',
classNameBindings:['isAdministrator']
});


当我们定义子类的时候,我们可以父类的方法,但是父类的原有方法实现还可以通过_super() 方法在子类中调用。
Person = Ember.Object.extend({
say: function(thing){
var name = this.get('name');
alert(name + " says: "+ thing);
}
});


Soldier = Person.extend({
say: function(thing){
this._super(thing + ", sir!");
}
});


var yehuda = Solier.create({
name: "Yehuda Katz"
});


yehuda.say("YES");




创建实例:
一旦你定义了一个类后,你可以通过调用该类的create()方法创建一个实例。 你在该类中定义的任何方法,属性和计算属性都能被该实例使用。
var person = Person.create();
person.say("Hello");


在创建一个实例时,你可以通过传入一个可选的hash到create()方法来初始化该类的属性。
Person = Ember.Object.extend({
  helloWorld: function() {
    alert("Hi, my name is " + this.get('name'));
  }
});


var tom = Person.create(
{
 name: "Tom Dale"
}
);


tom.helloWorld(); // alerts "Hi, my name is Tom Dale"


处于性能方面的原因,我们不能在调用create()方法是重新定义实例的计算属性或者方法,也不能增加它们。
我们应该只做一些简单属性设置。
如果需要重新定义方法或计算属性,则需要定义一个新的子类,在子类中完成,然后实例化该子类。


我们通常需要用PascalCased 单词作为类名称,而用纯小写单词作为实例名称,比如类Person, 实例person


初始化实例:
当一个新实例通过create()方法被创建时,它的init()方法会被自动调用,这里便是为实例提供设置的最佳地方。
Person = Ember.Object.extend({
init: function(){
var name = this.get('name');
alert(name + ", reporting for duty!");
}
});


Person.create(
{
name: "Stefan Penner"
}
);




如果我们定义继承自框架类的子类,比如Ember.View 或者Ember.ArrayController, 我们可以重写init()方法,但要记住调用this._super()方法。


我们在访问一个对象的属性时,必须使用get和set方法。
var person = Person.create();


var name = person.get('name');
person.set('name', "Tobias Fünke");


如果不使用访问器方法,计算属性将不会起作用,同时观察者将不会被触发,模板也不会被自动更新。




Computer Properties 计算属性
http://guides.emberjs.com/v1.11.0/object-model/computed-properties/


什么是计算属性?
概括来说,计算属性让你可以把函数声明为属性。
你可以通过定义一个函数作为计算属性,当你需要该属性时Ember会自动调用该函数获取属性值。使用上类似静态属性。


它在使用一个或者多个标准属性然后转换或者处理得到一个新的属性值时非常有用。


Person = Ember.Object.extend({
firstName: null,
lastName: null,

fullName: function(){
return this.get('firstName') + ' ' + this.get('lastName');
}.property('firstName','lastName')
});


var ironMan = Person.create({
firstName: "Tony",
lastName: "Stark"
});


ironMan.get('fullName');


我们看到fullName函数调用property, 这里就是声明该函数为一个计算属性。参数告诉Ember,它依赖于firstName,lastName属性。


无论何时你放我fullName, 该函数都会被调用,然后返回其计算结果。




Alternate Invocation替换调用:
到这里,我们也许会想,我们如何才能在一个函数上调用.property函数呢, 这是可以的,因为Ember扩展自function 原型。
如果你想替代上面的定义,可以使用如下写法:
fullName: Ember.computed('firstName','lastName',function(){
return this.get('firstName')+ ' ' + this.get('lastName');
})


改变计算属性:
我们可以使用计算属性作为值来创建一个新的计算属性,我们使用fullName属性加上其它属性为上例添加一个description新的计算属性。
Person = Ember.Object.extend({
  firstName: null,
  lastName: null,
  age: null,
  country: null,


  fullName: function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName'),


  description: function() {
    return this.get('fullName') + '; Age: ' + this.get('age') + '; Country: ' + this.get('country');
  }.property('fullName', 'age', 'country')
});


var captainAmerica = Person.create({
  firstName: 'Steve',
  lastName: 'Rogers',
  age: 80,
  country: 'USA'
});


captainAmerica.get('description'); // "Steve Rogers; Age: 80; Country: USA"




动态更新:
计算属性默认情况下会观察它们以来的属性的任何变化动态的更新自己。
captainAmerica.set('firstName', 'William');


captainAmerica.get('description'); // "William Rogers; Age: 80; Country: USA"


所以,对firstName的修改会被fullName观察到,而fullName又被description属性关注着。
设置任何依赖属性都会将这种变化在你设计的依赖链条上在依赖它们的计算属性中传递。




设置计算属性:
你还可以定义一些在计算属性被设置时Ember应该做的事情,如果你试图设置一个计算属性时,这些定义会被调用,依赖的值就会被设置到先前的上面。
Person = Ember.Object.extend({
  firstName: null,
  lastName: null,


  fullName: function(key, value, previousValue) {
    // setter
    if (arguments.length > 1) {
      var nameParts = value.split(/\s+/);
      this.set('firstName', nameParts[0]);
      this.set('lastName',  nameParts[1]);
    }


    // getter
    return this.get('firstName') + ' ' + this.get('lastName');
  }.property('firstName', 'lastName')
});




var captainAmerica = Person.create();
captainAmerica.set('fullName', "William Burnside");
captainAmerica.get('firstName'); // William
captainAmerica.get('lastName'); // Burnside


Ember将为setter和getter调用计算属性,所以如果你想使用计算属性作为一个setter,你需要检查参数数量来决定它是被getter还是setter调用了。
如果一个值来自于setter,那么它会被作为属性的值缓存起来。




========================
计算属性和数据集成 @EACH
COMPUTED PROPERTIES AND AGGREGATE DATA WITH @EACH
http://guides.emberjs.com/v1.11.0/object-model/computed-properties-and-aggregate-data/


通常,你可能有一个计算属性依赖于一个数组中各项的值,比如,你想统计一个数组中各项数据,获取某种特征值。
app/controllers/todos.js


export default Ember.Controller.extend({
  todos: [
    Ember.Object.create({ isDone: true }),
    Ember.Object.create({ isDone: false }),
    Ember.Object.create({ isDone: true })
  ],


  remaining: function() {
    var todos = this.get('todos');
    return todos.filterBy('isDone', false).get('length');
  }.property('[email protected]')
});


我们注意到这里的依赖key是[email protected] 包含了一个特殊的关键字 @each , 当如下4种情况出现时,它会指示Ember.js 去更新绑定并触发该计算属性的观察者。
1、todos数组对象中任何元素对象的isDone属性发生变化时。
2、有一个新元素被添加到todos数组
3、一个元素从todos数组中被移除
4、该数组对应的controller被更改到别的数组。


import TodosController from 'app/controllers/todos';
todosController = TodosController.create();
todosController.get('remaining');
// 1


我们修改todo的isDone属性,那么remaining属性也相应的被更新。
var todos = todosController.get('todos');
var todo = todos.objectAt(1);
todo.set('isDone', true);


todosController.get('remaining');
// 0


todo = Ember.Object.create({ isDone: false });
todos.pushObject(todo);


todosController.get('remaining');
// 1


这里的@each 关键字只能在第一层级上使用,不能嵌套使用。
也就是说如下使用是不合法的:
[email protected] 或则 [email protected][email protected].


======================
Observers 观察者
http://guides.emberjs.com/v1.11.0/object-model/observers/


Ember 支持观察任何属性,包括计算属性。 我们可以在一个函数上使用observes方法来创建一个对象的观察者。
Person = Ember.Object.extend({
// these will be supplied by 'create'
firstName: null,
lastName: null,

fullName: function(){
var firstName = this.get('firstName');
var lastName = this.get('lastName');

return firstName + ' ' + lastName;
}.property('firstName','lastName'),

fullNameChanged: function(){
//deal with the change
}.observes('fullName').on('init')
});


var person = Person.create({
firstName: 'Yehuda',
lastName: 'Katz'
});


person.set('firstName','lastName'); //observer will fire


观察者和同步问题:
观察者目前在Ember中是同步的,这就意味着一旦它们观察的属性发生改变,它们就会立刻被触发。因此,很容易引起属性不同步问题。
Person.reopen({
//观察者依赖于lastName 和 fullName,因为观察者是同步的,当该函数被调用时,fullName的值没有被更新,所以会显示fullName的历史值。
lastNameChanged: function(){
console.log(this.get('fullName'));
}.observes('lastName')
});


在同时观察多个属性时,同步性还会造成观察者多次被触发问题。
Person.reopen({
  partOfNameChanged: function() {
    // Because both firstName and lastName were set, this observer will fire twice.
  }.observes('firstName', 'lastName')
});


person.set('firstName', 'John');
person.set('lastName', 'Smith');


要解决这个问题,我们需要使用Ember.run.once. 它能保证任何你需要的操作只执行一次并且发生在下一次循环一旦所有的绑定完成。
Person.reopen({
  partOfNameChanged: function() {
    Ember.run.once(this, 'processFullName');
  }.observes('firstName', 'lastName'),


  processFullName: function() {
    // This will only fire once if you set two properties at the same time, and
    // will also happen in the next run loop once all properties are synchronized
    console.log(this.get('fullName'));
  }
});


person.set('firstName', 'John');
person.set('lastName', 'Smith');


观察者和对象初始化:
只有当一个对象完全被初始化后观察者才能被触发。
如果你需要在初始化过程中触发一个观察者作为初始化进程的一部分,使用.on('init')指定观察者应该在初始化后运行。
Person = Ember.Object.extend({
  init: function() {
    this.set('salutation', "Mr/Ms");
  },


  salutationDidChange: function() {
    // some side effect of salutation changing
  }.observes('salutation').on('init')
});




理解计算属性不能触发观察者:
如果你没有使用get获取计算属性,即使它的依赖属性发生变化,它的观察者将永远不会被触发。
你可以认为是它的值从一个未知值到另一个未知值。


这几乎不会影响应用程序编码,因为计算属性几乎都是在获取它们时被观察。比如你获取一个计算属性,把它放到DOM中,然后观察它,以便在依赖属性发生变化时更新DOM。
如果你需要观察一个计算属性,但目前不获取它,那就把它放到init方法里吧。






非Prototype 扩展:
如果你使用Ember的非prototype扩展,你可以使用Ember.observer方法来定义行内观察者。
Person.reopen({
fullNameChanged: Ember.observer('fullName', function(){
//deal with the change
})
});


在类外部定义观察者:
person.addObserver('fullName', function(){
//deal with the change
});




总结, 函数function上用observes, 非prototype扩展使用observer




==============================================
Bindings 绑定
http://guides.emberjs.com/v1.11.0/object-model/bindings/


绑定在两个属性之间创建了一个链接,当一个属性发生变化时,另一个会自动更新到新值。
绑定可以连接多个属性到同一个对象,或者跨越两个不同的对象。
不像其它框架只提供一些系列的绑定实现,Ember.js提供任意对象的绑定,不仅仅是view和model之间。


创建一个双向绑定的最容易的方式是使用计算别名,它指定路径到其它对象。
wife = Ember.Object.create({
householdIncome: 80000
});


Husband = Ember.Object.extend({
householdIncome: Ember.computed.alias('wife.housholdIncome')
});


husband = Husband.create({
wife:wife
});


husband.get('householdIncome');//80000


//someone gets raise
husband.set('householdIncome',90000);
wife.get('householdIncome');//90000


注意,绑定不会立刻更新。 Ember会等到你所有应用程序代码中的同步变化完成后才执行。所以,你可以绑定属性多次而不用担心
超负荷同步绑定。


One-way 绑定:
单向绑定只在一个方向上传递变化。通常单向绑定都是性能优化或者安全,其实大多还是使用双向绑定。
有时候一些特殊情形,可能会使用单向绑定,比如一个默认值和另外的属性一样但是能够被重写。
比如,一个配送地址开始于订单地址一样但是以后可以被更改:


user = Ember.Object.create({
fullName: "Kara Gates"
});


UserView = Ember.View.extend({
userName: Ember.computed.oneWay('user.fullName')
});


userView = UserView.create({
user: user
});


//changing the name of the user object changes the value on the view
user.set('fullName', "Krang Gates");
//userView.userName will become "Krang Gates"


//..but changes to the view don't make it back to the object
userView.set('userName', "Truckasaurus Gates");
user.get('fullName'); // Krang Gates


====================================================
Reopening Classes and instances
重新打开类并实例化
http://guides.emberjs.com/v1.11.0/object-model/reopening-classes-and-instances/


你不必一次就将一个类定义完整。你可以使用reopen方法重新打开一个类给它定义新的属性。
Person.reopen({
isPerson: true
});


Person.create().get('isPerson'); // true


在使用reopen定义是我们还可以重写已有的方法,并调用this._super
Person.reopen({
//override say to add an ! at the end
say: function(thing){
this._super(thing + "!");
}

}):


reopen 方法常用于添加实例方法和属性,这些新增方法和属性能够被该类所有实例对象共享。
在vanilla javascript中,它不添加方法和属性到类的特定实例。


但是当你需要创建类方法或者添加属性到类本身,即创建静态方法和属性时,你可以使用reopenClass方法。
Person.reopenClass({
createMan: function(){
return Person.create({isMan: true})
}
});


Person.createMan().get('isMan')// true




绑定,观察者,计算属性,什么时候使用它们呢?
http://guides.emberjs.com/v1.11.0/object-model/what-do-i-use-when/


有时候新手会感到很困惑什么时候使用计算属性,绑定和观察者,这里有一些指导帮助:
1、使用计算属性通过合成其它属性来创建新属性,计算属性不应该包含任何的应用程序行为,在调用时不应产生副作用。
除了少数情况,对同一个计算属性的多次调用应该返回同一个结果值。(除非计算属性的依赖属性值发生了改变)


2、观察者应该包含对其它属性变化的反应行为。当你需要在绑定完成时同步的完成一些行为时,它非常有用。


3、绑定通常用于确保对象在两个不同层里同步,比如你使用handlebars绑定你的views到你的controller.


=============================
创建一个应用程序
http://guides.emberjs.com/v1.11.0/application/


每个Ember.js应用程序都有一个Ember.Application实例。该对象位于你项目的./app/app.js 文件中。
创建一个Ember.Application实例能给你带来什么?


1、Application实例负责添加事件监听者到文档对象并负责为火事件到你的视图views
2、自动渲染application.hbs 模板
3、自动创建一个router并开始路由,基于当前的URL选择哪个模板和model用于显示。


=================================
应用程序模板
http://guides.emberjs.com/v1.11.0/templates/the-application-template/


application 模板是在应用程序启动时默认被渲染的模板。
你应该把你的header,footer和任何其它修饰内容放到这里。此外,该模板中至少有一个{{outlet}}声明。
{{outlet}} 是一个占位符,router将基于当前的URL选用适合的模板填充它。


app/templates/application.hbs
<header>
<h1>Igor' Blog</h1>
</header>


<div>
{{outlet}}
</div>


<footer>
&copy:2015 Igor's Publishing, Inc.
</footer>


这里header和footer会一直显示该用户,但是<div>中的内容将依赖于当前的用户的URL而变化。


如果你使用Ember CLI创建项目,它会自动帮你在app/templates/ 目录下创建模板文件 application.hbs
=================================================================================================
Handlebars 基础
http://guides.emberjs.com/v1.11.0/templates/handlebars-basics/


Ember.js使用Handlebars 模板库来增强应用程序的用户接口,handlebars模板跟标准的HTML类似,只是增加了嵌入表达式来改变显示。


我们使用Handlebar并扩展它能够获取很多强力的内容。我们可以认为Handlebar模板是一个用来描述用户接口界面的类HTML DSL。
一旦你告诉Ember.js渲染一个给定模板到屏幕时,你不必写任何的额外代码让它保持更新。


模板定义:
默认情况下,调整你的应用程序模板,该模板是自动为你生成在应用程序启动时显示。
你也可以自定义模板,通过名字可以使用。如果你想创建一个供你的应用程序多个区域使用的模板,则你应该研究compnent
,它是一种可重用的模板。


Handlebar表达式:
每个模板都有一个与之对应的controller, 模板在controller中查找要显示的属性。
我们可以使用双打括弧包裹属性名来显示属性。


Hello, <strong>{{firstName}} {{lastName}}</strong>!


该表达式会从模板对应的controller中查找firstName, lastName属性,然后将它们的值插入到HTML中,然后将它们植入DOM中。


默认情况下,你的最顶层模板会绑定到你application controller, 注意该文件默认是不显示的,因为它是通过背后的Ember CLI
产生。为了自定义化controller,创建如下文件:
app/controllers/application.js
export default Ember.Controller.extends({
firstName: "Terk",
lastName: "Glowacki"
});


跟上面的模板组合后会生出 如下 HTML
Hello, <strong>Trek Glowacki</strong>!


这些表达式都是 绑定感知的 ,binding aware。
这就意味着 如果template的 值一旦发生变化 ,你的 HTML将会自动更新 。


随着你的应用程序规模扩大 ,你将会有许多 模板 ,每个模板都会 绑定到相应的 controllers。




条件 表达式 :
http://guides.emberjs.com/v1.11.0/templates/conditionals/


有时候如果某个属性值存在的话你可能只想显示 模板的一部分。这时你可以使用 {{#if}} 助手 来有条件的渲染 某个块 。
{{#if person}}
  Welcome back, <b>{{person.firstName}} {{person.lastName}}</b>!
{{/if}}


如果传入的参数 是false,undefined,null或者 [] 任何 非值时 ,Handlebar都将不会渲染该块 。
如果表达式为非值 ,但是我们还想渲染一个替代模板则使用 {{else}}
{{#if person}}
  Welcome back, <b>{{person.firstName}} {{person.lastName}}</b>!
{{else}}
  Please log in.
{{/if}}


使用 {{#unless}}助手来显示块内容,除非是 false
{{#unless hasPaid}}
  You owe: ${{total}}
{{/unless}}


{{#if}} 和 {{unless}} 都是块表达式的例子 。
这些允许你调用一个助手和一个模板块 。 块表达式看齐来像标准表达式 ,除了它们在助手名前包含一个hash(#),并且需要一个关闭标签 。


行内IF语法:
我们还可以使用行内if来保持代码简洁 :
This message is {{if isImportant 'important' 'unimportant'}}.


我们还可以通过行内if语法将值绑定到属性:
<div class="message {{if isImportant 'important' 'unimportant'}}"></div>


内行if语法还支持使用一个内行unless的反向语法:
This message is {{unless isImportant 'unimportant' 'important'}}.




















你可能感兴趣的:(Emberjs 2)