2014年12月16日 星期二

Run Asp.Net vNext on Mac

前言:
.NET已經OpenSource了!但我相信這個消息早就不是新聞了,國外也有很多人分享了如何在很潮的Mac上寫.NET應用。只是英文的教學很多但是中文的卻很少,趁著機會趕緊分享一下自己在Mac上面安裝.NET開發相關環境的心得。

內文:
由於ASP.NET已經正式OpenSource了,原始碼都放在GitHub上而且官方教學也是放那裡,因此我們就從官方的教學來一步步完成吧。
官方連結:https://github.com/aspnet/home#getting-started

1)安裝KVM
首先,要去安裝KVM(K Version Manager)這是一個幫助管理KRE(K Runtime Environment)的工具,我們可以在同一時間安裝不同版本的KRE並且指定目前預設的KRE是哪一套。KVM我們可以用HomeBrew安裝,因此安裝方式超簡單。
下以下command把這個GitHub Repos加入HomeBrew的追蹤裡。

brew tap aspnet/k

接著run以下的command安裝KVM

brew install kvm

接下來依照指示在bash_profile中加入source kvm.sh
vim .bash_profile

2)安裝KRE
裝好KVM後執行upgrade
kvm upgrade
這個指令可以幫助我們下載KRE並且設為預設,現在最基本的環境這樣就架好了!
當然,工欲善其事,必先利其器 接下來我們就是要把開發環境的好用工具都裝上才行

3)下載Sublime Text 3
http://www.sublimetext.com/3

4)安裝Kulture
在Sublime Text中使用package Controller安裝package

5)安裝OmniSharp
在Sublime Text中使用package controller安裝package
這個套件強烈建議安裝,這是for intellisence用的,裝上去之後就會有智慧提醒,不用記長死人不償命的function name那些東西了~ XD

6)下載.NET官方提供的Demo project
切到你的工作目錄下並下指令
git clone https://github.com/shirhatti/Home.git
檔案其實不大,所以很快就會抓完了XD

下載完畢後,它的結構大概是Home資料夾底下有samples資料夾,samples裡面有有三個資料夾,我們先切到ConsoleApp這個資料夾底下並下command
kpm restore
這個指令會幫我們把專案相依的NuGet package給裝起來,由於這個console app所以我們直接下command就可以看到執行結果
k run
這個指令一執行後Terminal就會顯示出那個讓人熟悉又感動的Hello World!

console app是相對簡單的,比較不一樣的是web app,官方提供的samples有兩個web,我們就拿MVC來做例子吧~
一樣我們切到HelloMvc目錄下並把package裝上
這個web專案我們就換用sublime Text示範
用sublime Text開啟專案目錄,並且下 shift+command+p
輸入"k"找到Run k commands,選擇k kestrel這個指令
預設會run在port 5004上(若是用kesterl run的話),設定可以去看project.json的內容裡面都有寫。
選擇k kestrel後會跑一個console去run web server,這個時候去瀏覽器瀏覽http://localhost:5004就可以看到新版的Mvc的預設樣式囉~!

其實.NET寫了幾年了,從沒想過微軟也會走向如此開放的地步,終於可以不用在Linux或Mac上用virtualbox安裝windows來寫.NET應用了XD
還滿感動的 XD

2014年12月2日 星期二

Take a peek at EmberJS - Phase3

前言:
接續上次的文章,我們這次要把要一步一步把功能給實做完成~!
第一階段
第二階段

