Angularjs directive uses transclude attribute

使用directive時,會將符合restrict的element都做初始化,directive指向的element包含child-element,這時候就要設定transclude:true,將child-element,添加到template裡,可以使用ng-transclude或者透過directive定義controller去處理。

透過ng-transclude處裡child element

定義好tag的結構,button-group為預設tag:

1
2
3
4
5
<div>
<button-group>
<button>Enter</button>
</button-group>
</div>

透過ng-transclude,將原先在button-group裡的child-element添加至ng-transclude裡面:

1
2
3
4
5
6
7
8
9
10
11
app.directive( "buttonGroup", function(){

return {

restrict: 'EA',
template: '<div class="btn-group" ng-transclude ></div>',
replace: true,
transclude: true
};

});

最後就會取得到底下的結果:

1
2
3
4
5
<div>
<div class="btn-group" ng-transclude="">
<button class="ng-scope">Enter</button>
</div>
</div>

透過directive的controller添加child element

使用$transclude取得clone的element,在append至需要添加的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
controller: ['$scope', '$element', '$transclude', function ($scope, $element, $transclude) {

$transclude(function(clone) {

var primaryBlock = $element.find('div.primary-block');
var transcludedButtons = clone.filter(':button');

angular.forEach(transcludedButtons, function(e) {

if (angular.element(e).hasClass('primary')) {
primaryBlock.append(e);
}

});
});
}]

使用ngTransclude的Demo

完整的範例,請參考jsfiddle上的code。

Angularjs directive uses attributes of scope

在angularjs中的directive的scope,分別有以下3種設定方式:

  • = or =attr : 指定需要連結的model名稱,會與parent scope的model互相綁定。
  • @ or @attr : 依照屬性的value傳入,來設定directive的scope值。
  • & or &attr : 將預設的function,委託給其他event呼叫。

只要directive有設定scope對應的值,或者設定scope:true,則directive將會自行建立一個新的scope。

使用 & 和 @ 屬性來設定scope

定義一個alert屬性:

1
<div alert on-enter="$window.alert('Hello world')" message="Say hello world!"></div>

其中message屬性為alert呈現的訊息,on-enter則會在按鈕被點擊即時會觸發。

實作一個alert directive:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.directive( "alert", function(){

return {

restrict: "A",
template: "<div class='alert-container'><p ng-bind="message"></p><button ng-click='onEnter();'>Enter</botton></div>",
scope:{
"onEnter":"&",
"message":"@"
},
replace: true

};

});

alert的scope將會取得onEntermessage的屬性,分別去對照template的ng-click="onEnter()"ng-bind="message"

Demo

完整的範例,請參考jsfiddle上的code。

在angularjs限制同時間的request的數量

假設有多筆request要發送時,但又不希望一次全發送出去,希望將request的數量,在同個時段只出現5個request,這時候就需要一個manager之類的object去處理,在這裡使用ngHttpPool為例。

安裝ngHttpPool

使用bower安裝,或者至github下載:

1
bower install ngHttpPool

使用ngHttpPool

載入一個ngHttpPool module:

1
<script type="text/javascript" src="/bower_components/ngHttpPool/public/src/ngHttpPool.js"></script>

定義module相依性:

1
2
3
4
5
angular.module( "app", [ 
"ngHttpPool"
]).controller('ctrl',function( httpPool ){
//doSomething
});

建立一個pool和定義同時存在的最大request數量:

1
2

var pool = httpPool.create( 2 );

傳入config,發送多個request:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

for( var index = 0; index<10 ; index++ ){

/**
* @param {Object} config
* @param {function} success is optional
* @param {function} error is optional
*/
pool.map({method:"get",url:"/",params:{id:index}}, function(){

console.log( "success" );

});

}

如果要等request都從server返回,可使用以下的方式:

建立一個defer,會將之後發送request都當作監聽對象:

1
2

pool.listen()

將config push到pool,執行request的發送:

1
2
3
4
5

for( var index = 0; index<10 ; index++ ){

pool.map({method:"get",url:"/",params:{id:index}});
}

結束監聽,當所有結果回傳時,將會執行promise.then:

1
2
3
4
5
6

var promise = pool.Promise();

promise.then(function(){
console.log( "all of the callbacks." );
});

Demo

可參考ngHttpPool github。

網頁已過期在IE7、IE8

網頁已過期在IE7、IE8

通常這個問題是當form submit出去後,再按上一頁、下一頁就會出現的問題,不過如果在form的action設為錨點,也可能引起這種狀況,而這種情況則是未升級到IE9會發生的。

實際狀況

第一種狀況
form submit出去,由使者點選上一頁、下一頁就會出現這種狀況,或者是透過程式控制history

1
2
history.go()
history.back()

第二種狀況
form的action為’#’時,會導致form送出之後,在被重新導向

1
2
3
<form method="post" action="#">

</form>

