2014年6月30日 星期一

AngularJS 教學 - Element Directives

本篇介紹一下幾個常用的 AngularJS Directives,建議可以先看 AngularJS 教學 - Templates

AngularJS 教學 - Templates 提到了幾個好用的 Template Directives,

而接下來要介紹的 Directives 主要是用來對 HTML 元素做動態修改或是 CSS 調整

以下我依序分享這些好用的 Directives

  • ng-if
          從字面上就知道這是要用來做什麼啦,ng-if 屬性接受一個布林值,如果是 true 的話
          那 ng-if 的標籤內容就會顯示出來,範例如下:
          <span ng-if="flag">Flag is True</span>
      <span ng-if="!flag">Flag is False</span>
 
  • ng-show & ng-hide
          這也是一個蠻明顯易用的 Directive 拉,跟 ng-if 一樣是接受一個布林值,ng-show
          為 true 時會顯示反之則不顯示,至於 ng-if 同樣道理,範例如下:
      <span ng-hide="flag">Hide</span>
      <span ng-show="flag">Show</span>

2014年6月29日 星期日

NodeJS 教學 - NPM 介紹(2)

當你要建立一個 Node 的應用程式時,多數都是需要一個定義應用程式的資訊的地方

包括名稱、作者、依賴關係...等眾多的 meta 資訊,而定義這些資訊的檔案叫做 package.json

它必須位在你專案 root 下,package.json 可以幫助你 publish 你的應用程式到 NPM 上,

就算你沒有要這樣做,你也是需要利用它來管理你的依賴關係(dependencies)

而一個最簡單的 package.json 檔案必須包含 name 和 version,如下:

{
    "name": "demo-app",
    "version": "0.0.1"
}

其中 package.json 必須遵循一個標準的 JSON 的格式來撰寫

接下來說明一些常用的或是重要的屬性:

NodeJS 教學 - NPM 介紹(1)

NPM 全名是 Node Package Manager,NPM 能夠幫助你下載、安裝和管理第三方的模組(modules)

NPM 有一點點像 Maven,它提供一個第三方的 Repository 以及一些方法讓你將模組安裝到你的電腦內,

並且提供了一個標準的定義模組依賴關係

剛開始寫 Node 程式的時候可能還不需要太了解它,但是如果你的 Node 程式需要依賴

其他第三方模組的時候,就勢必要透過 NPM 了。

以下分享一些 NPM 的使用方式:


  • Installing Packages
          語法:npm install <package you want to install>
          範例:npm install mongoose                                          

          結果:會在目前 command 的路徑下看到一個 node_modules 資料夾,node_modules
                      資料夾會放置你所有依賴得第三方模組,以本例來說就會看到 mongoose 資料夾在內

2014年6月28日 星期六

AngularJS 教學 - Templates

Templates 在 AngularJS 中的視圖(View)是相當重要的一環,它跟多數套件的 Templates 有著相同的功能。

而 AngularJS 的 Templates 則是包括了 AngularJS 特殊的屬性(ex: ng-model)或元素(Directives)

所以 AngularJS 會融合 Templates 與 Model 和 Controller 的資訊來做到 View 的渲染以及 Two-Way Binding

本篇介紹一些常用或是好用的 AngularJS Templates:


  • ng-repeat
          ng-repeat 幾乎是一定會用到的一個 AngularJS 標籤屬性,他目的是用來在畫面上將
         多筆資料的 Model 用迴圈的方式一個一個的建立到視圖上,如下範例所示:
         
         <script>
              var demoApp = angular.module("demoApp", []);
              demoApp.controller("demoCtrl", function($scope){
                  $scope.users = [
                     {uid:'1', name: 'Allen', phone: '12493215'},
                     {uid:'2', name: 'Alex', phone: '70314568'},
                     {uid:'3', name: 'Alice', phone: '345106847'}
                  ];
             });
     </script>
     <div ng-controller="demoCtrl">
              <ul>
                  <li ng-repeat="user in users">
                       Name: {{user.name}} <br/>
                       Phone: {{user.phone}} <br/>
                  </li>
              </ul>
     </div>
        

