Quantcast
Channel: 黑暗執行緒
Viewing all articles
Browse latest Browse all 2311

NG筆記6-動態新增下拉選單選項

$
0
0

復刻對象: KO範例3 - 動態新增下拉選單選項

先示範一個失敗寫法。在KO範例裡,新增選項按鈕不包含在ViewModel範圍內,而是透過jQuery click事件在選項集合新增物件,而選項集合是ko.observabelArray(),KO能感測到新增動作,同步增加下拉選單選項;但同樣做法直接搬到NG行不通,option是尋常JavaScript陣列,NG感測不到Scope之外對ViewModel屬性的更動。如以下程式,按鈕後vm.options陣列雖已加入新元素,卻不會反應到下拉選單。Live Demo

<!DOCTYPEhtml>
<htmlng-app="sampleApp">
<head>
<metacharset="utf-8">
<title>Lab 3 - 動態增加SELECT選項(無效)</title>
</head>
<bodyng-controller="defaultCtrl">
<selectid="selOptions"style="width: 120px"
ng-options="item.text for item in model.options"ng-model="model.result">
</select>
Result=<spanng-bind="model.result.value"></span>
 
<divstyle="margin-top: 10px">
Text: <inputid='txtOptText'value="Firefox"/>
Value: <inputid='txtOptValue'value="ff"/>&nbsp;
<inputtype="button"value="新增選項"id='btnAddOpt'/>
<divid="dvDebug"></div>
</div>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js"></script>
<script>
var vm = null;
    angular.module("sampleApp", [])
    .controller("defaultCtrl", function($scope) {
function myViewModel() {
var self = this;
        self.options = [
          { text: "IE", value: "ie" }
        ];
        self.result = self.options[0];
      }
      vm = new myViewModel();
      $scope.model = vm;
    });
    $("#btnAddOpt").click(function() {
      vm.options.push({
"text": $("#txtOptText").val(),
"value": $("#txtOptValue").val()          
      });
      $("#dvDebug").text(JSON.stringify(vm.options));
    });
</script>
</body>
</html>

在下圖中,options陣列已新增Firefox選項,但下拉選單卻仍只有一個選項,由dvDebug顯示的JSON.stringify(vm.options)可以驗證這點:

以上範例突顯了NG與KO的一項重要差異: KO需要明確宣告ko.observable()、ko.observableArray(),但不管任何時候變更這些受觀察物件都會引發UI及相依變數連動;而在NG中,一般的JavaScript物件屬性就可做為繫結對象,但相對地,要"在Scope感應範圍內更動資料,才會引發UI改變及連動"。就這個案例而言,將新增選項動作移入ng-click(),Scope便會將資枓變化反應到<select>。Live Demo 

<inputtype="button"value="新增選項"id='btnAddOpt'ng-click="model.addOption()"/>
</div>
<scriptsrc="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.2.14/angular.js"></script>
<script>
var vm = null;
    angular.module("sampleApp", [])
    .controller("defaultCtrl", function($scope) {
function myViewModel() {
var self = this;
        self.options = [
          { text: "IE", value: "ie" }
        ];
        self.result = self.options[0];
        self.addOption = function() {
          self.options.push({
"text": $("#txtOptText").val(),
"value": $("#txtOptValue").val()
          });
        };
      }
      vm = new myViewModel();
      $scope.model = vm;
    });
</script>

(說明: 在ViewModel中存取$("#txt…")有違SoC原則,為不良設計。此處強調僅移動function()位置,故函式內部保留原樣)

想從NG事件之外更動ViewModel,需要一些技巧。NG提供jQuery.scope()方法,可以取得UI元素所屬的Scope物件,接著我們就可以存取到model物件及model.options: Live Demo

<script>
var vm = null;
    angular.module("sampleApp", [])
      .controller("defaultCtrl", function($scope) {
function myViewModel() {
var self = this;
          self.options = [{
            text: "IE",
            value: "ie"
          }];
          self.result = self.options[0];
        }
        vm = new myViewModel();
        $scope.model = vm;
      });
    $("#btnAddOpt").click(function() {
var scope = $(this).scope();
      scope.model.options.push({
"text": $("#txtOptText").val(),
"value": $("#txtOptValue").val()
      });
      $("#dvDebug").text(JSON.stringify(scope.model.options));
    });
</script>

但是以上程式仍不管用,還差一個關鍵: 必須在Scope監視下更動資料,才會觸發繫結UI元素、連動變數或函數的更新。使用$scope.$apply()執行ViewModel更新,NG才能掌握資料異動。用法有三種: $scope.$apply("指令字串") 、 $scope.$apply(function() { 更新程式碼 }),或是依一般做法更新後再不帶參數呼叫$scopt.$apply()。Live Demo

    $("#btnAddOpt").click(function() {
var scope = $(this).scope();
//方法1: 傳入指令字串給$apply()執行
      scope.$apply("model.options.push({text:'Chrome',value:'chrome'})");
//方法2: 利用$apply()執行函式
      scope.$apply(function() {
        scope.model.options.push({
"text": $("#txtOptText").val(),
"value": $("#txtOptValue").val()
        });
      });
//方法3: 執行完畢呼叫$apply()[不帶參數]
      scope.model.options.push({text:"Safari",value:"safari"});
      scope.$apply();
      $("#dvDebug").text(JSON.stringify(scope.model.options));
    });
 
[NG系列]
http://www.darkthread.net/kolab/labs/default.aspx?m=post&t=angularjs

Viewing all articles
Browse latest Browse all 2311

Trending Articles