2014年10月1日 星期三

Take a peek at EmberJS - Phase1

前言:
時間回到三年前(2011年)...那個時候做Web Application在前端Framework的選擇不像現在多的眼花繚亂,基本上套上jQuery與jQuery UI還有一票jQuery的徒子徒孫就是火力強大的工具了,那個時候也是我第一次使用完整的前端 framework solution。時間沒走多久前端的需求爆炸性的成長,只用jQeury來操作DOM元素已經不敷需求,另外用jQuery來做SPA要手工的地方太多也挺吃力的,所以很多SPA的框架就誕生了。目前最多人擁護的莫過於AngularJS,而後起之秀EmberJS雖然也是有不少擁護者但就台灣的環境來說AngularJS已獲得壓倒性的勝利,一方面是因為AngularJS發展穩定,又有Google這個大公司當靠山,所以網路上的資源比任何SPA Framework都還要多。但我就是比較搞怪一點所以就選EmberJS來看看了XD。目前EmberJS是Open Source的專案host在GitHub上
EmberJS => https://github.com/emberjs/ember.js
EmberJS相對於AngularJS變化比較大所以需要多關注GitHub上面的Issues或是commits,有些時候官網上面的教學文件還落後最新的Stable版本一段差距。

本文:
這篇文章會以官方的Todo教學為實現的功能,功能會到讓這個Todo應用可以塞入新的Todo事項,當中會加上一些我對它的理解。這個EmberJS Application會用到幾個libary,jQuery + Handlebars +Ember + Ember-Data
這其中handlebars至個libary還滿特別的,有用過AngularJS的人一定對他的功能不陌生,它其實就是一個讓我們可以方便建構語意式模版的工具,用法像是以下這樣:
<div class="entry">
  <h1>{{title}}</h1>
  <div class="body">
    {{body}}
  </div>
</div>
這個其實很多框架都有類似的功能(KnockoutJS也有),就看習慣哪一個表達方式的問題了。
另外一個可以抓出來說一下的就是Ember-data這個libary了,其實Emberjs是可以不用這個東西也可以run的很好的,不過這個libary有著Ember遠大的野心,用我的破英文來解讀是這樣的啦~XD
"Ember Data is designed to be agnostic to the underlying persistence mechanism, so it works just as well with JSON APIs over HTTP as it does with streaming WebSockets or local IndexedDB storage.",
"In particular, Ember Data uses Promises/A+-compatible promises from the ground up to manage loading and saving records, so integrating with other JavaScript APIs is easy."這兩段話出自他本身的說明文件,看起來這個libary會是未來Ember收到資料時的預設處理工具吧?

這邊還是用我習慣的ASP.NET MVC來實做,首先依然是開啟一個空白的專案,然後把上述四個會用到的四個Libary包含進去。若有裝ASP.NET Optimization Framework的話可以這樣做:
新增一個ScriptBundleConfig.cs檔於App_Start資料夾中,並於Global.asax.cs中呼叫他的靜態方法
using System.Web.Optimization;

    public class ScriptBundleConfig
    {
        public static void Register(BundleCollection bundleCollection)
        {
            bundleCollection.Add(new ScriptBundle("~/bundle/all").Include(
                "~/Scripts/jquery-2.1.1.js", 
                "~/Scripts/handlebars-v1.3.0.js", 
                "~/Scripts/ember.prod.js",
                "~/Scripts/ember-data.prod.js"));
            // 這個範例的EmberJS版本為1.7
            BundleTable.EnableOptimizations = true;
        }
    }
// addictional lines truncated for brevity

   ScriptBundleConfig.Register(BundleTable.Bundles);

// addictional lines truncated for brevity

而在母版(Views/Shared/_Layout)的.cshtml地方加上這段,這樣就可以把我們所需要的Libary都載入了
@System.Web.Optimization.Scripts.Render("~/bundle/all")