2014年6月27日 星期五

AngularJS 教學 - Data Binding

在 AngularJS 教學 - Controller and View 文章中我們介紹了 Controller 和 View 之間的關係

其中一個在 AngularJS 內相當重要的觀念就是 Data Binding (資料繫結)

在 AngularJS 的官網也有特別說明到這一點,這也是 AngularJS 相當重要的特點之一

其中 Data Binding 可以分為 One-Way 以及 Two-Way,當然 AngularJS 是建議你使用 Two-Way

先從 Onw-Way Data Binding 開始說起吧

















One -Way Data Binding 的目的在於要產生 View 之前,會將定義好的樣版(Template)與

樣版內對應的模型(Model)做合併(merge)的動作,再呈現在 View 上面,但其缺點在於

2014年6月25日 星期三

AngularJS 教學 - Controller 進階(2)

上一篇我們延續了 AngularJS 的 MVC 教學,並更深入的解釋 Controller 的一些共通問題

可以看 AngularJS 教學 - Controller 進階(1) 來先對 Controller 有更進一步了解

接下來要說明一個 Controller 繼承的陷阱,還記得上一篇有講到巢狀的 Controller 嗎

其實就是 Scope Inheritance 的意思,以下先用一個範例說明這個陷阱

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="demoApp">
     <head>
         <title>Bootstrap Examples</title>
         <script type="text/javascript" src="js/angular.min.js"></script>
         <script>
              var demoApp = angular.module("demoApp", []);
              demoApp.controller("parentCtrl1", function($scope){
                  $scope.count = 1;
                  $scope.add = function(){
                     $scope.count++;
                  };
             }).controller("childCtrl1", function($scope){
                  $scope.minus = function(){
                     $scope.count--;
                  };
             })
         </script>
     </head>
     <body>
         <div ng-controller="parentCtrl1">
              <p>Parent count: {{count}}</p>
              <button ng-click="add()">+1</button>

              <div ng-controller="childCtrl1">
                  <p>Child count: {{count}}</p>
                  <button ng-click="add()">+1</button><button ng-click="minus()">-1</button>
              </div>

         </div>
     </body>

</html>

先簡單說明一下程式,可以看到我定義了兩個Controller,分別是 parentCtrl1 與 childCtrl1

其中 childCtrl1 繼承 parentCtrl1,而  parentCtrl1 定義一個 add function 會聆聽頁面上

兩個 "+1" 的按鈕事件,而 childCtrl1 則定義一個 minus function 會聆聽頁面上的 "-1" 按鈕事件

其中 add function 雖然 childCtrl1 沒有定義,但因為繼承關係

執行時會去乎叫父類別的 add function,所以也不會有任何問題

$scope.count 也是如此,雖然 childCtrl 內沒定義但是依照繼承關係,也是會找到父類別的

乍看之下沒什麼問題,但在畫面操作時你可能會發現一些 bug,入下圖所示

我先按下上面的 +1按鈕 ,你會發現兩個 Controller 的 count 都會變 2,這是正確的














再按下下面的+1按鈕,同樣兩個直都會變 3,這也沒什麼問題














之後按下下面的 -1 按鈕, Child count 的直就會變成 2,但是上面的 Parent count 並沒有變
2,這是有問題的,因為 childCtrl1 的 $scope.count 是繼承於 parentCtrl1 的 count ,他們應該是同一個 Reference 才對














這時候再按下 任何一個 +1 按鈕,你會發現下面的 Child count 永遠不會被加1了












一開始遇到還以為是程式寫錯或是 AngularJS 的臭蟲,但其實這是正確的,

先來探討這個問題如何產生的,我們知道所有問題都是再按下 -1 的按鈕之後所造成的

其實 childCtrl1 的 minus function 並沒有什麼問題,問題再於 $scope.count 之上

