CanJS基础教程

来源:互联网 发布:js date获取日期 编辑:程序博客网 时间:2024/05/02 04:56

1 加载Loading CanJS

CanJS加载方式:
- 直接引用js库(canjs官网可以定制插件一起打包下载) l
- AMD(requirejs)

<html><head>    <title>CanJS Tutorial</title></head><body> <scriptsrc="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script> <scriptsrc="http://canjs.com/release/latest/can.jquery.js"></script>    <scripttype="text/javascript">        $(function(){            // Your tutorial code here        });    </script></body></html>

2 Constructor Functions

can.Construct可以简单的理解为CanJs的基类, Observables, Models, Controls都是基它的.
创建自己的construct,通过调用can.Construct.extend方法,并且传递两个参数,第一个参数是它的静态属性,第二个参数是对象(原型)属性.如果只传了一个参数,那么该参数当作对象(原型)属性.

var PrivateTodo = Todo.extend({},{  isSecret: function() {    return true;  }});var p = new PrivateTodo();p.allowedToEdit(); // false

2.1 初始化

在第二个参数中的init方法,创建一个实例时会调用.

var Todo = can.Construct.extend({  init: function(owner) {    this.owner = owner;  },  allowedToEdit: function() {    return true;  }});var t = new Todo("me");t.owner; // 'me'

2.2 继承

在init中调用父类的init方法(斜体部分)

var PrivateTodo = can.Construct.extend({  init: function(owner, isShared) {    *can.Construct.prototype.init.apply(this, arguments);*    this.isShared = isShared;  },  allowedToEdit: function(){    return this.owner === "me" || this.isShared;  }});

3 可监控的对象Observables

Observables:可以操作对象的数据并且可以监听数据的改变做相应的处理
Observables包含如下三种类型:

  • can.Map - Used forObjects.
  • can.List - Used for Arrays.
  • can.compute - Used for values/compute.

其中can.Map和can.List是常用的Observable类型,Models和can.rout是基于can.Map.后面会讲到的can.Component的scope属性也是一个can.Map类型

创建一个Map需要传递一个对象(JSON)类型参数,创建一个List需要传递一个数组

var pagination=new can.Map({page:1, perPage:25, count:1388})pagination.attr('perPage');// 25var hobbies=new can.List(['programming','bball','party rocking']);hobbies.attr(2);// 'partying'

3.1 操作属性

Map和List使用attr()方法来读取、修改、添加属性操作,使用removeAttr()方法来删除属性操作

pagination.attr('perPage');    // 25pagination.attr('perPage',50);pagination.attr('perPage');    // 50pagination.attr({page:10, lastVisited:1});pagination.attr();// {page: 10, perPage: 50, count: 1388, lastVisited: 1}pagination.removeAttr('count');pagination.attr();// {page: 10, perPage: 50, lastVisited: 1}

3.2 事件监听

当一个Map类型对象使用attr()方法修改属性时会触发两个事件,一个change事件和一个与被修改的属性同名的事件,可以通过使用bind来监听这些事件,通过unbind去掉对应的监听

