2013年6月7日星期五

AngularJS Directive Scope Note   [+/-]

Ticore's Blog

AngularJS 可以允許自行定義新的 directive
能以四種不同型態使用 element, attribute, class, comment
其中 attribute, class 方式,可以在同一個 element 上宣告多個 directive
每個 directive 又有各自的 scope 屬性設定

乍看之下好像 directive scope 似乎是獨立的
但是事實上~~
同一個 element 上宣告多個 directive
無論怎樣設定 scope 屬性皆共用同一個 $scope

差別僅在於:

  • 皆共用 parent scope (scope: false)
  • 皆共用 new scope (scope: true)
  • 皆共用 isolate scope (scope: {})

多組 scope 設定優先順序:isolate scope > new scope > parent scope

也因此,directive priority 處理順序也有一些限制在
要求建立 isolate scope 之後不可以再出現要求 isolate scope 或是 new scope
否則會得到

Error: Multiple directives [xxx, xxx] asking for isolated scope on: ...

以下是簡單的測試程式:

<!DOCTYPE HTML>
<html ng-app id="ng-app">
<head>
    <meta charset="UTF-8">
    <title>AngularJS App</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.5/angular.min.js"></script>
    <script>
        var app = angular.module("ng");

        var genNgDir = function(name, priority, scope) {
            app.directive(name, function factory() {
                return {
                    priority: priority,
                    scope: scope,
                    controller: function($scope) {
                        console.log(name, $scope);
                    }
                };
            });
        };

        genNgDir("myDir01", 0, {});
        genNgDir("myDir02", 1, true);
        genNgDir("myDir03", 2, false);

    </script>
</head>
<body>
    <p my-dir01 my-dir02 my-dir03>Content</p>
</body>
</html>

JSBin Edit

Read more...

2013年5月22日星期三

AngularJS Nested App   [+/-]

Ticore's Blog

AngularJS 提供 controller, directive, filter,... 等功能可以利用 module 來管理
但是即使如此,只要在同一個 app 內所有載入的 module 都會在同一個名稱空間內
東西變得複雜時候,很容易遇到名稱空間不足與衝突的問題
網路上也有人提出一些解法

Delegating Nested Directive Behavior To Parent Directive In AngularJS

不過那前提是上層 $scope.delegateDirectiveLinking
必須要了解下層所有可能會出現的 delegate directive
而且寫法有點複雜~

我想到另一種方式是在主要的 ng-app 下啟動 sub-app
兩個 app 之間名稱空間是完全獨立的
當然連帶 $scope 繼承關係也會被隔絕
這一點可以另外再想辦法做資料傳遞同步
程式碼如下:

<!DOCTYPE HTML>
<html ng-app="MainApp">
<head>
    <meta charset="UTF-8">
    <title>Nested App</title>
    <script src="../libs/angular-1.1.4.js"></script>
    <script>
        
        // 宣告 subApp directive
        (function() {
            var ngApp = angular.module("ng");
            var subAppDirective = function() {
                return {
                    restrict: "CA",
                    terminal: true,
                    link: function(scope, iElement, iAttrs, controllers) {
                        angular.bootstrap(iElement.children(), [iAttrs.subApp]);
                    }
                };
            };
            ngApp.directive("subApp", subAppDirective);
        })();


        // 宣告 MainApp module
        (function() {
            var mainApp = angular.module("MainApp", []);
            var mainCtrlFn = function($scope) {
                $scope.name = "MainApp";
                console.log("MainApp.MainCtrl();");
            };
            mainApp.controller("MainCtrl", mainCtrlFn);
        })();


        // 宣告 ChildApp module
        (function() {
            var subApp = angular.module("ChildApp", []);
            var mainCtrlFn = function($scope) {
                $scope.name = "ChildApp";
                console.log("ChildApp.MainCtrl();");
            };
            subApp.controller("MainCtrl", mainCtrlFn);

            var logDirective = function() {
                return function(scope, iElement, iAttrs) {
                    console.log("log:", iAttrs.log);
                };
            };
            subApp.directive("log", logDirective);
        })();

    </script>
</head>
<body ng-controller="MainCtrl">
    {{ name }}
    <div sub-app="ChildApp">
        <div ng-controller="MainCtrl" log="Hello!">
            {{ name }}
        </div>
    </div>
</body>
</html>

Nested App - JS Bin

Read more...

2013年5月7日星期二

AngularJS 語法樣式筆記   [+/-]

Ticore's Blog

AngularJS 標籤語法非常自由,能夠符合 HTML, XHTML 等樣式規範
具有多種寫法與樣式,可以寫成標籤、屬性、Class、註解...
看到後來也是搞不清楚到底有多少種可以用
所以想一次整理清楚,做個筆記