首先 childCtrl1 並沒有在他的 scope 內定義 count,他是使用(reuse) parentCtrl 的 scope count

這沒錯,畢竟我的目的就是要在這兩個不同的 view 上顯示同一個資料,沒必要分開定義

問題是在於 當我出發 mins function 的時候,因為 childCtrl1 的 scope 並沒有宣告 count

因此他在執行 $scope.count--; 的時候,會預設幫你在這個 childCtrl1 的 scope 定義一個 count

因此這時候兩個 Controller 的 count 就分別獨立了,因此不管你按了哪個 +1按鈕,child count 永遠都不會動了

該如何解決,你只要如下更改程式即可


<script>
      var demoApp = angular.module("demoApp", []);
      demoApp.controller("parentCtrl1", function($scope){
          $scope.data = {
           count: 1
        };
          $scope.add = function(){
              $scope.data.count++;
          };
      }).controller("childCtrl1", function($scope){
          $scope.minus = function(){
              $scope.data.count--;
          };
      });
</script>

<div ng-controller="parentCtrl1">
      <p>Parent count: {{data.count}}</p>
      <button ng-click="add()">+1</button>
      <div ng-controller="childCtrl1">
          <p>Child count: {{data.count}}</p>
          <button ng-click="add()">+1</button><button ng-click="minus()">-1</button>
      </div>
</div>

可以注意到粗體部分,我只將換了一個宣告方式,我不直接在 scope 內定義一個 count 屬性

而是在 scope 內定義一個物件,而物件內才有 count 屬性!!

這樣就解決所有問題了....QQ

為什麼會這樣,其實這是因為 Javascipt 的繼承機制造成的(prototype inheritance),

相信對 Javascript 有一定了解的都會聽過 ~

至於實際的原理就不多說了,因為那又是另一個很深度的話題了


2014年6月24日 星期二

AngularJS 教學 - Controller 進階(1)

上一篇介紹了基本的 Controller 與 View 之間的應用 AngularJS 教學 - Controller and View

這篇來進一步介紹 Controller 內部吧,其實 Controller 相較於 Model 或是 Directives

都還要簡單許多,就目地面來說的確如此,畢竟 Controller 就只是個扮演

Model 與 View 之間的連結角色,提供資料以及服務給 View,並且處理一些 user action

但要掌握一整個 MVC 架構,Controller 仍然是一個關鍵的因素

以下先用一個類似於上一篇的範例來說明起:

【Java】圖像裁切