paginate.bind('change',function(event, attr, how, newVal, oldVal){attr;  // 'perPage'how;   // 'set'newVal;// 30oldVal;// 50});paginate.bind('perPage',function(event, newVal, oldVal){ newVal;// 30  oldVal;// 50});paginate.attr('perPage',30);

3.3 使用each遍历Map属性

paginate.each(function(val, key){ console.log(key+': '+ val);});// page: 10// perPage: 30// lastVisited: 1

3.4 扩展Map对象

Paginate= can.Map.extend({  limit:100,  offset:0,  count:Infinity,  page:function(){    returnMath.floor(this.attr('offset')/this.attr('limit'))+1;  },  next:function(){    this.attr('offset',this.attr('offset')+this.attr('limit'));  }});var pageInfo=newPaginate();pageInfo.attr("offset")//-> 0pageInfo.next();pageInfo.attr("offset")//-> 100pageInfo.page()        //-> 2

3.5 监控List对象 Observable Arrays

Can.List继承can.Map,所以他有Map的功能,同时还有一些其他的一些常用的方法,当这些方法修改了List,如下所示:

  • indexOf, which looks for an item in a List.
  • pop, which removes the last item from a List.
  • push, which adds an item to the end of a List.
  • shift, which removes the first item from a List.
  • unshift, which adds an item to the front of a List.
  • splice, which removes and inserts items anywhere in a List.

同时我们也可以对List做一些监听,常用的如下所示,详见API

  • the change event fires onevery change to a List.
  • the set event is fired when an element is set.
  • the add event is firedwhen an element is added to the List.
  • the remove event is firedwhen an element is removed from the List.
  • the length event is firedwhen the length of the List changes.

3.6 监控计算 Computed values

Computedvalues(计算值,可读,可写,可监听),简单计算使用can.compute(value)创建,一样可以可读,可写,可监听

var age = can.compute(25),    previousAge = 0;// read the Compute's valueage(); // 25// listen for changes in the Compute's valueage.bind('change', function(ev, newVal, oldVal) {    previousAge = oldVal;});// set the Compute's valueage(26);age();       // 26previousAge; // 25

组合计算使用can.compute(getterFunction)创建

var name = new can.Map({    first: 'Alice',    last: 'Liddell'});var fullName = can.compute(function() {    // We use attr to read the values    // so the compute knows what to listen to.    return name.attr('first') + ' ' + name.attr('last');});var previousName = '';fullName();   // 'Alice Liddell'fullName.bind('change', function(ev, newVal, oldVal) {    previousName = oldVal;});name.attr({    first: 'Allison',    last: 'Wonderland'});fullname();   // 'Allison Wonderland'previousName; // 'Alice Liddell'

转换计算(Converted Computes)

// progress ranges from 0 to 1.var project = new can.Map({ progress: 0.3 });var progressPercentage = can.compute(function(newVal) {    if(newVal !== undefined) {        // set a value        project.attr('progress', newVal / 100);    } else {        // get the value        return project.attr('progress') * 100;    }});percentage();     // 30// Setting percentage...percentage(75);// ...updates project.progress!project.attr('progress'); // .75

4 模型及数据交互

4.1 创建Model

使用can.Model,特定的静态方法与后台交互(RESTful API)。常用的有:

var Todo = can.Model({    findAll: 'GET /todos',    findOne: 'GET /todos/{id}',    create:  'POST /todos',    update:  'PUT /todos/{id}',    destroy: 'DELETE /todos/{id}'}, {});var dishesTask = new Todo({description: 'Do the dishes.'});

4.2 与服务交互

从服务中接受数据can.Model.findAll方法接收一组Model对象

Todo.findAll({},function(todos){    // todos is a can.Model.List of Todo Models.},function(xhr){    // handle errors});

can.Model.findOne同findAll一样,但只接收一个Model对象

update、create 、destroy

调用save方法时,如果该model有Id,则调用update,否则调用create

var shopping=newTodo({description:"Go grocery shopping."});shopping.save(function(saved){    // saved is the saved Todo    saved.attr('description','Remember the milk.');    saved.save();});

调用destroy方法 删除一个model对象,调用destroy

var cats=newTodo({description:"Feed the cats."});cats.save(function(saved){    saved.destroy(function(destroyed){        // destroyed is the Todo that was destroyed    });});

4.3 事件监听

因为Model也是一种特殊的Observes,也可以和其他的Observers一样 bind相同事件,使用Model.bind(eventType, handler(event, model))来监听某Model类型的事件,使用model.bind(eventType, handler(event))来监听具体的model对象的的事件
Model三种特殊事件:

  • created, when an instance iscreated on the server.
  • updated, when an instance isupdated on the server.
  • destroyed, when an instance isdestroyed on the server.
var mop=newTodo({description:'Mop the floor.'});mop.bind('created',function(ev, created){    // created is the created Todo});mop.save();

也可以在Model类上bind上述三种事件,表示任何该类型的对象的操作(created、updated,destroyed)都会监听到

Todo.bind('created',function(ev, created){    // created is the created Todo});

4.4 Model Lists

ModelLists (provided by can.Model.List) are Lists whose items are Models. When one of aModel List’s elements are destroyed, that element is removed from the list.

Todo.findAll({}, function(todos) {    todos.length; // 5               todos[0].destroy(function() {     todos.length; // 4      }}

5 模板Templates(View层)

can.view,根据提供的Model数据渲染模板,并返回一个documentFragment 元素,EJS和 Mustache两种模板语言可以动态的bind到Observes.
Observes change—-> update your template in the DOM

5.1 加载模板

两种方式:从srcipt标签加载(id)和从url加载
Sricpt标签加载
定义

<script type="text/ejs" id="todoList"><% can.each(this,function(val, key){%>    <li><%= val.attr('description')%></li><%});%></script>

通过ID加载

Todo.findAll({},function(todos){    $('#nav').html(can.view('todoList', todos))  });

通过url加载,模版内容是单独的文件

 Todo.findAll({},function(todos){     $('#nav').html(can.view('todos/todos.ejs', todos))  });

5.2 Passing Deferreds(传递延迟对象)

can.view('todos.ejs',{    todos:Todo.findAll().    user:User.findOne({id:5})}).then(function(fragment){    document.getElementById('todos').appendChild(fragment);});

6 Mustache模版

Mustache是一个弱逻辑的模版语言,mustache提供了一些简单好用的标签来渲染绑定的Observes对象,我们可以使用自定义的Helpers(后面会讲到)来扩展它功能.
此处我们Mustache的官网地址http://mustache.github.io/;can只使用的Mustache的一些javascrpt简单的功能.
定义

<script id="template" type="text/mustache">    <h1>Welcome {{user}}!</h1>    <p>You have {{messages}} messages.</p></script>

使用

var data = new can.Map({    user: 'Tina Fey',    messages: 0});var template = can.view("#template", data)document.body.appendChild(template);

生成的Html

<h1>Welcome Tina Fey!</h1><p>You have 0 messages.</p>

6.1 加载/定义模版的方式

有两种方式Script Tags和URL两种方式
Script Tags方式:在html中嵌入模版脚本;使用

<script id="mytemplate" type="text/mustache">    My body lies over the {{.}}</script>
var template= can.view("#mytemplate",'water');can.$(document.body).append(template);

URL方式:把模版内容写在单独的一个文件中

var template = can.view('//lib/views/mytemplate.mustache', dataToPass);can.$(document.body).append(template);

6.2 模版标签

6.2.1 基本标签

{{key}}   输出html转义后的值{{{key}}}  同{{}},但是输出的是非转义的值{{! note }} 模版中的注释{{#key}}BLOCK{{/key}} 有两个功能:一是处理简单的逻辑判断,并根据结果渲染BLOCK.,二是遍历一个非空数组(后续详细介绍){{^key}}BLOCK{{/key}} 判断是key的值为false时,渲染BLOCK{{>key}} 嵌套模版;其中key是子模版的路径/Id

注:undefined, null, false, ”,0,和[]视为false,其他的视为ture

6.2.2 辅助标签

{{helper args…}}
调用Mustache辅助函数,将其返回值插入模板

{{#helper}}

{{#if key}}BLOCKA{{else}}BLOCKB{{/if}}
简单的逻辑判断 如果key是true则渲染BLOCKA 否则渲染BLOCKB

{{#each key}}
遍历一个数组/对象.当遍历数组时可以使用{{@index}}来获得索引;遍历一个数组时,可以使用{{@key}} 来获得对象的属性key

{{data name}}
在el上bind一个当前的contenxt,通过can.data(el,name)去获取

{{(el)->CODE}}
在el上执行内嵌回调代码,常用于对el上处理某操作 ;如{{(el)->el.hammer()}},对元素加上手持事件支持(hammer插件)

6.3 Context

上下文对象:(待续)

6.4 Helpers

Mustache可以注册一个方法用于在模版中调用,这样的方法就是Helpers,这个相当有用,因为Mustache是弱逻辑的模版,我们可以通过Helper来扩展一些逻辑处理.
模版

<scripttype="text/mustache"id="todosList">{{#todos}}<li>{{uppercase description}}</li>{{/todos}}</script>

脚本

var fragment= can.view('todosList',{todos: ['aa','bb']},{    uppercase:function(str){        return str.toUppercase();    }});

这就定义了一个转大写的helper方法

6.4.1 Global helpers

全局的Helpers 使用can.mustache.registerHelper或者Mustache.registerHelper去定义;全局Helpers对于任何Mustache模版都可以使用,这样相当于定义了公用的方法,任何地方都可以使用.

Mustache.registerHelper('money', function(price, option) {    var temp = price;    if ( typeof price == 'function') {        temp = price();    }    return can.mustache.safeString((temp/100).toFixed(1));});

示例中定义了一个对金额转化的helper,把传入的值除以100之后保留一位小数

6.4.2 Datahelpers

DataHelpe就是{{data name}}标签,在元素el上bind一个当前的context,通过can.data(el,name)去获取bind的context.示例:

<script type="text/mustache"id="nameDiv"><div {{data 'name'}} id ='test1'>{{myName}}</div></script>
$(body).append(can.view('nameDiv',{ myName:'JIM'}));var obj= can.data($('#test1'),'name'); //获取的 obj = { myName:'JIM'}

7 控制层 Controls

Controls 是Constructs的子类,它直接控制Model(由can.mode创建)并且结合View(Mustache由can.view创建)进行页面显示.
示例:

var Todos = can.Control({        default:{            age:23,            name: 'sheldon1'        }},{    init: function(el, options) {     var self = this;    Todo.findAll({}, function(todos) {         self.element.html(can.view('todoList', todos));    }); }});

初始化一个Todos的实例如下:

var todosList = new Todos('#todos', {name:'sheldon',age:'27'});

第一个参数可以是选择器,元素,节点列表,这些在init方法中对应第一个参数中的el,此时的el已经是一个Dom元素对象.
第二个参数,options就是传入的json对象和上面提到的defaults里的值进行合并,可以用options.name来取得相对应的值。

7.1 Listening to events

Controls 会自动绑定那些看起来像事件的实例方法,如下面实例中的’li click’方法,这些方法传递两个参数,第一个是事件触发的元素,第二个是事件本身.
can.Control使用了事件托管,所以当添加或者移除元素时,不需要重新绑定事件处理。

varTodos= can.Control({    init:function(el, options){        var self=this;        Todo.findAll({},function(todos){            self.element.html(can.view('todoList', todos));        });    },    'li click':function(el, ev){        console.log('You clicked '+ el.text());    },    'li .destroy click':function(el, ev){        var li= el.closest('li'),        todo = li.data('todo');        todo.destroy();    }});

上面示例中当点击li .destroy元素时,调用了model的destory,从模型中删除了该对象,这样会同时把页面的对应的页面元素也删除掉。

7.2 Templating event handlers

如果在event handler中包含一个占位符,can.Control会在Control的option查找对应的占位符key的值,然后在window中查找.

var Todos = can.Control({defaults: {        destroyEvent: 'click'    }},{    init: function(el, options) {        var self = this;        Todo.findAll({}, function(todos) {            self.element.html(can.view(this.options.view, todos));        });    },    'li .destroy {destroyEvent}': function(el, ev) {        var li = el.closest('li'),        todo = li.data('todo');        todo.destroy();    }});new Todos('#todos', {destroyEvent; 'mouseenter'});

7.3 Rebinding events

Control可以通过调用on 方法Unbind和rebind所有的Control的event handlers,这个在Control开始监听一个指定的model或者改变监听的model时很常用。

varEditor= can.Control({    setDesc:function(){        this.element.val(this.options.todo.description);    },    // change what Todo this Control points at    todo:function(todo){        this.options.todo= todo;        this.on();        this.setDesc();    },    // listen for changes in the Todo    '{todo} updated':function(){        this.setDesc();    },    // when the input changes, update the Todo    ' change':function(el, ev){        this.options.todo.attr('description', el.val());        this.options.todo.save();    }});var todo1=newTodo({id:7, description:'Take out the trash.'}),    todo2 =newTodo({id:8, description:'Wash the dishes.'}),    editor =newEditor('#editor');// start editing the first Todoeditor.todo(todo1);// switch to editing the second Todoeditor.todo(todo2);

7.4 Destroying Controls

Control调用destroy 方法会去除绑定的监听事件,同时移除它关联的元素,但是不回去移除页面上的页面元素。

var list=newTodos('#todos');$('#todos').length;// 1list.destroy();$('#todos').length;// 1

可是,当Control对应的页面元素被删除掉时,Control会去调用destroy 。
当一个应用在Body元素上添加Control和监听事件,我们可以通过调用$(document.body).empty().清除该Control的所有数据

8 组件 Components

Component 可以很容易的结合observables,templates,controls的功能特性。

can.Component.extend({  tag:"my-element",  scope:{    visible:true,    toggle:function(){      this.attr("visible",!this.attr("visible"))    }  },  template:"<div can-click='toggle'>"+              "{{#isVisible}}"+                "<content/>"+              "{{else}}"+                "I am hidden"+              "{{/isVisible}}"+            "</div>",  helpers:{    isVisible:function(options){      returnthis.attr("visible")?        options.fn(): options.inverse();    }  },  events:{    "inserted":function(){      console.log("you add a my-element to the page")    }  }})
  • tag -指定该Component的Html标签,用于在定义的标签中嵌入该component模版的内容.
  • scope –can.Map结构,指定该Component的Model,用来渲染Component 的模版.
  • template – Component的模版,渲染后的模版内容会嵌入到Component的THtml标签中.
  • helpers – 作用于Component的mustache模版辅助方法.
  • events – 类似Control的事件监听.

8.1 Tag

Tag:Component的标签,非Html解析的元素,当自定义的tab标签在模板中出现时,就会在这个标签元素上创建一个Component实例

can.Component.extend({tag:"todos-editor"});vartemplate= can.mustache("Here is my <todos-editor>todos-editor element</todos-editor>")var frag = template();frag.childNodes[1].nodeName//-> "todos-editor element"

注:tag必须小写,带有大写会出现找不到对应的tag问题

8.2 Template

可以是模版的字符串,也可使用can.view指向模版文件,或者使用can.Mustache指向一个已经定义好的Mustache模版
可以在模版中使用<content></context>获取Component标签元素之间的内容
模版中的动态内容请参考第六章,这里要说明的是模版可以访问scope中的定义的属性/方法和helpers中定义的辅助方法.

8.3 Scope

Scope:一个can.map对象,作用在template上,在template中可以访问scope中的属性,

var template = can.mustache("<h1>Todo: {{mytodo.name}}</h1><todos-editor todo='mytodo'></todos-editor>");var mytodo = new can.Map({name: "Do the dishes"});can.Component.extend({    tag: "todos-editor",    template: "{{#if todo}}<input type='text'can-value='todo.name'/>{{/if}}",    scope: {    placeholder:"@"    }});var frag = template({ mytodo: mytodo})document.body.appendChild(frag)

注:can-value 是对于Scope中的属性和表单元素值双线绑定;即修改scope对于的属性表单元素的值也一起修改,修改表单元素的值Scope中对应的属性的值也同样会改变.使用@符号获取Component标签元素中的属性值

这里着重说明下:

  1. 在Scope和helpers中定义的方法可以通过this.获取在Scope中方法/属性,在events中定义的方法要通过this.scope.
    获取在Scope中方法/属性.
  2. 访问Scope中的属性时,可以用 .属性名和 .attr(“属性名”) 两种方式访问,对于不会被监听的属性建议使用 .属性名访问. 对于会被用在Observes的,则建议使用.attr(“属性名”)中.
  3. 修改Scope属性:不被模版中访问的的属性建议使用直接赋值(this.aa = 123),而在模版中会被访问的属性使用.attr(“属性名”,属性值)方式修改属性值

8.3.1 Scope bindings

在scope中添加模板上的事件处理

can.Component.extend({  tag:"todos-editor",  template:"<form can-click='toggle'>Editor: "+              "{{#if visible}}<input type='text'/>{{/if}}"+            "</form>",  scope:{    visible:true,    toggle:function(context, el, ev){      this.attr("visible",!this.attr("visible"))    }  }})

注: can-click=’toggle’表示给元素绑定click事件,事件触发调用的是scope中定义的方法.

8.3.2 Scope value functions

可以在template中访问Scope中的方法

can.Component.extend({  tag:"todos-editor",  template:"<form can-click='toggle'>{{visibility}}: "+              "{{#if visible}}<input type='text'/>{{/if}}"+            "</form>",  scope:{    visible:true,    toggle:function(context, el, ev){      this.attr("visible",!this.attr("visible"))    },    visibility:function(){      returnthis.attr("visible")?        "visible":"invisible"    }  }})

8.3.3 Passing values to a component’s scope

通过在Component的元素上设置属性,可以向Component传递Scope上的数据

var template = can.mustache("<h1>Todo: {{mytodo.name}}</h1>"+                 "<todos-editor todo='mytodo'></todos-editor>")var mytodo = new can.Map({name: "Do the dishes"})can.Component.extend({  tag: "todos-editor",  template: "{{#if todo}}"+              "<input type='text' can-value='todo.name'/>"+            "{{/if}}",  scope: {}})var frag = template({  mytodo: mytodo})document.body.appendChild(frag)

8.4 Helpers

见Mustache的helpers说明

8.5 Events

Events中可以定义对Route的监听,对Socpe中的对象监听(创建/修改/删除等),可以Dom元素上监听事件,也可以监听window上的事件

9 Routing

can.route 是CanJS路由的核心功能,也是一个特殊的Observe,当window.location.hash的值有变动时,can.route的属性值也会更新;同样,can.route的属性值有变动时,window.location.hash的值也会有更新。
可以给can.route附加一个传递URL属性的模板.Eg:

// Give can.route a template.can.route(':type/:id');// If you set a hash that looks like the route...window.location.hash='#!todos/5';// ... the route data changes accordingly.can.route.attr();// {type: 'todos', id: 5}// If the route data is changed...can.route.attr({type:'users', id:29});// ...the hash is changed using the template.window.location.hash;// '#!users/7'// You can also supply defaults for routes.can.route('',{type:'recipe'});// Then if you change the hash...window.location.hash='';// ...the route data reflects the defaults.can.route.attr();// {type: 'recipe'}

9.1 Listening to events

因为can.route也是一个Observe,所以也可以在它上面绑定监听事件

can.route.bind('id',function(ev, newVal, oldVal){    console.log('The hash\'s id changed.');});

你也可以在Control或者Component中监听 routingevents:

varRouting= can.Control({    'route':function(){    // Matches every routing change, but gets passed no data.    },    'todos/:id route':function(data){        // Matches routes like #!todos/5,        // and will get passed {id: 5} as data.    },    ':type/:id route':function(data){        // Matches routes like #!recipes/5,    // and will get passed {id: 5, type: 'recipes'} as data.    }})

can.route.url: 根据当前的route,添加route属性并构造hash值 URL

can.route(':type/:id',{type:'todos'});can.route.url({id:7});// #!todos/7

can.route.link: 同can.route.url功能类似,但它是用来构造HTML中的A(链接元素)的href,同时也可为A元素添加一些属性。

var a= can.route.link(    'Todo 5',    {id:7},    {className:'button'});a;// <a href="#!todos/7" class="button">Todo 5</a>

10 Utility Functions(见API)

0 0
原创粉丝点击