依據撰寫位置可以分為四種:

  1. Element:    <my-dir />
  2. Attribute:    <p my-dir="exp" />
  3. Class:    <p class="my-dir: exp;" />
  4. Comment:    <!-- directive: my-dir exp -->

依據撰寫風格又可以分為四種:

  1. None:    my-dir
  2. XML Namespace:    my:dir
  3. HTML5 Data:    data-my-dir
  4. XHTML:    x-my-dir

兩者之間基本上可以排列組合,但是又有幾個例外,做成對照表如下:

Element Attribute Class Comment
None <my-dir /> <p my-dir="exp" /> <p class="my-dir:exp;" /> <!-- directive: my-dir exp -->
XML Namespace <my:dir /> <p my:dir="exp" /> N/A N/A
HTML5 Data <data-my-dir /> <p data-my-dir="exp" /> <p class="data-my-dir:exp;" /> <!-- directive: data-my-dir exp -->
XHTML <x-my-dir /> <p x-my-dir="exp" /> <p class="x-my-dir:exp;" /> <!-- directive: x-my-dir exp -->

語法註解 my-dir: exp;
其中 my 表示 namespace
dir 表示 directive name
exp 表示 expression 表示式

可以看到 element 寫法是無法直接支援 exp 表示式的
因為通常 element 底下還會再包 child node

另外,註解型態的語法只能支援一個 directive + exp 而已

對於 AngularJS 內建 directive 來說,通常只有支援到 attribute, class 一兩種寫法而已
要測試的話得要自行宣告 directive 加上 restrict: "ECMA"

以下附一個通用 directive 範例,示範四種語法:

<!DOCTYPE HTML>
<html ng-app id="ng-app">
<head>
    <meta charset="UTF-8">
    <title ng-init="name='<AngularJS>';">{{name}}</title>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
    <script>

        var app = angular.module("ng");

        app.directive("myDir", function factory() {
            return {
                restrict: "ECMA",
                scope: true,
                template: "<span>{{exp}}</span>",
                replace: true,
                controller: function($scope) {
                    console.log("myDirCtrlFn();");
                },
                link: function(scope, element, attrs, ctrl) {
                    scope.$watch(attrs.myDir, function(value) {
                        scope.exp = value === undefined ? "[default]" : value;
                    });
                }
            };
        });

    </script>
</head>
<body>

    <h3>Element Directive:</h3>
    <p>
        &lt;my-dir /&gt; :
        <my-dir />
    </p>

    <h3>Attribute Directive:</h3>
    <p>
        &lt;span my-dir="exp" /&gt; :
        <span my-dir="'OK'" />
    </p>

    <h3>Class Directive:</h3>
    <p>
        &lt;span class="my-dir: exp;" /&gt; :
        <span class="my-dir:'OK';" />
    </p>

    <h3>Comment Directive:</h3>
    <p>
        &lt;!-- directive: my-dir exp --&gt; :
        <!-- directive: my-dir 'OK' -->
    </p>

</body>
</html>

JS Bin Code View

Read more...

2013年5月2日星期四

AngularJS Injector and Cache Note   [+/-]

Ticore's Blog

我在看 AngularJS 時候,固然覺得它的 Injector, DI 等功能設計得非常好
但是常常搞不清楚哪裡可以注入哪些物件
最近重新看 AngularJS 文件,索性連原始碼一起看
把 Injector, Cache, Provider, Instance 等關係弄清楚做個筆記

AngularJS 內部有兩種 injector 以及對應的 cache
分別是 providerInjector (providerCache) 以及 instanceInjector (instanceCache)
providerCache 主要是用來存放 service provider,用來建立 service 物件,存放在 instanceCache 內
譬如 $controllerProvider 會建立 $controller 物件

在 angular.bootstrap 或手動呼叫 angular.injector (aka createInjector) 時
會同時建立兩者,並回傳 instanceInjector
而兩種 injector 可以透過 $injector 名稱注入取得

但是只有在 module.config 注入的 $injector 可以取得 providerInjector
除此之外,拿到的 $injector 都是 instanceInjector

以此類推,在 module.config 能注入的主要只有 provider 類物件
e.g. $controllerProvider, $windowProvider, $httpProvider, ngBindDirectiveProvider
以及 $injector, $provide 與 constant, value provider

其它地方能注入的主要只有非 provider 類物件
e.g. $controller, $window, $http, ngInitDirective
以及 $injector 與 constant, value,但不包含 $provide

觀察 cache 方式:

