在这一部分中,我们将看到Ember的工作原理,如何使用Ember Data以及如何使用它构建简单的东西。 路由器,路由,模型,模板和存储是Ember的一些概念。 我不会解释其中的每一个,因此,如果您感到困惑,请使用文档 。 与往常一样,您可以在此处下载该部分的代码。
让我们编码
请注意,在使用Ember开发时,最好下载Ember Inspector。 他们发布了带有Chrome扩展程序的Ember,现在该扩展程序也在Firefox上。
对于此示例,我们将JS的每一行都放在/public/static/app.js
。 在实际的项目中,这不是一个好主意。 这简化了我们的示例,但请问自己-您是否曾经在一个大文件中对MVC架构进行过认真的工作? 我们看到了Laravel的工作方式:控制器在一个文件夹中,每个控制器在一个文件中,配置在其自己的文件夹中,模型也在其中。 我建议您在进入适当的项目时对Ember做同样的事情。
启动Ember时要做的第一件事就是创建应用程序。 它是您使用Ember编写的所有代码的全局命名空间。 可以这样创建一个应用程序:
App = Ember.Application.create();
我建议在创建应用程序时仅通过添加一行代码来激活一些调试功能。
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
它的作用不只是通过控制台中的URL和模板输出您的动作。 另外,我们将使用Ember Data,它是Ember的一个独立模块,可与REST很好地集成,将所有内容从存储对象转换为服务器上的请求。 默认情况下,Ember Data使用剩余适配器。 您也可以使用夹具适配器进行测试和本地开发。 基本上,Ember Data是服务器(Rest API)和具有存储对象的本地存储之间的桥梁。
如前所述,我们的API使用命名空间。 Ember的数据带有一个Rest适配器,该适配器接受一个名称空间,这个前缀就像我们在Laravel Route组上看到的一样。 让我们传入名称空间作为参数。
App.ApplicationAdapter = DS.RESTAdapter.extend({
namespace: 'api/v1'
});
适配器现在通过example.com/api/v1/
请求所有数据。
将App Store与适配器链接起来,就可以开始开发了。
App.Store = DS.Store.extend({
adapter: 'App.ApplicationAdapter'
});
Ember的主要概念之一是URL。 一切都围绕着这个想法。 路由器使URL和模板保持同步。 在路由器内部,您可以定义资源并将该资源映射到特定的URL。 在此示例中,我们将仅使用照片资源和用户资源。 随时添加类别资源,并与Ember建立一对多关系。 不要忘记,我们之前曾与Laravel建立了一些关系(一对多和属于),但是我们并没有使用它们。 在Laravel中使用一对多关系很容易,但是我不想让您不知所措。 如果对评论产生了足够的兴趣,我们将在后续帖子中将其与分页一起添加到我们的应用中。
路由器是应定义所有路由的地方。 在这里,我们用它们的URL定义了两个资源。 URL在这里是可选的。 :photo_id
是一个参数。 假设我们导航到example.com/photo/2
。 会发生什么? 我们有一个资源可以将我们的请求传递给模型或控制器,并在那里从商店中获取一些数据。 如果商店找不到,它将在服务器上查找。 :photo_id
可用于检索此数据。 在这种情况下,它将查找example.com/api/v1/photos/2
。 您会看到照片是复数的。 Ember本身会寻找资源的复数形式。
App.Router.map(function() {
this.resource('photo', {path: "/photo/:photo_id"});
this.resource('user', {path: "/user/:user_id"});
});
路由以资源的首字母大写开头,并且应位于App命名空间中。 另外,在资源名称后添加“ Route”一词。 因此,对于照片资源,路线应如下所示: App.PhotoRoute
它还应该扩展Route对象。
App.PhotoRoute = Ember.Route.extend({});
路由对象可以具有用于不同事物的不同钩子。 这些挂钩中的两个用于定义该资源的控制器名称和定义模型。 让我们坚持使用模型。
App.PhotoRoute = Ember.Route.extend({
model: function(params){
return this.store.find('photo', params.photo_id);
}
});
在内部,我们指定了模型挂钩并传递了一个参数。 该参数在哪里? 图片资源的网址带有参数: /photo/:photo_id
。 photo_id
存储在params
并且可以在函数内部使用。 不要忘记,每个资源和每条路线都可以访问商店。 Store对象将所有信息保存在其中,并使用Local Storage以获得更好的性能。 这样,它减少了服务器上的请求数量。 这就是为什么使用Ember开发可以加快您的应用程序的速度-最终,用户会更快乐。
通过使用store.find('resource')
您可以从store对象中检索该资源的所有数据。 您也只能检索一行。 例如,如果您只想接收具有给定id的照片,请使用store对象并找到具有给定id的照片资源作为第二个参数。
return this.store.find('photo', params.photo_id);
灰烬在example.com/api/v1/photo_id
搜索数据。 默认情况下,Ember通过查找ID来处理数据。 如果您为此资源插入了一些关系,则还可以检索与其关联的数据。 这就是路线的所有代码,每种情况都非常相似且简单明了:
App.IndexRoute = Ember.Route.extend({
model: function(){
return this.store.find('photo');
}
});
App.PhotoRoute = Ember.Route.extend({
model: function(params){
return this.store.find('photo', params.photo_id);
}
});
App.UserRoute = Ember.Route.extend({
model: function(params){
return this.store.find('user', params.user_id);
}
});
快速说明:IndexRoute是默认路由,与根URL链接。 从根本上讲,我的意思是example.com/
URL。 还有其他默认路由,例如ApplicationRoute在应用程序启动时执行。
模型对象
在Ember的模型对象中,您可以指定数据及其资源类型。 Ember的一个不错的功能是,当资源的值更改并且另一个值取决于更改后的值时,它会通过观察者的魔法自动更新。 模型应以大写字母开头,并应扩展模型对象。
App.Photo = DS.Model.extend({});
在该对象内,您应该指定所有字段和其他依赖于这些核心值的值。 您也可以在模型内部添加“关系”。
照片模型应如下所示:
var attr = DS.attr; // This cuts my writting. Inside the model i use attr instead of DS.attr
App.Photo = DS.Model.extend({
user_id: attr("number"), // The expected value is a number
url: attr("string"), // The expected value is a string
title: attr("string"),
description: attr("string"),
category: attr("number"),
fullUrl: function(){ // Another value that depends on core values.
return "/files/" + this.get("url");
}.property('url'),
backgroundImage: function(){// This depends on another value but not on core ones
return 'background: url("' + this.get("fullUrl") + '") no-repeat; ';
}.property('fullUrl')
});
使用attr
( DS.attr
),您可以指定如何希望这些数据到达。 例如,我们希望user_id
值是一个数字。 这样,我们可以免受外部数据的攻击。
用户模型相似。 请记住,Ember Data将在/api/v1/users
寻找它。 命名约定有些棘手。 例如,如果您请求一个名为user的资源,Ember Data将查找example.com/prefix/users
;如果您请求一个特定的资源,则它将请求example.com/prefix/users/user_id
。 了解Laravel如何公开数据以及Ember如何希望其数据可以使您免于头痛。
App.User = DS.Model.extend({
name: attr("string"),
lastname: attr("string"),
username: attr("string"),
fullname: function(){
return this.get('name') + " " + this.get('lastname');
}.property("name", "lastname")
});
观看次数
在跳入模板之前,建议您使用Ember Inspector查看应用程序的状态。 在此可以找到路线,视图和控制器。 您还可以找到控制器和路由之间的关系。 花一些时间与Inspector环顾一下,稍后在您开发自己的Ember应用程序时会提供很大的帮助。
您还记得我们在第三部分中编写的第一个模板吗? 那就是应用程序模板。 在浏览器中访问example.com
时,将呈现该模板。
如果未在该模板内进行修改,则无法进一步开发该应用程序。 将<!-- The content will be here -->
替换为<!-- The content will be here -->
注释为: {{outlet}}
。
为什么? 我们所有的资源都嵌套在应用程序路由内。 但是,如果我查看代码,则路由器上看不到索引。 这是为什么?
默认情况下, example.com/
url分配给IndexRoute
除非您已将该URL分配给另一个路由。 Ember默认情况下将应用程序置于顶层,并且所有内容都嵌套在其中。 如果您在该应用程序路由内请求一个URL,则通过使用{{outlet}}
作为占位符,Ember将采用该路由的模板并将其放在该占位符内。
让我们制作另一个模板并将其用于IndexRoute
。 这将是第一页。 第一个模板是应用程序模板。 索引模板将呈现在应用程序的{{outlet}}
。
data-template-name
是data-template-name
。 该脚本标签内的所有代码都将放置在{{outlet}}
。
<script type="text/x-handlebars" data-template-name="index">
<ul class="small-block-grid-1 medium-block-grid-2 large-block-grid-3 custom-grid-ul">
{{#each}}
<li {{bind-attr style="backgroundImage"}}>
<div class="custom-grid">
{{#link-to 'photo' this}}<h5 class="custom-header">{{title}}</h5>{{/link-to}}
<span>Author: {{user_id}}</span>
</div>
</li>
{{/each}}
</ul>
</script>
{{#each}}
就像一个循环。 如果模板的模型有一个数组,并且我们要查询所有数据,则可以使用此特殊标记。 该循环以{{#each}}
开始,以{{/each}}
。 在此循环内,我们使用从循环返回的所有值。 记住,在模型内部我们返回了资源photo
。 该模型从商店中检索数据,并将其返回到模板。 看一下照片模型。 我们在此处指定了一些字段,并且这些模板模板{{#each}}
循环中正在使用这些字段。
另一个特殊标签是{{#link-to}}
标签。 该标签会生成指向照片路线的链接并传递参数。 this
参数是该对象的id
。 在这种情况下,带照片的身份证。 同样, {{#link-to}}
标签以{{/link-to}}
结尾。 {{title}}
不是一个特殊标记,它仅检索该对象的标题值。
让我们添加照片模板。 此模板是“照片路线”的模板。 同样,我建议查看有关如何映射以及如何完成命名的命名约定 。
<script type="text/x-handlebars" data-template-name="photo">
<div style="text-align: center;">
<h4>{{title}}</h4><br>
<img {{bind-attr src="fullUrl" alt="title"}}><br>
<span>Author: {{#link-to 'user' user_id}}{{author.name}}{{/link-to}}</span>
</div>
</script>
通过使用{{attribute-here}}
标签,所选属性将在此标签内生成。 我们在<img>
标签中使用了它。 将标记内的{{title}}
用作属性会导致问题。 把手和Ember在DOM内生成一些额外的对象。 为了解决这个问题,我们改用{{bind-attr}}
。 当我们链接到用户路线时,我们传递一个参数: user_id
。 通过单击链接,URL将使用example.com/user/the_id
进行更新。 但是我们还没有用户模板。 让我们创建一个。
<script type="text/x-handlebars" data-template-name="user">
<h2>Hello: {{fullname}} </h2>
</script>
这仅显示全名。 fullname
是我们的财产App.User
扩展DS.Model
。
在将所有内容打包之前,我对它的外观进行了gif处理:
包起来
如您所见,这尚未完成。 仍然需要大量工作。 继续尝试它,从中学习并改变它。 完整的项目将托管在我的Github帐户上 ,并将经常更新。 欢迎任何贡献,我很乐意共同努力。
在本系列中,我们学到了很多东西-我也学到了很多东西。 我们了解了如何使用云,了解了云的优缺点。 我们了解了如何在两种环境中开发应用程序,以及如何为不同的环境配置Laravel。 我们看到了如何通过与Ember保持在应用程序的同一页面上来使用Laravel构建REST API。 希望大家都和我一样开心。
你怎么看? 您想在Heroku,Laravel或Ember上看到更多吗? 在下面发表评论,总是很高兴听到读者的反馈!
From: https://www.sitepoint.com/single-page-app-laravel-emberjs/