呼叫 ImageExpert 的 cropImage method 傳入圖片的所在路徑,以及要裁切位置的 x 與 y 軸,和要裁切的長度與高度

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
public class ImageExpert {
 public static void cropImage(String location, String x, String y, String h, String w){
  try {
   BufferedImage img = ImageIO.read(new File(location));
   Dimension size = new Dimension(Integer.valueOf(w), Integer.valueOf(h));
   Rectangle clip = null;
   if ((size.width + Integer.valueOf(x)) <= img.getWidth()
     && (size.height + Integer.valueOf(y)) <= img.getHeight()) {
     clip = new Rectangle(size);
   } else {
    if ((size.width + Integer.valueOf(x)) > img.getWidth()){
     size.width = img.getWidth() - Integer.valueOf(x);
    }
    if ((size.height + Integer.valueOf(y)) > img.getHeight()){
     size.height = img.getHeight() - Integer.valueOf(y);
    }
    clip = new Rectangle(size);
   }
   clip.x = Integer.valueOf(x);
   clip.y = Integer.valueOf(y);
   BufferedImage clipped = img.getSubimage(clip.x, clip.y, clip.height, clip.width);
   File outputfile = new File(location);
   ImageIO.write(clipped, getImageType(location), outputfile);
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 private static String getImageType(String locationOrName){
  return locationOrName.substring((locationOrName.lastIndexOf(".")+1));
 }
}

2014年6月22日 星期日

AngularJS 教學 - Controller and View

上一篇  AngularJS 教學 - MVC 介紹 說明了 AngularJS 的 MVC 特性

接下來簡單說明一下 Controller 與 View 在 AngularJS 間的關係

因為 Model 比較麻煩~  之後再說吧。

如何建立一個 Controller ?

        可以透過 module 物件呼叫 controller 方法來建立,如下:

        <script>
          var demoApp = angular.module("demoApp", []);
          demoApp.controller("demoCtrl", function($scope){
              $scope.name = "Allen";
          });
     </script>

        第一行先建立一個名為 demoApp 的 module 的物件,接下來透過

        此 module 物件呼叫 controller function 來建立一個名為 demoCtrl 的 Controller

        controller function 需要傳入兩個參數
       
        第一個為 Controller 的名子,

        第二個是一個 function 在 Controller 被建立時會被執行

       
什麼是 $scope ?

        上例中的 function 內有一行 $scope.name = "Allen";

        這行描述了我要在 $scope 物件內給予一個屬性 name,值是 Allen

        而 $scope 表示的是 Controller 與 View 之間的一個"作用域",如下圖:



        簡單來說他關聯了這個 Controller 以及其對應的 View 的資料,

        至於 $scope 怎麼來的,其實是 AngularJS 的依賴注入系統所給予的,

        細節之後在寫文章介紹,總之可以先當作你需要,AngularJS就會給你


如何對應 Controller 到 View 上?

        每個 Controller 都負責控管 HTML 上的某段區域,我們可以利用 ng-controller 屬性

        來指定一個 Controller 到一個 HTML 上,如下:

     <div ng-controller="demoCtrl">
              Name: {{name}}
              <div>
                   Name again: {{name}}
              </div>

     </div>

        我指定了 "demoCtrl" 於 div 標籤上,因此 demoCtrl 的作用範圍就會是此 div 下所有的內容

        因此在執行期間遇到 {{name}},會從 demoCtrl 的 $scope 找出是否有 name 這個屬性並呈現出來

        如果你在這個 div 之外寫上 {{name}} 是不會顯示出值的。


如何反應 View 的行為到 Controller 上?

        例如使用者按下頁面上的按鈕,我們該如何處理這個 click 事件

        首先 AngularJS 提供很多方法來聆聽 HTML 上的事件 (Ex:onclick, onselect....)

        以 click 為例,我們可以利用 ng-click 來聆聽 click 事件,如下:

        <button ng-click="changeName()">Click me</button>

        當 button 被按下時會呼叫 $scope 的 changeName function ,

        所以我們就必須將對應這個 View 的 Controller 的 $scope 物件加上 changeName

        demoApp.controller("demoCtrl", function($scope){
          $scope.name = "Allen";
          $scope.changeName = function(){
               $scope.name = "AllenFang";
          };
     });
       
        結果最後你會發現按下按鈕之後,View 上面的資料會變(Allen => AllenFang)

        你可能想說我只是更動了 $scope 的 name 屬性值,怎麼連 View 上的值都會變

        這是因為 AngularJS 的 Two-way data binding 的因素,詳情可以看AngularJS Data Binding

        這也是很多前端框架會提供的功能,之後我會再詳細描述。


以下為完整程式碼

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" ng-app="demoApp">
     <head>
         <title>Bootstrap Examples</title>
         <script type="text/javascript" src="js/angular.min.js"></script>
         <script>
              var demoApp = angular.module("demoApp", []);
              demoApp.controller("demoCtrl", function($scope){
                  $scope.name = "Allen";
                  $scope.changeName = function(){
                       $scope.name = "AllenFang";
                  };
              });
         </script>
     </head>
     <body>
         <div ng-controller="demoCtrl">
              Name: {{name}}
              <div>
                Name: {{name}}
              </div>
              <button ng-click="changeName()">Click me</button>
         </div>
     </body>

</html>

之後再進一步說明 Controller 的細節。