<!DOCTYPE HTML>
<html ng-app="MyApp">
<head>
<meta charset="UTF-8">
    <title>AngularJS App</title>
    <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
    <script>
        // 從 module.config 取得 $injector
        var app = angular.module("MyApp", []);
        app.constant("constObj", "[Constant Object]");
        app.value("valueObj", "[Value Object]");

        var configFn = function($injector, constObj, valueObjProvider) {
            console.log("Provider Injector:", $injector);
            console.log("Constant Obj:", constObj);
            console.log("Value Obj Provider:", valueObjProvider);
        };
        app.config(configFn);

        // 從 controller 取得 $injector
        var MainCtrlFn = function ($injector, constObj, valueObj) {
            console.log("MainCtrl();");
            console.log("Instance Injector:", $injector);
            console.log("Constant Obj:", constObj);
            console.log("Value Obj:", valueObj);
        }
        app.controller("MainCtrl", MainCtrlFn);

        // 直接手動方式從 module 建立 injector
        var injector = angular.injector(["ng"]);
        console.log("Empty Injector:", injector);

    </script>
</head>
<body ng-controller="MainCtrl">
    <p ng-init="name='<AngularJS>';">{{name}}</p>
</body>
</html>

將取得 $injector 物件 log 出來
打開 console 展開 $injector object / get / <function scope> / Closure[0] / cache
看到的便是已經建立且能注入的物件快取


再打開下一個 Closure[1] 可以同時看到 providerCache, instanceCache
倘若是 instanceInjector 尚未建立的物件,只要 providerCache 內有對應的 Provider 也能注入
會自動建立 instance

Read more...

2013年4月19日星期五

Dart M4 Async Update Note   [+/-]

Ticore's Blog

Dart 更新到 M4,部分 API 修改過
簡單對 Async Future 部分做個筆記
其它部分可以參考官方說明

Dart News & Updates - List of Last Minute M4 Breaking Changes
Dart News & Updates - Goodbye InvocationMirror, Hello Invocation and Symbol
Dartwatch - Dart Milestone 4 is released

Dart M4 Async Future 變更如下:

  • 非同步執行
    new Future(computation())
    new Future.delayed(duration, [computation()])
  • 同步執行
    new Future.of(computation()) 改為 new Future.sync(computation())
    或非同步的 new Future(computation()) 也能達到攔截錯誤效果
  • 非同步回傳結果
    new Future.immediate(value) 改為 new Future.value(value)
  • 非同步回傳錯誤
    new Future.immediateError(error) 改為 new Future.error(error)
  • 移除 AsyncError class
    onError, catchError 會直接收到原始非同步錯誤物件

Read more...

2013年4月9日星期二

Dart 非同步錯誤流程筆記   [+/-]

Ticore's Blog

繼續之前提到 Dart Async 流程控制
Dart Async Future 有支援兩種非同步錯誤捕捉寫法
分別是 then onError 與 catchError
功能上略有差異

首先是流程控制部分,兩種 API 能夠捕捉錯誤範圍不一樣
下面用簡單示意圖表示

then onError flow:

                                          Normal Flow
                                      /--------->--------\
                                     /                    \
                                    /                      \
>>----------------->---------------/                        \----------->
new Future(...) . then ( onValue: ... , onError: ... ) . then ( ... ) ...
  |__________|                             /-------------->------------->
        \                                 /
         \                               /
          \-------------->--------------/
                    Error Flow

catchError flow:

                                              Normal Flow
                                       /----------->-----------\
                                      /                         \
                                     /                           \
>>----------------->----------------/                             \----------->
new Future(...) . then ( onValue: ... ) . catchError ( ... ) . then ( ... ) ...
  |________________________________|          /---------------->-------------->
                  \                          /
                   \                        /
                    \----------->----------/
                           Error Flow

從示意圖應該可以看得出來 onError 是無法捕捉同一層的 onValue 內的錯誤
除此之外,catchError 還可以額外多傳入一個表示式,決定能處裡的錯誤種類

new Future.error("String Error")
  .catchError((e) => print("[String] $e"), test: (e) => e is String)
  .catchError((e) => print("[int] $e"), test: (e) => e is int);

Dart Async Future API 設計很有趣
單單一串宣告下來,其中卻包含兩種不同流程:
Normal Flow 傳遞的是非同步計算結果
Error Flow 傳遞的卻是非同步錯誤

再來是第一次呼叫非同步函式,立即發生的錯誤
這無論是用 onError 或 catchError 都無法捕捉的
不過 Future 有提供一個方式可以解決
把所有實作都用 new Future(...) 或 new Future.sync(...) 包起來
這樣產生的錯誤就可以被 onError, catchError 捉到了

import "dart:async";

Future asyncCall() {
  return new Future(() {
    print("asyncCall();");
    throw "Error";
    return new Future.value("Done");
  });
}

void main () {
  Future future = asyncCall()
    .then(
      (_) {
        print("[onValue] $_");
      }
      , onError: (e) => print("[onError] $e")
    );
}

2013/04/17 更新為 Dart M4

Read more...