如果认真看了前面的ember101课程,相信已经有了驾驭ember 实现一个真正的应用程序的冲动,这次我们运用我们已经掌握的知识,构建一个完整的、刚好实现的博客程序。参照的书籍是ember实战。
应用程序主要有两个代码文件组成index.html 和 app.js 。实际的应用程序里,把app.js 分解成了几个.js 文件:models.js(模型)、views.js(视图)、controllers.js(控制器)、router(路由) 。其实对于本例不复杂的应用程序,这些文件内容都放在app.js 也没什么不可以,这样看着清晰些吧。
1、先来整体看下index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<title>Ember.js In Action Blog</title>
<link rel="stylesheet" href="css/master.css" type="text/css" charset="utf-8">
<meta name="author" content="Joachim Haagen Skeie">
<script src="js/scripts/jquery-1.10.2.min.js" type="text/javascript" charset="utf-8"></script>
<script src="js/scripts/showdown.js" type="text/javascript" charset="utf-8"></script>
<script src="js/scripts/handlebars-1.0.0.js" type="text/javascript" charset="utf-8"></script>
<script src="js/scripts/ember-1.0.0.js" type="text/javascript" charset="utf-8"></script>
<script src="js/scripts/ember-data-beta-1.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app/app.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app/models.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app/views.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app/controllers.js" type="text/javascript" charset="utf-8"></script>
<script src="js/app/router.js" type="text/javascript" charset="utf-8"></script>
//这里是顶级模版的定义,handlebars模版的linkTo块语句链接Home到博客的索引页面,链接About到关于页面;
浏览器里我们看到的页面,这只是两个选项,主体内容由lutlet引入下面两个二级模版。
<script type="text/x-handlebars" id="application">
<h1>Ember.js in Action Blog</h1>
<div class="headerLinks">
{{#linkTo "blog.index"}}Home{{/linkTo}}<span class="middot">·</span>
{{#linkTo "about"}}About{{/linkTo}}
</div>
{{outlet}}
</script>
//这里是 blog/index 模版的定义,这是个blog的二级模版
<script type="text/x-handlebars" id="blog/index">
{{#each controller}}
<h1>{{postTitle}}</h1>
<div class="postDate">{{formattedDate}}</div>
{{postLongIntro}}<br />
<br />{{#linkTo "blog.post" this}}Full Article ->{{/linkTo}}
<hr class="blogSeperator"/>
{{/each}}
</script>
//这里是 blog/post 模版的定义,这是blog另一个二级模版,用来在上面模版blog/index单击`Full Article -> `后链接对应的博客文章,
这个模板里在点击`>back`后链接到blog/index 博客索引界面。
<script type="text/x-handlebars" id="blog/post">
<div class="postDate">{{formattedDate}}</div>
<br />{{#linkTo "blog.index"}}< back{{/linkTo}}
{{markdown}}
<br />{{#linkTo "blog.index"}}< back{{/linkTo}}
</script>
//这里是 about 模版的定义
<script type="text/x-handlebars" id="about">
<h1>About</h1>
This blog is part of the Ember in Action book.
Sample code for chapter 3.
</script>
</head>
<body>
</body>
</html>
2、再来看看路由router
路由名称---- 对应网址URL
index路由---- 跳转到/posts
blog路由---- 对应 /blog
blog/index子路由---- 对应 /posts
blog/post子路由---- 对应 /psot/:post_id
about路由---- 对应 /about
按照前面ember101的讲解,ember会分别为定义application、index、blog、about 路由建立默认的类(路由、控制器、视图)和模版;拿blog做例子就是建立BlogRoute类、BlogContrller类、BlogView类和模版 blog。
//定义一个blog的路由,这里的`hash`参数是默认值,可以`histore` 区别在于URL的编码格式多了一个#散列符,前者是:#/blog/post/:post_id 后者是/blog/post/:post_id
Blog.Router = Ember.Router.extend({
location: 'hash'
});
Blog.Router.map(function() {
this.route("index", {path: "/"});
this.resource("blog", {path: "/blog"}, function() {
this.route("index", {path: '/posts'});
this.route("post", {path: '/post/:blog_post_id'});
});
this.route("about", {path: "/about"});
});
//index 路由实际就是博客的索引界面,所以没有实质性内容,直接跳转到博客索引界面blog.index
Blog.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('blog.index');
}
});
//定义blog.index子路由数据模型获取所有博客文章
Blog.BlogIndexRoute = Ember.Route.extend({
model: function() {
return this.store.find('blogPost');
}
});
//定义blog.index子路由数据模型获取指定ID的博客文章
Blog.BlogPostRoute = Ember.Route.extend({
model: function(blogPost) {
return this.store.find('blogPost', blogPost.blog_post_id);
}
});
3、看下model 模型里的内容
//定义blog.post模型的数据结构
Blog.BlogPost = DS.Model.extend({
postTitle: DS.attr('string'),
postDate: DS.attr('date'),
postShortIntro: DS.attr('string'),
postLongIntro: DS.attr('string'),
postFilename: DS.attr('string'),
//看到.property就知道这里是一个计算属性,用来把日期格式化,显示成我们比较习惯的格式。
markdown: null,
formattedDate: function() {
if (this.get('postDate')) {
return this.get('postDate').getUTCDay()
+ "/" + (this.get('postDate').getUTCMonth() + 1)
+ "/" + this.get('postDate').getUTCFullYear();
}
return '';
}.property('postDate'),
//这里定义了文章的获取路径/blog/post/id
postFullUrl: function() {
return "/blog/post/" + this.get('id');
}.property('id')
});
4、看下控制器的内容
Blog.ApplicationController = Ember.Controller.extend({});
Blog.BlogIndexController = Ember.ArrayController.extend({});
Blog.BlogPostController = Ember.ObjectController.extend({
content: null,
contentObserver: function() {
console.log('Blog.BlogPostController contentObserver: ' + this.get('content.id'));
if (this.get('content')) {
var page = this.get('content');
$.get("/posts/" + this.get('content.id') + ".md", function(data) {
var converter = new Showdown.converter();
page.set('markdown', new Handlebars.SafeString(converter.makeHtml(data)));
}, "text")
.error(function() {
page.set('markdown', "Unable to find specified page");
//TODO: Navigate to 404 state
});
}
}.observes('content')
});
5、看看app.js里还需要有哪些内容
var Blog = Ember.Application.create({
log: function(message) {
if (window.console) console.log(message);
}
});
Blog.Store = DS.Store.extend({
adapter: DS.RESTAdapter
});
6、简单浏览下view视图的内容
Blog.ApplicationView = Ember.View.extend({
elementId: 'mainArea'
});
Blog.BlogIndexView = Ember.View.extend({
elementId: 'blogsArea'
});
Blog.BlogPostView = Ember.View.extend({
elementId: 'blogPostArea'
});
Blog.AboutView = Ember.View.extend({
elementId: 'aboutArea'
});