Emberjs 中component 的创建以及本地 使用 ember-cli-mirage 来mock数据
在实际的项目中,我们总会重复写一些代码来实现一些相同的功能,比如说购物车列表内的每一项,比如说租房列表中的每一项。它们的样式都一样,我们也不可能为每一项复制一遍。这个时候我们就可以使用
handlebars
的each as
以及emberjs
的component
来写出简洁的代码。而在 emberjs 中我们还可以使用ember-cli-mirage
来mock本地数据,从而可以在后期更快地与后端对接数据。
模拟场景
我们以 链家 再售的二手房列表为例:
分析:
可以看到每一个 房产(条目) 都具有相同的结构,分为左侧的展示图片,右侧的标题以及其他具体信息等。
具体操作
使用emberjs 框架开始创建一个组件,并mock本地数据(使用在 EmberGuide 教程中的项目)。
创建基本组件:
ember g component lj-ershoufang
可以看到 ember-cli 帮我们生成了两个文件,一个是主管组件展示的template.hbs
,另一个是主管组件逻辑的component.js
。 1
组件的数据传递
在一个简单的组件中:
{{!-- component-train-passing-properties/template.hbs --}}
<h2>{{title}}</h2>
我们可以在 component.js
中为 title
变量赋值,在 handlebars 文件中将会展示出来:
// component-train-passing-properties/component.js
// ...
this.set('title','welcome to use emberjs');
而在调用组件页面我们将会看到这句话。
但是在实际项目中,我们基本上都是从后端接受数据再通过路由中的handlebars 文件调用组件,将数据传递进去,再进行相应的展示:
{{!-- train/template.hbs --}}
{{component-train-passing-properties title="Welcome to use component!"}}
这个时候,在组件的调用页面,我们就可以看到
展示在我们面前了。
使用 flex 布局 书写相关代码。
要注意 flex 布局的兼容性问题。(待补充)
自定义组件属性:
- 修改包裹体的标签:
默认的 组件 外部的包裹元素为div
,我们可以通过使用tagName: 'li'
这种语法来修改包裹体的标签; - 向组件添加 类 名:
有以下几种方法:
- 在定义的时候通过属性添加:classNames: ['header-title']
;
- 在调用的时候通过属性传递:{{component-train-passing-properties class='passing-properties-container'}}
;
- 绑定某一属性,动态添加类:
// component-train-passing-properties/component.js
classNameBindings: ["showBg:show-background"]
而后在对应的 `styles.scss`文件内写上样式,这里是添加背景色。
在调用端:
{{!-- train/template.hbs --}}
<section local-class="defined-class">
{{component-train-passing-properties title=title showBg=true}}
{{component-train-passing-properties title=title showBg=false}}
</section>
可以看到:
如果classNameBindings
绑定的属性的值是一个字符串,那字符串将直接传递入组件的类中。
模拟相关数据。
详细代码
在新创建的展示路由中howGenerateComponent
中,添加展示区域:
<h1>how to Generate a component</h1>
<section local-class="ershoufang-list-container">
<ul>
{{#each model as |item|}}
{{lj-ershoufang data=item}}
{{/each}}
</ul>
</section>
而在路由的route.js
文件中,我们使用最原始的生成数据方式:
import Route from "@ember/routing/route";
export default Route.extend({
model() {
return [{
img: "https://image1.ljcdn.com/110000-inspection/test-5cca6d6b-1419-46aa-9938-b03dcb90602b.jpeg.296x216.jpg",
title: "南北通透两居室 2010年建成 板楼有电梯",
address: "鸿业兴园二区",
houseInfo: ["鸿业兴园二区 ", "2室1厅", "88.14平米", "南 北", "简装"],
positionInfo: ["高楼层(共18层)", "2009年建板塔结合", "玉泉营"],
followInfo: ["1135人关注", "33次带看"],
tag: ["房本满五年", "随时看房"],
totalPrice: 460,
perPrice: 52190
},
{
img: "https://image1.ljcdn.com/110000-inspection/test-dabcf77d-5c2c-4601-b528-e557a12deff9.jpeg.296x216.jpg",
title: "靠近地铁8号线,小区中间位置,中间楼层,已经满五年",
address: "和谐家园一区 ",
houseInfo: ["和谐家园一区 ", "3室2厅", "124.08平米", "南 北", "简装", "无电梯"],
positionInfo: ["低楼层(共6层)", "2006年建板楼", "回龙观"],
followInfo: ["1630人关注", "42次带看"],
tag: ["近地铁", "房本满五年", "随时看房"],
totalPrice: 540,
perPrice: 43521
}
];
}
});
样式文件就不写了。
这时候可以看到我们使用了 each as
的 handlebars 语法,遍历的数组是 model hook 中 return 出来数组。将遍历出来的数组的每一项传入了 lj-ershoufang
这个组件中。
在组件中我们可以看到:
<img local-class="ershoufang-info info-img" src={{data.img}} alt="data.title">
<div local-class="ershoufang-info info-detail">
<p local-class="detail-item title">{{data.title}}</p>
<p local-class="detail-item house-info">{{#each data.houseInfo as |hinfo|}} {{hinfo}}<span local-class="divide">/</span> {{/each}}</p>
<p local-class="detail-item position-info">{{#each data.positionInfo as |pinfo|}} {{pinfo}} <span local-class="divide">/</span> {{/each}}</p>
<p local-class="detail-item follow-info">{{#each data.followInfo as |finfo|}} {{finfo}} <span local-class="divide">/</span> {{/each}}</p>
<p local-class="detail-item tag-info">{{#each data.tag as |tag|}} <span> {{tag}} </span> {{/each}}</p>
<div local-class="price-info">
<p local-class="total-price"><span>{{data.totalPrice}}</span> 万</p>
<p local-class="per-price"> <span>单价</span> {{data.perPrice}} <span>元/平米</span> </p>
</div>
</div>
可以看到,具体使用传来的数据的时候就使用data.img
之类的方式。也就是 javascript 中从对象中取值的方式。
而component.js
也支持一些属性来让我们自定义一些组件的样式等:
import Component from "@ember/component";
export default Component.extend({
localClassNames: "lj-ershoufang",
classNames: ["ershoufang-list"],
tagName: "li"
});
tagName
是自定义组件最外层的包裹体的tag的名称;classNames
是向组件添加类名;localClassNames
是向组件添加 css-modules 的类。
现在可见的页面为:
使用 ember-cli-mirage
虚拟数据
在实际的生产中前端总是在向后端请求数据,当数据返回后,前端将接收到的数据展示出来:
// mirage/config.js
export default function () {
this.namespace = "/api";
this.get("/ershoufangs", function () {
return {
data: [
{
type: "ershoufang",
id: "ershoufang01",
attributes: {
img: "https://image1.ljcdn.com/110000-inspection/test-5cca6d6b-1419-46aa-9938-b03dcb90602b.jpeg.296x216.jpg",
title: "南北通透两居室 2010年建成 板楼有电梯",
address: "鸿业兴园二区",
houseinfo: ["鸿业兴园二区 ", "2室1厅", "88.14平米", "南 北", "简装"],
positioninfo: ["高楼层(共18层)", "2009年建板塔结合", "玉泉营"],
followinfo: ["1135人关注", "33次带看"],
tag: ["房本满五年", "随时看房"],
totalprice: 460,
perprice: 52190
}
},
{
type: "ershoufang",
id: "ershoufang02",
attributes: {
img: "https://image1.ljcdn.com/110000-inspection/test-dabcf77d-5c2c-4601-b528-e557a12deff9.jpeg.296x216.jpg",
title: "靠近地铁8号线,小区中间位置,中间楼层,已经满五年",
address: "和谐家园一区 ",
houseinfo: ["和谐家园一区 ", "3室2厅", "124.08平米", "南 北", "简装", "无电梯"],
positioninfo: ["低楼层(共6层)", "2006年建板楼", "回龙观"],
followinfo: ["1630人关注", "42次带看"],
tag: ["近地铁", "房本满五年", "随时看房"],
totalprice: 540,
perprice: 43521
}
}
]
};
});
}
可以看到数据放在了ershoufangs
的请求链接下,数据内的type
就代表着 emberjs 中的 model 实例,生成 model 实例的方式为:
ember g model ershoufang
在生成的 ershoufang/model.js
文件下,
import DS from "ember-data";
export default DS.Model.extend({
img: DS.attr("string"),
title: DS.attr("string"),
address: DS.attr("string"),
houseinfo: DS.attr(),
positioninfo: DS.attr(),
followinfo: DS.attr(),
tag: DS.attr(),
totalprice: DS.attr("number"),
perprice: DS.attr("number")
});
而在我们路由下的 route.js
将改为:
import Route from "@ember/routing/route";
export default Route.extend({
model() {
return this.store.findAll("ershoufang");
}
});
数据就可以正常的显示了。
Written by Frank Wang.
有问题可以下方评论,或发邮件至 法研鲁迅 .
在使用原生的目录结构时,生成的文件名称可能有所不同;另外 组件的名称也必须有中划线。 ↩︎