而第二種狀況比較特別,只在未升級的成IE9以上瀏覽器,用相容模式在IE8包括以下的瀏覽器,才會出現這種問題。至於目前使用的chromefirefoxIE9+都已經不會有這問題了。但第一種狀況IE還是會出現,其餘瀏覽器都會重新submit和保留之前的畫面。

angularjs-placeholder submit a form on IE

使用angularjs-placeholder可以在老舊IE上,支援placeholder這個屬性,但是底層還是去實作foucsblur這兩個event,並且用替換value的方式,達到模擬placeholder的效果。在一般使用上沒什麼太大問題,但是假設今天有一個formsubmit的時候,會把placeholder的預設值也一起送出,這時就必須在submit之前,就將預設值清除,或者在取值得時候,暫時將欄位值清除,之後在還原。

使用placeholder.ensure取值:

ensure接受兩個參數:

1
2
3
4
5
6
7

/**
* @param {jQlite} $elem 可以是form或者為多個elements
* @param {function} callback 實際取值的地方
*/


placeholder.ensure( $elem, callback );

callback裡面取得值後,可使用record.back來回復placeholder的預設值。

1
2
3
4
5
6
7
8
9
10
11
12
app.controller( "formController", [ "placeholder", "$element", function( placeholder, $form ){

placeholder.ensure( $form , function( record ){

var $textarea = $form.find("textarea");
alert( $textarea );

//復原
record.back();
});

});

若要直接submit可直接呼叫ensure,不須使用callback。

1
placeholder.ensure( $form )

如果使用有支援placeholder的瀏覽器,還是會有ensure可以使用,但是將不會改變任何屬性,純粹只是個包裝過後的function。

Fixed a placeholder feature on IE (angularjs)

Placeholder要支援老舊的IE7~IE9,使用jQuery也都有一堆plugin可以使用了。如果要在angularjs上使用,但是又不想載入jQuery,這時候可以使用angularjs-placeholder module。

使用angularjs-placeholder

載入angular module:

1
<script type="text/javascript" src="../src/angularjs-placeholder.js"></script>

定義需要使用到的module,和初始化ng-app:

1
2
3
4
<script type="text/javascript">
var app = angular.module( "demoApp", ["html5.placeholder"] );
angular.bootstrap( document, [app.name] );
</script>

實作angularjs-placeholder遇到的小問題

在實作angularjs-placeholder module時,在IE7發現一個小問題,無法取得placeholder的值,但是在有的jQuery情況,都會幫忙處理掉了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* input default: <input placeholder="Please,enter your name!" type="text" />
*/


//use native

var attr = input.getAttribute("placeholder");

// attr == null
console.log( attr );

//use jqlite
var $elem = angular.element( input );

// attr == null
console.log( $elem.attr( "placeholder" ) );

所以如果要在IE7取得placeholder,這時候native有提供另一個api可以使用。

1
2
3
4
5
6
var node = input.getAttributeNode( "placeholder" );

var attr = node?node.nodeValue:node;

//Please,enter your name!
console.log( attr );

Titanium相同檔名會拋出錯誤

大部分的程式,只要檔名大小寫不同,就會視為不同的檔案,但是在titanium編譯的時候,則會發生錯誤,因為titanium不允許在相同資料夾裡,有相同的檔案名稱。因為之前一直是用tishadow測試,都沒遇到這個問題,最後要build在device上,就遇到了以下錯誤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[ERROR] /home/sparrow/Works/test/build/android/bin/assets/Resources/actions/IndexAction.js: error: File is case-insensitive equivalent to: /home/sparrow/Works/ec2/test/build/android/bin/assets/Resources/actions/indexAction.js
[ERROR] Exception occured while building Android project:
[ERROR] Traceback (most recent call last):
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.2.GA/android/builder.py", line 2621, in <module>
[ERROR] builder.build_and_run(True, avd_id, device_args=device_args, debugger_host=debugger_host, profiler_host=profiler_host)
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.2.GA/android/builder.py", line 2400, in build_and_run
[ERROR] launched, launch_failed = self.package_and_deploy()
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.2.GA/android/builder.py", line 1867, in package_and_deploy
[ERROR] unsigned_apk = self.create_unsigned_apk(ap_, webview_js_files)
[ERROR] File "/home/sparrow/.titanium/mobilesdk/linux/3.1.2.GA/android/builder.py", line 1679, in create_unsigned_apk
[ERROR] resources_zip = zipfile.ZipFile(resources_zip_file)
[ERROR] File "/usr/lib/python2.7/zipfile.py", line 701, in __init__
[ERROR] self.fp = open(file, modeDict[mode])
[ERROR] IOError: [Errno 2] No such file or directory: '/home/sparrow/Works/ec2/test/build/android/bin/app.ap_'
[ERROR] Build process exited with code 1
[ERROR] Project failed to build after 1m 40s 422ms

解決方法就是更換其中一個檔案的名稱,有了這次教訓…,以後盡量不使用相同檔案名稱。

Javascript prototype and this記憶體用量

在javascript中,使用function建立一個object時,用prototype來配置屬性,被建立的object會指向同一個prototype object,而使用this建立的,則會是獨立的屬性

Example:

定義TomMary二個人。

1
2
3
4
5
6
7
function Tom(){

}

function Mary(){

}

使用prototype定義John的父母,分別為TomMary

1
2
3
4
5
6
John.prototype = {
parents:{
dad:new Tom(),
mom:new Mary()
}
};

this的方式定義John的父母。

1
2
3
4
5
6
7
function John(){

this.parents = {
dad:new Tom(),
mom:new Mary()
};
}

然後用thisprototype的方式,各自建立1000000

1
2
3
4
5
var people = [];

for( var i = 0; i < 1000000 ; i++ ){
people.push( new John() );
}

結果如下:

使用prototype的方式
使用this的方式

this使用的記憶體為72.5m,而prototype的記憶體是25m,由此可知,使用prototype來建立多個object,會節省很多記憶體。


完整Prototype Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>use "prototype"</h1>
<button id="create">create children</button>
<div id="count">0</div>

<script type="text/javascript">
function Tom(){

}

function Mary(){

}

function John(){

}

John.prototype = {
parents:{
dad:new Tom(),
mom:new Mary()
}
};

var people = [],countElem = document.querySelector("#count");

document.querySelector("#create").addEventListener( "click", function(){

for( var i = 0; i < 1000000 ; i++ ){
people.push( new John() );
}

countElem.innerHTML = people.length;
});

</script>
</body>
</html>

完整This Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>use "this"</h1>
<button id="create">create children</button>
<div id="count">0</div>

<script type="text/javascript">

//define dad
function Tom(){

}

//define mom
function Mary(){

}

//define child
function John(){

this.parents = {
dad:new Tom(),
mom:new Mary()
};
}


var people = [],countElem = document.querySelector("#count");

//create children
document.querySelector("#create").addEventListener( "click", function(){

for( var i = 0; i < 1000000 ; i++ ){
people.push( new John() );
}

countElem.innerHTML = people.length;
});

</script>
</body>
</html>

Javascript Prototype的概念

javascript的prototypethis,是剛接觸javascript的人比較難理解的。在javascript裡,假設定義一個一個Bird function時,可使用同個prototype建立不同物件,也就所有經由Bird function產生的物件,都會擁有相同的protoype的屬性。

Example:

定義一隻鳥,這種鳥的名稱叫做Sparrow

1
2
3
4
5
6
7
function Bird(){
//do something
}

Bird.prototype = {
name:"Sparrow"
};

接著建立兩個變數,分別為Tom的寵物

1
2
var bird = new Bird();
var petOfTom = new Bird();

Tom把他的寵物稱為Raven,而這種的名稱,重新命名為Black Sparrow

1
2
3
petOfTom.name = "Raven";

Bird.prototype.name = "Black Sparrow";

最後你就會發現一個有趣現象,即使這種的名稱重新命名了,Tom的寵物仍然會以Tom命名的方式顯示

1
2
3
4
5
//name of bird:Black Sparrow 
console.log( "name of bird:" + bird.name );

//Tom's pet called him: Raven.
console.log( "Tom's pet called him: " + petOfTom.name + "." );

也就是說也就是說,當prototype的name改變時,其他經由Bird new出來的object name,都會跟著修改但是被new出來的object,一旦屬性被修改,不管之後prototype如何更動,則object的屬性會依照你所指定的修改


完整範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>

<script type="text/javascript">

function Bird(){

}

Bird.prototype = {
name:"Sparrow"
};

var bird = new Bird();
var petOfTom = new Bird();

console.log( "name of bird:" + bird.name );

//rename
petOfTom.name = "Raven";

//rename
Bird.prototype.name = "Black Sparrow";

console.log( "rename..." );

console.log( "name of bird:" + bird.name );

console.log( "Tom's pet called him: " + petOfTom.name + "." );

</script>

</body>
</html>

python encode

在作業系統上都有預設語言,例如windows上預設可能是big5,linux則是utf8。當python要轉換編碼的時候,可以使用encode,但是有個先決條件,必須要先把編碼轉換成作業系統的預設語言

以linux為例,要轉成big5,需先decode成utf8,在encode成big5:

1
2
3
4
5
6
# -*- coding: utf-8 -*-

u_hello = "哈囉".decode("utf8")
big5_hello = u_hello.encode("big5")

print big5_hello

接著將轉換過得字串寫入到檔案,在用gedit、word…之類的軟體,以big5編碼開啟就能看到剛剛寫入文字。

1
2
3
4
#write to file
f = open( "big5_text.txt", "w" )
f.write( big5_hello )
f.close()

另外在字串前面加個u,和decode成utf8相同:

1
2
3
4
5
6
7
8
9
10
11
>>> u"哈囉" == "哈囉".decode("utf8")
True

>>> type("哈囉")
<type 'str'>

>>> type(u"哈囉")
<type 'unicode'>

>>> type("哈囉".decode("utf8"))
<type 'unicode'>

如果是windows的作業系統,只要先將decode成big5,即可轉成其他編碼。