Ember component

component顾名思义,是ember中的组件。其特性为可以重复利用。component也是由两部分组成,一个handlebars的模板文件以及一个js文件。其中模板文件控制如何渲染,js文件控制它的行为。

组件的定义

类似的,定义component,也可以使用ember-cli的命令行工具:
ember g component <your-componetn-name>

这样就能在你定义的目录下,创建一个componentcomponent是可以重复使用的,意味着,我们可以在其他的component或者是route中使用它,使用时很简单,使用component helper就好。
我们在此举例:(1)创建一个名字为component-demo的component,然后在模板文件中写下如下的内容:

//app/templates/components/component-demo.hbs
<div>this is a component start</div>
{{yield}}
<div>this is a component end</div>

下面尝试在component-parent中进行调用。

//app/templates/components/component-parent.hbs
{{#component-demo}}
    <div>add content to the component</div>
{{/component-demo}}

这样,就可以将<div>add content to the component</div>渲染到{{yield}}中去(这个也叫作’包裹内容’)。

传递参数

我们都知道,如果没有数据的话,就无法正常渲染,同理,组件也需要进行传参,这就要求组件必须要接受参数、返回参数的能力。

接受参数

component接受参数十分简单,只需要在helper中进行传入即可。
举例而言,我们有一个名为blog-list的component,其要做的事情,是渲染所有的博客列表,其中博客列表以一个array的形式传入:

//app/templates/components/blog-list.hbs
<div>this is the application start</div>
{{#each blogs as |blog|}}
    title:{{blog.title}}
    content:{{blog.content}}
{{/each}}
<div>this is the application end</div>

然后在调用的地方,我们可以在一个routeblog-route中这么定义:

//app/templates/blog-route.hbs
{{blog-list blogs=model}}
//app/routes/blog-route.js
export default Ember.Route.extend({
    model(){
        return [
            {
                title:'blog1',
                content:'content1'
            },
            {
                title:'blog2',
                content:'content2'
            }           
        ]
    }
});

这样,就可以渲染出如下页面:
这里写图片描述

这也就实现了数据的传入。
除此以外,还可以通过入参位置进行指定。在此不作详细探讨,而且不推荐那样使用,可以参考:[ember component](https://guides.emberjs.com/v2.11.0/components/passing-properties-to-a-component/

传出参数

除了给component传入参数之外,我们往往也有需求,就是将component的数据向上传递。

首先,我们要确定的是,什么情况下,数据需要向上传递。一般情况下,数据是双向绑定的,这意味着,如果我们只是修改传入的数据的话,那么我们就无需去传出,因为你直接使用传入的参数的就可以。

那么什么时候需要传出呢?

方案1

我们还是以上述的component为例,其中component中js如下:

export default Ember.Component.extend({
    modelLength:Ember.computed('model',function(){
        return model.length;
    });
});

这个length返回传入参数的长度,我们先不考虑这个绣球有没有这个需要,只考虑如何将这个length调用他的route或者component中去。

方法是有的,首先介绍一下一种方式,就是以事件的方式向上冒泡,这个方法使用是有一定的条件的,就是必须要在component中定义一个事件,如click、hover、change等,主要就是调用sendAction()方法,还是以刚才的component为例:

//app/templates/components/blog-list.hbs
<div>this is the application start</div>
{{#each blogs as |blog|}}
    title:{{blog.title}}
    content:{{blog.content}}
{{/each}}

<button type="button" {{action 'submitAction' }}>提交</button>
<div>this is the application end</div>

其中js代码为:

export default Ember.Component.extend({
    actions:{
        submitAction(){
            let len = this.get('modelLength');
            this.sendAction('submitLen',len);
        }
    }
});

其中在route中调用component的代码:

{{blog-list submitLen='routeAction'}}

其中在route中获得传参的代码:

export default Ember.Component.extend({
    actions:{
        routeAction(param){
            alert(param);
        }
    }
});

这样,就可以进行参数的传递,但是很明显,这种方式,需要有事件才能进行传递,如果事件没有触发,那么就无法获取到component中的参数。不过,正是由于Ember中数据是双向绑定的,所以说,这种情况下,基本已经满足大部分的需求。但是还是有一部分情况无法满足。

方案2:使用service

这种时候,就需要动一些脑筋。一般情况下,使用全局的变量来进行数据的保存,是比较正常的做法。

说道全局的概念,在ember中,有一个service的概念,是在初始化后,就是全局的,可以利用这个进行全局变量的保存。

这里先定义个service,同样是使用ember-cli来进行创建一个名为blog-service的serivce:ember g service blog-service。service会在app/services目录下创建一个blog-service.js文件,现在我们在service中定义一个全局变量以及一个全局方法:

//app/services/blog-services.js
export default Ember.Service.extend({
    data:[],
    addData(data){
        this.get('data').pushObject(data);
    },
    getData(){
        return this.get('data');
    }
});

在component中调用service中的方法:

export default Ember.Component.extend({
    blogService:Ember.inject.service('blog-service'),//注入service,有点类似于依赖注入,是单例的。
    actions:{
        routeAction(){
            this.get('blogService').addData(123);
            this.sendAction('routeAction');
        }
    }
});

在route中进行调用:

export default Ember.Route.extend({
    blogService:Ember.inject.service('blog-service'),
    actions:{
        routeAction(){
            alert(this.get('blogService').getData());
        }
    }
});

这样,route中就可以拿到这个component放到service中的变量。当然,service还有更加灵活的用法,只要注意是全局的就好。

一般情况下,我们不建议使用这种方式,因为这样的话,component的复用就会成问题,因为每次component调用都是创建新的实例,但是service是单例的,所以,变量也是全局的,不管你如何控制,可能会破坏component的独立性。此外,如果事件传递的方式无法解决问题的话,很大程度上是因为compoent设计不合理导致的,一般情况下,能够通过数据双向绑定解决的,那么就不要用其他的方式。

这里可以考虑一个场景,一个route循环调用一个component10次,那么service就没法很好的管理component向其中插入的变量。但是,如果你在使用的时候,每次最多只实例化一个component,那么就没有问题。

定制component

在实际的渲染过程中,我们会发现,默认情况下,ember会给component外面加上一个div元素(如下所示),这个有时会给我带来麻烦。典型的情况,就是如果我们想要创建一个行内元素的componet,类似于input、img等,那么就没有办法很好的创建。

//ember 渲染出的元素
<div id="ember180" class="ember-view">
  <h1>My Component</h1>
</div>

ember在这里提供一个解决方案,可以定制你自己的compoent。

替换元素名、类

例如,默认component的最外层元素为div元素,如果我们想使其变为nav,我们可以在component的js文件中进行修改,并且指定classprimary my-class-name,那么可以如下完善:

export default Ember.Component.extend({  
    // 使用tabName属性指定渲染之后HTML标签
    // 注意属性的值必须是标准的HTML标签名
    tagName: 'nav',
    classNames: ['primary', 'my-class-name']  //指定包裹元素的CSS类
});

替换元素属性

如果想要增加元素的属性,例如我们想要添加一个href属性或者是name属性,那么可以如下操作:

export default Ember.Component.extend({  
    attributeBindings: ['href','name'],
    href: 'www.baidu.com',
    name: 'linking'
});

处理事件

component可以处理丰富的事件,其中包含的事件,详细可以看官方资料。下面简要介绍一下如何处理事件。

component级别的处理

如果component的处理,例如鼠标时间,ember中支持

  • mouseDown
  • mouseUp
  • contextMenu
  • click
  • doubleClick
  • mouseMove
  • focusIn
  • focusOut
  • mouseEnter
  • mouseLeave

如果是在component的最外层进行调用事件,以click为例,那么js文件如下:

//app/components/your-component.js
export default Ember.Component.extend({  
    click(event){
        alert($(event.target).html())
    }
});

模板文件为:

//app/templates/components/your-component.js
{{yield}}
<div>content1</div>
<div>content2</div>

route中调用如下:

{{#your-component}}
    <div>route-content</div>
{{/your-component}}

那么分别点击content1 content2 route-content,页面展示为
这里写图片描述
这里写图片描述
这里写图片描述

如果说点击最外层父元素的话,展示为:
这里写图片描述

总之,其他的也是一样的。

组件内元素事件

如果组件内某一个元素,例如一个按钮,需要添加事件,那么需要如何操作呢?以your-component为例:

//app/templates/components/your-component.hbs
{{yield}}
<button type="button" name="button"></button>

在这个button的基础上,我们分别给他添加上click, mouseEnter, mouseLever事件。

click事件
//app/templates/components/your-component.hbs
{{yield}}
<button type="button" name="button" {{action 'clickButton'}}>button</button>
//等同于:<button type="button" name="button" {{action 'clickButton' on='click'}}>button</button>

js代码为:

//app/components/your-component.js
export default Ember.Component.extend({
    actions:{
        clickButton(){
            alert('click event');
        }
    }
});

mouseEnter、mouseLeaver

//app/templates/components/your-component.hbs
{{yield}}
<button type="button" name="button" {{action 'enterButton' 'enter' on='mouseEnter'}} {{action 'leaveButton' 'leaver' on='mouseLeave'}}>button</button>

js代码为:

//app/components/your-component.js
export default Ember.Component.extend({
    actions:{
        enterButton(){
            console.log('enterButton');
        },
        leaveButton(){
            console.log('leaveButton');
        }
    }
});

一个元素上是可以绑定多个action的,默认的事件为click事件。

给action传递参数

click为例

//app/templates/components/your-component.hbs
{{yield}}
<button type="button" name="button" {{action 'clickButton' '1' on='click'}}>button</button>

js代码为:

//app/components/your-component.js
export default Ember.Component.extend({
    actions:{
        clickButton(params){
            alert(params);
        }
    }
});

这里的params就是1,注意参数只能传递一个,不能传递多个,且顺序不能乱了。

冒泡事件

如果想要向上冒泡事件,可以使用sendAction()这个方法。
click为例

//app/templates/components/your-component.hbs
{{yield}}
<button type="button" name="button" {{action 'clickButton' '1' on='click'}}>button</button>

js代码为:

//app/components/your-component.js
export default Ember.Component.extend({
    actions:{
        clickButton(params){
            this.sendAction('clickButtonComponent',params);
        }
    }
});

父route的hbs:

{{your-component clickButtonComponent='clickButtonRoute'}}

父route的js:

//app/routes/your-route.js
export default Ember.Route.extend({
    actions:{
        clickButtonRoute(params){
            alert(params);
        }
    }
});

这样,当点击按钮的时候,就可以弹出弹框了。

最后需要注意一下,如果事件与component的事件重叠了,那么处理的顺序为,先处理自己的,在处理component级别的。

component的生命周期

在component中,我们免不了要接触到component的生命周期,理解component的生命周期,会使我们更加的理解如何更好的使用ember。

(1)初始化时

  • init
  • didReceiveAttrs
  • willRender
  • didInsertElement
  • didRender

(2) 重新渲染时(rerender)

  • didUpdateAttrs
  • didReceiveAttrs
  • willUpdate
  • willRender
  • didUpdate
  • didRender

(3)销毁时

  • willDestroyElement
  • willClearRender
  • didDestroyElement

我们一般不太关注销毁的内容,主要关注初始化和重新渲染的时候。

我们首先要知道,什么时候会初始化,什么时候会重新渲染。

初始化

初始化的顺序为:

  • init :负责初始化内容
  • didReceiveAttrs:负责格式化要展示的参数
  • willRender:负责渲染前的动作
  • didInsertElement:三方包的调用
  • didRender:在已经渲染好的页面上新增元素

其中didReceiveAttrswillRenderdidRender,在复写逻辑的时候,需要格外小心,因为在重新渲染的时候也有用到这个方法。

注意,如果你在willRender之后,修改了页面中要渲染的数据,那么,在执行完didRender后,就会进入重新渲染流程

重新渲染顺序

  • didUpdateAttrs:负责更新参数
  • didReceiveAttrs:负责格式化要展示的参数
  • willUpdate:数据更新前的动作
  • willRender:页面渲染前的动作
  • didUpdate:更新三方包的元素
  • didRender:在已经渲染好的页面上新增元素

这里需要注意,如果在didUpdate或者didRender里面增加数据更新的操作,那么会进入死循环!!!所以,你的操作,尽量要与方法的含义一致!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值