EmberJS屬於前端的MVC框架,而其執行也遵照「naming conventions」這個準則,以下我們會由Todo List這個小功能來驗證這個特性。
我們先在Controller中新增一個Action,Controller的內容相當簡單,這個範例也只有在Client端
public class ToDoController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}
要開始一個EmberJS應用首先我們需要一個起始的js檔案
// new 一個 EmberJS 應用程式的 instance
window.Todos = Ember.Application.create();
上面這個檔案就是宣告一個全域變數叫Todos,把這個檔案include進去我們的Index頁面,這個檔案必須是除了基底libary之外第一個要引用的檔案!
接下來就是Index.cshtml上面的頁面要寫什麼東西呢?
@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
// 模版的名稱會影響到由那個Route渲染這個Template,這個例子則是MyTodosRoute會去渲染這個Template
<script type="text/x-handlebars" data-template-name="MyTodos">

    <section id="todoapp">
        <header id="header">
            <h1>todos</h1>
            {{input type="text" id="new-todo" placeholder="What needs to be done?" 
            value=newTitle action="createTodo"}}
        </header>

        <section id="main">
            <ul id="todo-list">
                {{#each}}
                <li {{bind-attr class="isCompleted:completed"}}>
                    <input type="checkbox" class="toggle" />
                        <label>{{title}}</label><button class="destroy"></button>
                </li>
                {{/each}}
            </ul>

            <input type="checkbox" id="toggle-all">
        </section>
</script>

@section JSSection{
    <script src="~/Scripts/application.js"></script>
}
整塊用script包起來的就是handlebar的作用區域他的type就是「text/x-handlebars」,EmberJS其實本身是可以不需要用這個libary就可以運作的,不過官方推薦搭配這個libary來讓UI更有表達能力,因為Handlebars Template本身就是for UI的Html-like的DSL(特定領域語言),看起來是挺直覺的。
接著就是新增一個route.js檔案來告訴Ember.js要怎麼去render畫面以及這個畫面所需要用到的Model是哪一個,另外根據nameing convention也要有個同名的controller對應到它
Todos.Router.map(function () {
    // Ember會去偵測當Uri為'/'時去render名稱為「todos」的Template
    this.resource('MyTodos', { path: '/' });
});

Todos.MyTodosRoute = Ember.Route.extend({
    //指示這個Route的Model要掛載哪一個Model進來
    model: function () {
        return this.store.find('MyTodo');
    }
});
當建好這隻Route後,我們接著就是要生一個Model出來讓Template render時有東西可以render。follow Ember的naming convention準則,我們的Model也取名叫做MyTodo,不過Model這邊倒是不強制一定要這樣命名
Todos.MyTodo = DS.Model.extend({
    title: DS.attr('string'),
    isCompleted: DS.attr('boolean')
});

// 這個是DS所提供的feature, Fixture data,要啟用這樣的功能我們需要回過頭在application.js裡面做些設定
Todos.MyTodo.FIXTURES = [
 {
     id: 1,
     title: 'Learn Ember.js',
     isCompleted: true
 },
 {
     id: 2,
     title: 'Benjamin Has something to do',
     isCompleted: false
 },
 {
     id: 3,
     title: 'Profit!',
     isCompleted: false
 }
];

//補上以下的程式碼

// 使用 Fixure 資料, 這可以讓DS把資料存取由memory來,這個設定通常是用在開發與測試
// DS Model 是設計用來做為EmberJS的persistence機制
Todos.ApplicationAdapter = DS.FixtureAdapter.extend();
到目前為止,我們已經把頁面上面的靜態資料都實做完成了,若現在run這個web site的話就可以看到browser原先都看不懂的東西現在都已經被我們所輸入的靜態資料填滿,但若想要跟這個Todo互動的話我們還缺少controller的幫忙。
依據Ember的naming convention我們新增一個MyTodosController檔案
Todos.MyTodosController = Ember.ArrayController.extend({
    // 行為(方法)必須要包在actions這個物件中,這也是Ember的一個naming conventioon
    actions: {
        createTodo: function () {
            // 由newTitle取得Title的值
            // 取Template中元素的Value值
            // 這裡的get方法是由Ember提供
            var title = this.get('newTitle');
            if (!title) { return false; }
            if (!title.trim()) { return; }

            // 產生一個新的Todo Model
            var todo = this.store.createRecord('MyTodo', {
                title: title,
                isCompleted: false
            });

            // 把newTitle的值清空
            this.set('newTitle', '');

            // Save the new model
            todo.save();
        }
    }
});
好了~現在再run一次web site,我們現在就可以新增Todo清單了!不過刪除的行為我們還沒有實做,所以目前也只能新增 XD
不過寫到這邊也符合目前第一階段的目標了。
EmberJS雖然不像AngularJS受大眾寵愛,但也是一個備受期待的MVC框架,不過這個框架的教學資源與學習文件確實不像AngularJS這麼豐富,我想這大概也是目前環境還很少人投入這個框架的原因之一吧~
不過學習東西嘛~快樂最重要 XD

之後應該還會有後續的階段,除了按照官方的文件寫之外也會加上實做上的一些心得

Reference:
http://ember.vicramon.com/chapters
http://emberjs.com/guides/getting-started/
http://www.emberjs.cn/guides/getting-started/ => 簡體中文版,這個版本會稍舊一點,不過若真的是英文苦手可以看一下這個版本