# API Reference __ 命名约定 __ - `Component` 此接口同时属于`Regular`及其子类. - `Regular` 此接口只属于Regular本身 - `component`: 代表Regular及其子类的实例 ## 静态接口 ### Component.extend( options ) {#extend} 创建一个继承自`Component`的组件,参数 [options](#options) 中的所有属性都会成为 __原型属性__. __Usage:__ ```js const Component = Regular.extend({ template: `

{count}

`, tick: function(posfix) { this.data.count += posfix; } }) ``` __Arguments__ |Param|Type|Detail| |--|--|--| |options|Object|组件定义和配置,见 [__options__](#options)| __Return__ Component > 注意 extend 是原型继承的一个语法糖,options是实例共享的 ### Component.implement(options) {#implement} 扩展Component的__原型属性与方法__。 [options](#options) 与`Component.extend`一致.  __Arguments__ |Param|Type|Detail| |--|--|--| |options|Object|组件定义和配置,见 [__options__](#options)| __Return__ Component __小技巧__: 通过 __implement__ 与 __extend__ 扩展的方法,都可以通过`this.supr(arg1, arg2..)`调用父类同名函数 > "Regular的类式继承体系来源于著名的[ded/klass](https://github.com/ded/klass)." __Example__ ```js Component.extend({ init: function(){ this.supr() // call the super init } }).implement({ hello: function( msg ){ this.supr( msg ) // call the super hello } }) ``` ### new Component(options) __Example__ ```javascript var component = new Component({ // ...other options data: { username: "leeluolee" } }) component.$inject('#container'); ``` __Arguments__ |Param|Type|Detail| |--|--|--| |options|Object|组件定义和配置,见 [__options__](#options)| __Return__ Component的实例: [查看实例接口](#instance) > 通过实例化传入的 [options](#options) 将成为__实例属性__, 意味它将覆盖 extend 与 implement 的定义.并且方法中无法使用 `this.supr()` ### Component.directive(name, definition) {#directive} 定义指令 __Arguments__ |param|type|detail| |--|--|--| |name|String| 指令名| |definition.link|Function[required]| 链接函数,指令编译时会被调用 | |definition.update|Function[optional]| 更新函数,指令绑定的表达式变更时会被调用 | |definition.params|Array[optional]| 指令参数,指定指令参数列表 | > 如果 definition 是一个 Function,则视为成为link函数 __definition.link(elem, value)__ - elem 绑定的元素节点 - value 属性值(可能是字符串或是一个[Expression]; - this 这里的this指向component组件本身 __definition.update(elem, value)__ - elem 绑定的元素节点 - value 属性值(可能是字符串或是一个[Expression]; - this 这里的this指向component组件本身 __definition.params__ __Example__ (source code of builtin `r-html` ) ```javascript Regular.directive('r-html', function(elem, value){ this.$watch(value, function(newValue){ elem.innerHTML = newValue }) }) ``` 这里由于[$watch](../core/binding.md)同时接受字符串或者Expression, 所以我们可以在模板里传字符串或插值, 最终r-html的效果是一样的 ```html
``` 如果必要你也可以在函数返回一个destroy函数做指令的销毁工作(比如绑定了节点事件). 需要注意的是, regular中watch数据是不需要进行销毁的, regular会自动清理对应的数据绑定 __Example__ ```javascript Regular.directive('some-directive', function(elem, value){ return function destroy(){ ... destroy logic } }) ``` ###Component.filter regularjs 当然也支持普遍存在于模板中的过滤器,过滤器支持链式的多重调用. regularjs也支持[__双向过滤__](#two-way-filter), 来帮助你解决双向数据流的需求 __Usage__ `Component.filter(name, factory)` __Syntax__ `{Expression|filter1: args.. | filter2: args...}` __Arguments__ |param|type|detail| |--|--|--| |name|string| 过滤器名称| |factory|function object| 创建新的自定义过滤器| __factory__ - `factory.get(origin, args...)` [Function]: 数据从终点到源的处理函数. - `factory.set(dest, args...) ` [Function]: 从最终结果反推到源头的处理函数. . _如果传入的factory是函数类型,则自动成为factory.get_ __Example1 >__ 一个简单的日期格式化过滤器 ```javascript // simplest date format var filter = function(){ function fix(str){ str = "" + (str || ""); return str.length <= 1? "0" + str : str; } var maps = { 'yyyy': function(date){return date.getFullYear()}, 'MM': function(date){return fix(date.getMonth() + 1); }, 'dd': function(date){ return fix(date.getDate()) }, 'HH': function(date){ return fix(date.getHours()) }, 'mm': function(date){ return fix(date.getMinutes())} } var trunk = new RegExp(Object.keys(maps).join('|'),'g'); return function(value, format){ format = format || "yyyy-MM-dd HH:mm"; value = new Date(value); return format.replace(trunk, function(capture){ return maps[capture]? maps[capture](value): ""; }); } }(); Regular.filter("format", filter) ``` 然后在模板中使用 ```html

{time| format: 'yyyy-MM-dd HH:mm'}

``` 输出 ```html

2014-12-31 12:30

``` #### 双向过滤器 双向过滤器主要是帮助我们实现数据的对流, 对任意数据读或写操作时可以进行过滤操作, 与计算属性不同的是,双向过滤器定义是不与具体的数据进行绑定,它是一种可复用的抽象. 双向过滤器如其名,经常会用在双向绑定上, 由于这个特性, r-model 得以与一个数组类型实现双向绑定。 当然你也可以使用它在其它可能有“数据回流”场合,比如[内嵌组件](?syntax-zh#composite) take `{[1,2,3]|join: '-'}` for example 过滤器定义 ```js Regular.filter('join', { //["1","2","3"] - > '1-2-3' get: function(origin, split ){ return origin.join( split || "-" ); }, // **Important ** // "1"-"2"-"3" - > ["1","2","3"] set: function(dest, split ){ return dest.split( split || "-" ); } }) ``` ```html {array|json} ``` [【 DEMO : two-way filter】](https://codepen.io/leeluolee/pen/jEGJmy) ####内建过滤器 #####json 这是一个双向过滤器 __example__ ```js var component = new Regular({ template: "

{user|json}

" }) component.$update("user|json", "{'name': 'leeluolee', 'age': 10}") //console.log(user) --> {'user':'leeluolee', 'age': 10} ``` __Only Browser that support JSON API can get the json filter__ #####last 获得数组最后一个元素, 这是一个单向过滤器 ```html {[1,2,3]|last} ===> 3 ``` ###Component.event __Usage__ `Component.event(name, factory)` 设置自定义dom事件 __Argument__ |Param|Type|Detail| |--|--|--| |name|String|the custom event name| |factory|Function| Factory function for creating event type| ###Component.animation 自定义一个动画command. animation接口完全是为`r-animation`指令服务的. 查看 [指南: animation](https://regularjs.github.io/guide/zh/animation/README.html) 了解更多 __Usage__ Component.animation(name, factory) __Arguments__ |Param|Type|Detail| |--|--|--| |name|String|the custom animation name| |factory|Function| Factory function for creating command| __Example__ ###Component.component 注册一个组件,使其可以被, 这里类似与在[options](#options)中声明`name` __Usage__ `Component.component(name, factory)` __Arguments__ |Param|Type|Detail| |--|--|--| |name|String|the name used to insert Component in template| |factory| Component | A Component to be register | __Example >__ ```js var Pager = Regular.extend({ // other options }) Component.component('pager', Pager) // you can use pager as nested component Component2 = Component.extend({ template: "" }) ``` ###Component.use __Usage__ `Component.use(factory)` 著名的angular中模块化的解决方案是`angular.module()`和依赖注入, 一个模块可以有factory可以有filter可以有directive等等. 在regular中不可能照搬这种方式, 这是因为 - regular中没有`$rootScope.$digest()`这种全局性的__解药__无脑的促使所有绑定进入数据检查阶段,regular组件的生命周期都是独立的, 这就决定了必须让扩展建立与组件的关系. >比如angular的`$timeout`之类的实现只需在定时器完成后`$rootScope.$digest()`即可进入全局的数据检查, 而regular中[timeout](#timeout)之后必须调用组件的`$update()`才进入组件本身的数据检查阶段,即需建立与组件的关系. - 模块插件应该是与组件无关的, 绑定只应该在被使用时发生, 这样才是可复用的模块插件. 所以一个典型的插件的写法应该是这样的 ```javascript function FooPlugin(Componenet){ Component.implement()// implement method .filter() // define filter .directive() // define directive .event() // define custom event } var YourComponent = Regular.extend(); FooPlugin(YourComponent); // lazy bind FooPlugin(Regular); // lazy bind to globals ``` 为了更统一, 所有Component都有一个`use`函数来统一'使用'插件, 如上例可以写成 ```javascript YourComponent.use(FooPlugin); // global Regular.use(FooPlugin); ``` ##options {#options} `new Component`,`Component.extend`, `Component.implement` 都接受同一种 __options__ 参数 > 没有提及的配置项都会自动成为Component的原型属性( 或实例属性 ) ### template - type: String | AST 模板字符串需要遵循[模板语法](./syntax.html),模板只会在首次实例化时被编译一次 > 你可以选择在构建时通过 `Regular.parse` 将模板先处理成 `AST` 。 ### name 注册组件到父组件的命名空间内,使其可以被声明式调用。 > 注意通过name注册,是全局的 ```js const Component = SuperComponent.extend({ //other options name: 'foo1' }) const Component2 = SuperComponent.extend({ template: "" }) ``` 也可使用[Component.component](#component) 注册, 上例实际上等同于 ```js const Component = SuperComponent.extend({}); Regular.component('foo1', Component) ``` ### events - type: Object 批量定义绑定事件,__这个在需要绑定一些[内置事件](../basic/event.html)时格外有用。 ```javascript Regular.extend({ events: { "$init": function(){ // same in component.init }, "$destroy": function(){ // same in component.destroy } } }) ``` ### data - type: Object __⚠️不要在 extend 或 implement 时定义data属性 !!!__ ,这会导致所有实例共享数据。 > 永远只在 `new Component` 或 `config` 中定义初始化函数 ### computed - Type: Object 计算属性定义为键值对 - key: 表达式名 - value: 表达式定义 __Example__ ```js comuted: { title: } ``` 表达式定义支持几种类型 ## 生命周期钩子 [options](#options) 中还可以定义如下生命周期钩子 ### config( data ) - type: Function 在模板编译 __之前__ 被调用,__config一般是用来初始化参数__,它接受的 `data` 即你在new Component() 时传入的 `data`属性。 ### init() - type: Function 会在模板编译 __之后__( 即DOM结构已产生 )被调用. 你可以在这里处理一些与DOM相关的逻辑  ### destory() - type: Function 如果你需要有额外的回收逻辑, 你可以重写destroy方法 __记得调用`this.supr()`来运行默认的回收逻辑 !!!!__, 否则会回收失败 ```javascript var Component = Regular.extend({ //..... destroy: function(){ this.supr(); // call the super destroy ...other logic } }) var component = new Component(); component.destory(); ``` ### modifyBodyComponent( component, next ) {#modify} ##实例接口 {#instance} component即代表组件实例, 注意这些公有都有`$`前缀 意味不建议进行重写 ###component.$inject {#inject} 插入组件到指定位置 __Usage__ `component.$inject(element[, direction])` __Arguments__ |Param|Type|Detail| |--|--|--| |element|`Node` `false` | 被插入节点,如果传入__false__则代表将此组件从dom中移除| |direction_(optional default:'bottom')_|String| 组件的位置插入目标的位置. 可以是 'top', 'bottom', 'after', or 'before'.| __Example >__ 假设你已经有这样一个组件 ```js var component = new Component({ template: "

{title}

", data: { title : "Example" } }) var div = document.getElementById("#div"); ``` 和一段html片段 ```html
``` - `compnent.$inject( div )` or `component.$inject( div, 'bottom' )` __resulting html__ ```html

Example

``` - `compnent.$inject( div, 'top' )` __resulting html__ ```html

Example

``` - `compnent.$inject( div, 'after' )` __resulting html__ ```html

Example

``` - or `component.$inject( div, 'before' )` __resulting html__ ```html

Example

``` - __`component.$inject( false )`__(假设我们已经调用了以上方法插入了本组件) __ 完全从原插入位置移除它(但是没有销毁,你仍然可以再次$inject它)__ __resulting html__ ```javascript
``` __Tips__ 你通过多次调用`$inject` 将组件有一个位置移动到另外一个位置 ###component.$watch 注册一个监听回调,一旦绑定的表达式的值发生改变,它将会被调用 __Usage__ `component.$watch(expression, callback [, options])` __Arguments__ |Param|Type|Detail| |--|--|--| |expression|Expression|一旦表达式求值发生改变,回调会被触发| |callback(newValue, oldValue)|Function| 回调接受两个参数.
1. newValue: 表达式的新值.
2.oldValue: 表达式的原值| __Return__ watchid [Number]: 监听id,用于方法 [$unwatch](#unwatch) - expression 会在每次脏检查时被调用,并比较之前的值 - 当值与上次求值发生变化的判断依据是严格不相等即`!==`. 一种例外就是当求值为数组时,Regularjs会使用[莱文斯坦距离](https://en.wikipedia.org/wiki/Levenshtein_distance)计算数组差异 ```js component.$watch("user.name", function(newValue, oldValue){ alert("user.name changed from " + oldValue + " to " + newValue) ; }) ``` ###component.$unwatch 利用watchid解绑一个数据监听, 一般来讲你很少会用到它,因为所有regularjs中的数据绑定会被自动回收,除非你想在模板回收之前清除某个绑定. __Usage__ ```js var component = new Regular(); component.$watch('b', function(b){ alert('b watcher 1'); }) var id = component.$watch('b', function(){ alert('b watcher 2'); }) component.$unwatch(id); component.$update('b', 100); // only alert 'watcher 1' ``` ###component.$update `component.$update` is used to synchronize data and view 由于regularjs是基于脏检查,所以当不是由regularjs本身控制的操作(如事件、指令)引起的数据操作,可能需要你手动的去同步data与view的数据. $update方法即帮助将你的data同步到view层. __Usage__ `component.$update([expr] [, value])` 更新某个值,并强制进入digest阶段,即脏检查. __Arguments__ * expr(Optional) [Expression| Function | String] - expression可以有多种参数类型 - String: 此字符串会先被Regular.expression处理为Expression - Expression: 此expression需要有set函数, [查看Expression](../syntax/expression.md) - Object: 多重设值 * value - 设置的值 __Example >__ ```js var component = new Regular({ template: "

{title}

", data: { title: "REGULARJS" } }); //=> log 'REGULARJS' , with no doubt console.log( component.$refs.h2.innerHTML ) component.data.title = "LEELUOLEE"; //=> also log 'REGULARJS', regularjs don't know the value is changed. console.log( component.$refs.h2.innerHTML ) // // force synchronizing data and view component.$update() //=> also 'REGULARJS'. synchronize now. console.log( component.$refs.h2.innerHTML ) // // trigger on-click event component.$refs.h2.click(); // should log leeluolee. // the Expression `title=title.toLowerCase()` is actived. // when listener is done, regularjs will enter digest phase console.log( component.$refs.h2.innerHTML ) // ``` you may need check [$refs](#refs) first Beacuse you may need to set a complex Expression, $update also accept optional params to set the property easily, for Example ```js // 1. simple component.$update("user.name", 'leeluolee') // is equals to component.data.user.name = 'leeluolee' component.$update() // 2. multiple component.$update({ "user.name": "leeluolee", "user.age": 20 }) // is equlas to component.data.user.name = 'leeluolee' component.data.user.age = 20 component.$update() ``` 你当然也可以使用更复杂的表达式,不过你必须保证你的表达式是可设值的, 不过由于会创建表达式,这显然是不高效的,作者强烈建议不怎么做, 除非你需要通过[双向过滤器](#two-way-filter)来设值. ```js // JSON.parse the title first. component.$update('title|json', "{'title': 1}"); console.log(component.data.title) // => {title:1}; ``` > Warning: > 无论传入什么参数,运行$update之后都会进行组件作用域内的dirty-check ###component.$get __Usage__ `component.$get(Expression|String)` 获得一个Expression的值,类似于angular的$eval函数 __Example >__ ```js component.data.username = "leeluolee" component.data.job = "developer" component.$get('username + ":" + job') // => leeluolee:developer ``` __Arguments__ |Param|Type|Detail| |--|--|--| |expression|Expression|String|表达式| ###component.$refs - type: Object 在模板中,你可以使用`ref`属性来标记一个节点或组件. 在实例化后,你可以通过component.$refs 来获取你标记的节点 __Example >__ ```html component = new Regular({ template: " ", init: function(){ this.$refs.input // -> the input tag this.$refs.pager // -> the pager component } }) ``` > The less reference the better ###component.$on Register an `event` handler `fn`. __Usage__ `component.$on(event, fn])` __Arguments__ |Param|Type|Detail| |--|--|--| |eventName| Object String | 事件名| |fn| Function | 监听器回调| 如果你传入一个Object, 会成为一个多重事件绑定 __Example >__ ```js component.$on("hello", fn1) // multiple component.$on({ notify: fn2, message: fn3 }) ``` ###component.$off __Usage__ `component.$off([event] [,fn])` __Arguments__ |Param|Type|Detail| |--|--|--| |eventName| Object String | 事件名| |fn| Function | 监听器回调| - 如果同时传入 event和fn, 则移除指定event类型下的fn函数 - 只传入event, 移除所有event对应的监听器 - 什么都不传,移除所有 ###component.$emit 触发指定事件 __Usage__ `component.$emit(eventName [, args...])` __Arguments__ |Param|Type|Detail| |--|--|--| |eventName| Object String | 事件名| |args| Function | 剩余的参数都会作为参数传入到监听器| __Example >__ ```javascript var component = new Regular(); var clickhandler1 = function(arg1){ console.log('clickhandler1:' + arg1)} var clickhandler2 = function(arg1){ console.log('clickhandler2:' + arg1)} var clickhandler3 = function(arg1){ console.log('clickhandler3:' + arg1)} component.$on('hello', clickhandler1); component.$on('hello', clickhandler2); component.$on({ 'other': clickhandler3 }); component.$emit('hello', 1); // handler1 handler2 trigger component.$off('hello', clickhandler1) // hello: handler1 removed component.$emit('hello', 2); // handler1 handler2 trigger component.$off('hello') // all hello handler removed component.$off() // all component's handler removed component.$emit('other'); ``` ###component.$mute 你可以使用`$mute(true)`让组件失效,使其不参与到脏检查中. 后续使用 $mute(false) 来重新激活一个被失效的组件, 激活的同时,会自动进行一次数据与ui同步. __Usage__ `component.$mute( isMute )` __Argument__ |Param|Type|Detail| |--|--|--| |mute|Boolean|是否disable这个组件(可以后续重启它)| __Example >__ ```js var component = new Regular({ template: '

{title}

', data: { title: "hello" } }) //resulting html

hello

component.$mute(true) // disable it component.data.hello = 'title changed' component.$update(); // resulting html

hello

``` ###component.$bind 创建组件之间的双向绑定. __这已是一个不推荐的方法__. 由于$bind过于灵活的双向绑定,极可能不当使用带来难以维护的对象间关系. 请使用事件通讯来处理组件之间的消息同步。 __Usage__ `component.$bind(component2, expr1[, expr2])` __Arguments__ 1. component2: 要绑定的组件 2. expr1 : 此参数有多种参数类型 - Expression|String: 本组件要绑定的表达式 - Object: 同时绑定多个表达式对 - Array: 表达式列表,同时实现多个同名表达式(即只传入expr1) 3. expr2 : 目标组件要绑定的表达式, 缺省为expr1 >
WARN
> 1. 如果两个表达式都是setable的,可实现双向绑定,否则只能实现单向绑定 > 2. 如果连个组件在bind时是不同步的,component2数据会先同步到component create binding between pager components. ```javascript // insert var pager = new Pager( {data: {total: 100, current:20}} ).$inject('#bind1'); var pager2 = new Pager( {data: {total: 50, current:2}} ).$inject('#bind1'); var pager3 = new Pager({data: {total: 100, current:20} }).$inject('#bind2'); var pager4 = new Pager({data: {total: 50, current:2}}).$inject('#bind2'); var pager5 = new Pager({data: {total: 100, current:2}}).$inject('#bind3'); var pager6 = new Pager({data: {total: 50, current:20}}).$inject('#bind3'); // style 1 pager.$bind(pager2, ['current', 'total']); // style 2 pager3.$bind(pager4, 'current', 'current') pager3.$bind(pager4, 'total') // the same as pager3.$bind(pager4, 'total', 'total') // style 3 pager5.$bind(pager6, {current: "current", total: "total"}); // bind chain var pager = new Pager({data:{total: 1000, current:1}}).$inject('#bind_chain'); for(var i = 0; i < 10; i++){ var pager = new Pager({data:{total: 1000, current:1}}) .$bind(pager, ['total', 'current']) .$inject('#bind_chain'); } ``` [Demo here](https://jsfiddle.net/leeluolee/7wgUf/light/) you may want [the source code of pager ](https://rawgit.com/regularjs/regular/master/example/pager/pager.js) ##其它 ###Regular.dom 由于内部实现需要,Regular实现了部分常用的跨浏览器的dom方法,如果只是简单的dom处理,你可以直接使用Regular.dom. ####Regular.dom.inject(element, refer, direction) `component.$inject` 依赖于此方法 __Arguments__ |Param|Type|Detail| |--|--|--| |element|`Node` `false` | 要被插入的节点| |refer|`Node` `false` | 参考节点| |direction_(optional default:'bottom')_|String| 组件的位置插入目标的位置. 可以是 'top', 'bottom', 'after', or 'before'.| ####Regular.dom.on(element, event, handle) 绑定节点事件, 下列事件对象中的属性已经被修正,你可以在IE6-8使用它们. 回调的this对象也修正为element本身. - event.target - event.which - event.pageX - event.pageY - event.stopPropagation(); - event.preventDefault(); __Example >__ ```javascript var dom = Regular.dom; dom.on(element, 'click', function(ev){ ev.preventDefault(); }) ``` ####Regular.dom.off(node, event, handle) 移除一个事件监听器 ####Regular.dom.addClass(element, className) 添加节点className ####Regular.dom.delClass(element, className) 移除节点的某段className ####Regular.dom.hasClass(element, className) 判断节点是否拥有某个className ```javascript
dom.hasClass(element, 'class1') // => true ``` ####Regular.dom.text(element[, value]) 根据浏览器和节点, 设置节点的textContent 或 innerText ####Regular.dom.html(element[, value]) 设置或获取节点的innerHTML值 ####Regular.dom.attr(element, name [ , value]) 设置或获取节点的指定属性 ###Regular.config 配置一些全局属性, 目前主要可以用来配置模板的自定义开关符号 __Usage__ `Regular.config( settings )` __Arguments__ |Param|Type|Detail| |--|--|--| |settings.BEGIN|String| OPEN_TAG (default: '{')| |settings.END|String| END_TAG (default: '}') | __Example >__ 将默认符号`{}`修改为 `{{}}`. ```javascript Regular.config({ BEGIN: "{{", END: "}}" }) ``` ###Regular.parse {#parse} __Usage__ `Regular.parse(templateString, setting)` 解析模板字符串为AST, 基本上你不会使用此方法, 你可以使用此方法来预解析你得regularjs模板 __Arguments__ |Param|Type|Detail| |--|--|--| |templateString|String| 要解析的模板字符串| |settings.BEGIN|String| 开符号 (default: '{'| |settings.END|String| 关符号 (default: '}')| |settings.stringify|Boolean| 是否stringify 输出的AST (default: false)| __Usage__ __Example >__ ```javascript Regular.parse("

{{page.title + page.desc}}

", { BEGIN: '{{', END: '}}' }) // output [ { "type": "element", "tag": "h2", "attrs": [], "children": [ { "type": "expression", "body": "_d_['page']['title']+'-'+_d_['page']['desc']", "constant": false, "setbody": false } ] } ] ``` ## 服务端渲染