內文:
延續上次的文章,我們已經完成Todo應用的CUD(新增、修改、刪除),接下來我們要做的便是可以讓使用者filter已經完成的Todo,哪些又是尚未完成的Todo。因此這邊我們需要把我們的Template分成數個子Template並利用框架的Route來做到讓這些事情在同一頁完成。
首先我們先把列表頁的內容抽出來變成一個新的Template
<!-- data-template-name內的值就相當於是一個路由,index是讓這個Template顯示的路由值,裡面的資料來源從哪裡來則從route去設定 -->
<script type="text/x-handlebars" data-template-name="MyTodos/index">
    <ul id="todo-list">
        {{#each itemController="MyTodoList"}}
        <li {{bind-attr class="isCompleted:completed isEditing:editing" }}>
            {{#if isEditing}}
            {{input class="edit" value=title focus-out="acceptChanges" insert-newline="acceptChanges"}}
            {{else}}
            {{input type="checkbox" checked=isItemCompleted class="toggle"}}
            <label {{action "editTodo" on="doubleClick" }}>{{title}}</label><button {{action "removeTodo" }} class="destroy"></button>
            {{/if}}
        </li>
        {{/each}}
    </ul>
</script>
而原本的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=myTitle1 action="createTodo"}}
        </header>

        <section id="main">
            {{outlet}}
            <input type="checkbox" id="toggle-all">
        </section>
</script>
<!--忽略很多-->
Handlebars helper "{{outlet}}" 是用來幫助我們宣告一個區域,當我們改變route時可以render出指定route的畫面,而第一個child route就會是填滿這個區塊的route。
改完View之後我們要新增一個route.js檔案,在裡面撰寫route相關的邏輯。
Todos.Router.map(function () {
    
    // 當Uri為'/'時去render名稱為「MyTodos」的Template
    this.resource('MyTodos', { path: '/' }, function () {
        //額外的child routes
    });
});

Todos.MyTodosRoute = Ember.Route.extend({
    // 表明這個route要顯示的model是那個
    model: function () {
        return this.store.find('MyTodo');
    }
});

// EmberJS的controller預設的route名稱就是Index,因此若直接存取預設的controller那麼根據框架就會去render Index的內容
Todos.MyTodosIndexRoute = Ember.Route.extend({
    // 這邊表示index的model就是MyTodos
    model: function () {
        return this.modelFor('MyTodos');
    }
});

現在重新刷一次頁面應該就可以看到我們的清單顯示於頁面中。不過我們還要這個Todo應用可以切換以做完、未做完的顯示。首先還是來調整一下頁面。
<script type="text/x-handlebars" data-template-name="MyTodos">
<!--忽略很多-->
        <footer id="footer">
            <span id="todo-count">
                <strong>{{remaining}}</strong> {{inflection}} left
            </span>
            <ul id="filters">
                <li>
                    {{#link-to "MyTodos.index" activeClass="selected"}}All{{/link-to}}
                </li>
                <li>
                    {{#link-to "MyTodos.active" activeClass="selected"}}Active{{/link-to}}<--會打到active這個route>
                </li>
                <li>
                    {{#link-to "MyTodos.completed" activeClass="selected"}}Completed{{/link-to}}
                </li>
            </ul>
<!--忽略很多-->
</script>
接著調整一下route.js的內容,讓route知道這兩個route分別要呈現什麼東西。
Todos.Router.map(function () {
    
    // 當Uri為'/'時去render名稱為「MyTodos」的Template
    this.resource('MyTodos', { path: '/' }, function () {
        // 這些route會在MyTodo這個Route之下
        this.route('active');// MyTodos.active
        this.route('completed');// MyTodos.completed
    });
});

<!--忽略...-->

Todos.MyTodosActiveRoute = Ember.Route.extend({
    model: function () {
        return this.store.filter('MyTodo', function (todo) {
            return !todo.get('isCompleted');
        });
    },
    renderTemplate: function (controller) {
        //這裡重複使用了既存的Template來render
        this.render('MyTodos/index', { controller: controller });
    }
});

Todos.MyTodosCompletedRoute = Ember.Route.extend({
    model: function () {
        return this.store.filter('MyTodo', function (todo) {
            return todo.get('isCompleted');
        });
    },
    renderTemplate: function (controller) {
        this.render('MyTodos/index', { controller: controller });
    }
});
現在重新刷一下畫面,應該已經可以切換完成、未完成的項目了。接下來我們新增一個可以清除所有已經完成的項目的功能。還是一樣,首先修改頁面
<footer id="footer">
<!--忽略...-->
            {{#if hasCompleted}}
            <button id="clear-completed">
                {{action "clearCompleted"}}
                Clear completed ({{completed}})
            </button>
            {{/if}}
</footer>
接下來我們要在Controller中新增以下功能
Todos.MyTodosController = Ember.ArrayController.extend({

// 忽略好大一段...

    // 清空已經完成的項目
    clearCompleted: function () {
        var completed = this.filterBy('isCompleted', true);
        // emberjs的array api提供的方法
        completed.invoke('deleteRecord');
        completed.invoke('save');
    },

    hasCompleted: function () {
        return this.get('completed') > 0;
    }.property('completed'),

    completed: function () {
        return this.filterBy('isCompleted', true).get('length');
    }.property('@each.isCompleted')
)};
重刷一下畫面後,功能就實做完成了~!但,既然有清除全部已經做完當然也會有全部已做完的功能才是
因此我們再來改一下畫面
<!--忽略...-->
        <section id="main">
            {{outlet}}
            {{input type="checkbox" id="toggle-all" checked=allAreDone}}
        </section>
<!--忽略...-->
//忽略很多...

allAreDone: function (key, value) {
        if (value === undefined) {
            return !!this.get('length') && this.isEvery('isCompleted', true);
        } else {
            this.setEach('isCompleted', value);
            this.invoke('save');
            return value;
        }
    }.property('@each.isCompleted')
好~改好之後存好檔,重新刷一下頁面這樣我們的全選功能就算完成了~!

以上這些小功能都完成後,一個簡單的Todo應用的基本能力就實做完成了,剩下的就是資料來源的問題,這就流到下次吧 = =汗

2014年11月4日 星期二

Take a peek at EmberJS - Phase2

前言:
接續上個月(拖好久...)的進度我們繼續把這個簡單的應用給完成吧~
第一階段
第一階段我們只是把最簡單的Model型態完成,但是卻還沒賦予Model跟View互動的機制,這階段就是要完成可以對TodoList的項目進行編輯以及把項目標示為已完成。

本文:
首先我們先完成選取在每個項目前面的勾勾就把該項目給畫上一個橫線表示完成的功能。在這個功能中我們會需要對列表的每個項目進行操作,我們的HTML一開始是長這樣的:
{{#each}}
          <li {{bind-attr class="isCompleted:completed"}}>
            <input type="checkbox" class="toggle">
            <label>{{title}}</label><button class="destroy"></button>
          </li>
        {{/each}}
在這裡我們可以新增一個controller來wrapping每個each裡的項目(Ember裡的controller管理狀態與跟畫面的互動,manage state and decorate data),新的controller內容如下:
Todos.MyTodoListController = Ember.ObjectController.extend({
    // 類似Knockout.js的Computed property,這個屬性會依照相依的屬性值的變化而改變
    // 是否完成
    isItemCompleted: function (key, value) {
        var model = this.get('model');

        if (value === undefined) {
            // Getter, 把目前項目的isCompleted回傳
            return model.get('isCompleted');
        } else {
            // Setter
            model.set('isCompleted', value);
            model.save(); // 更改項目的isCompleted值
            return value;
        }
    }.property('model.isCompleted')

    // 或也可以寫成如下方式:
    // 這個寫法看起來比較明確點
    isItemCompleted: Ember.computed('model.isCompleted', function (key, value) {
        var model = this.get('model');

        if (value === undefined) {
            // Getter, 把目前項目的isCompleted回傳
            return model.get('isCompleted');
        } else {
            model.set('isCompleted', value);
            model.save(); // 更改項目的isCompleted值
            return value;
        }
    })

})
這個isItemCompleted屬性後面呼叫了property方法,這相當於宣告這個屬性是一個computed property。
然後在前端的html我們改成這樣:
<ul id="todo-list">
   {{#each itemController="MyTodoList"}}
      <li {{bind-attr class="isCompleted:completed"}}>
         {{input type="checkbox" checked=isCompleted class="toggle"}}
            <label>{{title}}</label><button class="destroy"></button>
      </li>
   {{/each}}
</ul>
這樣在我們點選勾勾時,就可以把該項目劃上一個橫線表示完成了~!
接下來我們還要為這個Todo List增添一個功能,讓已經寫下來的項目可以被修改,同樣的這個功能也是針對each裡面的每個項目的操作,因此我們要把它放在MyTodoListController.js這個檔案裡面。首先我們在Index.schtml改寫成這樣:
<ul id="todo-list">
            {{#each itemController="MyTodoList"}}
            // 這邊分別用isCompleted與isEditing兩個Computed property來切換樣式
            <li {{bind-attr class="isCompleted:completed isEditing:editing" }}>
                // Handlebars有if else可以用,這點比用knockout方便很多
                {{#if isEditing}}
                <input class="edit"/>
                {{else}}
                {{input type="checkbox" checked=isItemCompleted class="toggle"}}
                // 這邊就是綁定事件的地方,讓這個元素(Label)的doubleClick事件與ditTodo方法綁在一起
                <label {{action "editTodo" on="doubleClick" }}>{{title}}</label><button class="destroy"></button>
                {{/if}}
            </li>
            {{/each}}
        </ul>
頁面改成這樣後,我們就要去MyTodoListController.js新增相對應的Code來實現這個功能:
Todos.MyTodoListController = Ember.ObjectController.extend({
    // action屬性裡面的方法(其實也就是屬性)代表與外界互動的方法
    actions: {
        editTodo: function () {
            this.set('isEditing', true);
        }
    },
    // 這個屬性是用來判斷項目是否正處於編輯狀態
    isEditing: false,

    isItemCompleted: function (key, value) {
        var model = this.get('model');

        if (value === undefined) {
            // Getter, 把目前項目的isCompleted回傳
            return model.get('isCompleted');
        } else {
            // Setter
            model.set('isCompleted', value);
            model.save(); // 更改項目的isCompleted值
            return value;
        }
    }.property('model.isCompleted')
})
完成這些Code之後目前Todo的項目就可以點擊兩下後進入編輯模式,但顯然還不完整。
我們需要讓編輯後的Title可以被儲存,並且在清空Title時可以把該筆記錄刪除,為了達到這個目的我們的頁面繼續修改~
<li {{bind-attr class="isCompleted:completed isEditing:editing" }}>
   {{#if isEditing}}
   // 我這邊的寫法跟官方的Toturial不太一樣,官方是用另一個Handlebars來處理這個View,我這邊是直接使用原本的Handlebars來做
   {{input class="edit" value=title focus-out="acceptChanges" insert-newline="acceptChanges"}}
   {{else}}
   {{input type="checkbox" checked=isItemCompleted class="toggle"}}
   <label {{action "editTodo" on="doubleClick" }}>{{title}}</label><button {{action "removeTodo"}} class="destroy"></button>
   {{/if}}
</li>
而在MyTodoListController中新增相對應的方法來處理這個View
Todos.MyTodoListController = Ember.ObjectController.extend({
   actions: {
        editTodo: function () {
            this.set('isEditing', true);
        },
        acceptChanges: function () {
            this.set('isEditing', false);

            if (Ember.isEmpty(this.get('model.title'))) {
                // 呼叫removeTodo方法
                this.send('removeTodo');
            } else {
                this.get('model').save();
            }
        },
        removeTodo: function () {
            // 若Todo的Title為空則把該記錄移除
            var todo = this.get('model');
            // 這個方法是相對於createRecord方法
            todo.destroyRecord();
            // 把更新後的model儲存
            todo.save();
        }
    },
   .................
}
完成以上Code後我們的Todo List就擁有最基本的新增修改刪除功能了~!

而在這個範例的最後一個篇章會介紹如何讓這個Todo List的資料來源變成外部取得~

ps.在看這個部分的教學文件並實做時在controller這個角色上面卡了很久,不是很清楚在Emberjs裡面Controller扮演的角色是什麼,哪裡改新增controller處理邏輯哪裡可以直接用,因此若願意的話歡迎在下方留言給予指教~

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/ => 簡體中文版,這個版本會稍舊一點,不過若真的是英文苦手可以看一下這個版本