1. 程式人生 > >AngularJS自學之路(三)——控制器和表示式

AngularJS自學之路(三)——控制器和表示式

控制器

控制器在AngularJS中的作用是增強檢視。
AngularJS中的控制器是一個函式,用來向檢視的作用域中新增額外的功能。我們用它來給作用域物件設定初始狀態,並新增自定義行為。
當我們在頁面上建立一個新的控制器時,AngularJS會生成並傳遞一個新的$scope給這個控制器。可以在這個控制器裡初始化scope。由於AngularJS會負責處理控制器的例項化過程,我們只需編寫建構函式即可。下面的例子展示了控制器初始化:

function FirstController($scope) {
$scope.message = "hello";
}

細心的讀者會發現,我們是在全域性作用域中建立的這個函式。這樣做並不合適,因為會汙染全域性名稱空間。更合理的方式是建立一個模組,然後在模組中建立控制器,如下所示:

var app = angular.module('app', []);
app.controller('FirstController', function($scope) {
$scope.message = "hello";
});

只需建立控制器作用域中的函式,就能建立可以在檢視中使用的自定義操作。很幸運,AngularJS允許我們在檢視中像呼叫普通資料一樣呼叫$scope上的函式。

用內建指令ng-click可以將按鈕、連結等其他任何DOM元素同點擊事件進行繫結。ng-click指令將瀏覽器中的mouseup事件,同設定在DOM元素上的事件處理程式繫結在一起(例如,當瀏覽器在某個DOM元素上觸發了點選事件,函式就會被呼叫)。和前面的例子類似,繫結看起來是這樣的:

<div ng-controller="FirstController">
<h4>The simplest adding machine ever</h4>
<button ng-click="add(1)" class="button">Add</button>
<a ng-click="subtract(1)" class="button alert">Subtract</a>
<h4>Current count: {{ counter }}</h4>
</div>

按鈕和連結都被繫結在了內部$scope的一個操作上,當點選任何一個元素時AngularJS都會呼叫相應的方法。注意,當設定呼叫哪個函式時,會同時用括號傳遞一個引數(add(1))。
下面給FirstController新增一個操作:

app.controller('FirstController', function($scope) {
$scope.counter = 0;
$scope.add = function(amount) { $scope.counter += amount; };
$scope.subtract = function(amount) { $scope.counter -= amount; };
});

控制器可以將與一個獨立檢視相關的業務邏輯封裝在一個獨立的容器中。儘可能地精簡控制器是很好的做法。作為AngularJS開發者,使用依賴注入來訪問服務可以實現這個目的。

AngularJS同其他JavaScript框架最主要的一個區別就是,控制器並不適合用來執行DOM操作、格式化或資料操作,以及除儲存資料模型之外的狀態維護操作。它只是檢視和$scope之間的橋樑。

控制器巢狀(作用域包含作用域)

預設情況下,AngularJS在當前作用域中無法找到某個屬性時,便會在父級作用域中進行查詢。如果AngularJS找不到對應的屬性,會順著父級作用域一直向上尋找,直到抵達$rootScope為止。如果在rootScope中也找不到,程式會繼續執行,但檢視無法更新。

通過例子來看一下這個行為。建立一個ParentController,其中包含一個user物件,再建立一個ChildController來引用這個物件:

app.controller('ParentController', function($scope) {
$scope.person = {greeted: false};
});
app.controller('ChildController', function($scope) {
$scope.sayHello = function() {
$scope.person.name = 'Ari Lerner';
};
});

如果我們將ChildController置於ParentController內部,那ChildController的$scope物件的父級作用域就是ParentController的scope物件。根據原型繼承的機制,我們可以在子作用域中訪問ParentController的scope物件。

例如,我們可以在ChildController的DOM元素中訪問定義在ParentController中的person物件

<div ng-controller="ParentController">
<div ng-controller="ChildController">
<a ng-click="sayHello()">Say hello</a>
</div>
{{ person }}
</div>

我們看到,點選按鈕時,可以在ChildController中訪問ParentController中$scope.person的值,就好像person物件定義在ChildController的scope中一樣。
這裡寫圖片描述

控制器應該儘可能保持短小精悍,而在控制器中進行DOM操作和資料操作則是一個不好的實踐。

設計良好的應用會將複雜的邏輯放到指令和服務中。通過使用指令和服務,我們可以將控制器重構成一個輕量且更易維護的形式
簡潔的控制器:

angular.module('myApp', [])
.controller('MyController', function($scope,UserSrv) {
// 內容可以被指令控制
$scope.onLogin = function(user) {
UserSrv.runLogin(user);
};
});

表示式

前面已經見過使用表示式的示例。用{{ }}符號將一個變數繫結到$scope上的寫法本質上就是一個表示式:{{ expression }}。當用watch進行監聽時,AngularJS會對錶達式或函式進行運算。
表示式和eval(javascript)非常相似,但是由於表示式由AngularJS來處理,它們有以下顯著不同的特性:

  1. 所有的表示式都在其所屬的作用域內部執行,並有訪問本地$scope的許可權;
  2. 如果表示式發生了TypeError和ReferenceError並不會丟擲異常;
  3. 不允許使用任何流程控制功能(條件控制,例如if/else);
  4. 可以接受過濾器和過濾器鏈。

對錶達式進行的任何操作,都會在其所屬的作用域內部執行,因此可以在表示式內部呼叫那些限制在此作用域內的變數,並進行迴圈、函式呼叫、將變數應用到數學表示式中等操作。

解析AngularJS 表示式

儘管AngularJS會在執行$digest迴圈的過程中自動解析表示式,但有時手動解析表示式也是非常有用的。

AngularJS通過$parse這個內部服務來進行表示式的運算,這個服務能夠訪問當前所處的作用域。

將$parse服務注入到控制器中,然後呼叫它就可以實現手動解析表示式。舉例來說,如果頁面上有一個輸入框繫結到了expr變數上,如下所示:

<div ng-controller="MyController">
<input ng-model="expr" type="text" 
placeholder="Enter an expression" />
<h2>{{ parseValue }}</h2>
</div>

我們可以在MyController中給expr這個表示式設定一個$watch並解析它:

angular.module("myApp", [])
.controller('MyController',
function($scope,$parse) {
$scope.$watch('expr', function(newVal, oldVal, scope) {
if (newVal !== oldVal) {
// 用該表示式設定parseFun
var parseFun = $parse(newVal);
// 獲取經過解析後表示式的值
$scope.parsedValue = parseFun(scope);
}
});
});

插值字串

在AngularJS中,我們的確有手動執行模板編譯的能力。例如,插值允許基於作用域上的某個條件實時更新文字字串。

要在字串模板中做插值操作,需要在你的物件中注入$interpolate服務。在下面的例子中,我們將會將它注入到一個控制器中:

angular.module('myApp', []).controller('MyController',
function($scope, $interpolate) {
// 我們同時擁有訪問$scope$interpolate服務的許可權
});

$interpolate服務是一個可以接受三個引數的函式,其中第一個引數是必需的。

  1. text(字串):一個包含字元插值標記的字串。
  2. mustHaveExpression(布林型):如果將這個引數設為true,當傳入的字串中不含有表示式時會返回null。
  3. trustedContext(字串):AngularJS會對已經進行過字元插值操作的字串通過$sec.getTrusted()方法進行嚴格的上下文轉義。

$interpolate服務返回一個函式,用來在特定的上下文中運算表示式。
設定好這些引數後,就可以在控制器中進行字元插值的操作了。例如,假設我們希望可以在電子郵件的正文中進行實時編輯,當文字發生變化時進行字元插值操作並將結果展示出來。

<div ng-controller="MyController">
<input ng-model="to"
type="email"
placeholder="Recipient" />
<textarea ng-model="emailBody"></textarea>
<pre>{{ previewText }}</pre>
</div>

由於控制器內部設定了一個需要每次變化都重新進行字元插值的自定義輸入欄位,因此需要設定一個$watch來監聽資料的變化。

簡而言之,$watch函式會監視scope上的某個屬性。只要屬性發生變化就會呼叫對應的函式。可以使用watch函式在scope上某個屬性發生變化時直接執行一個自定義函式。

在控制器中,我們設定了$watch來監視郵件正文的變化,並將emailBody屬性的值進行字元插值後的結果賦值給previewText屬性。

angular.module('myApp', [])
.controller('MyController', function($scope, $interpolate) {
// 設定監聽
$scope.$watch('emailBody', function(body) {
if (body) {
var template = $interpolate(body);
$scope.previewText =
template({to: $scope.to});
}
};
});
在

現在,在{{ previewText }}內部的文字中可以將{{ to }}當做一個變數來使用,並對文字的變化進行實時更新。

用startSymbol()方法可以修改標識開始的符號。這個方法接受一個引數。

用endSymbol()方法可以修改標識結束的符號。這個方法也接受一個引數。

如果要修改這兩個符號的設定,需要在建立新模組時將$interpolateProvider注入進去。

angular.module('emailParser', [])
.config(['$interpolateProvider', function($interpolateProvider) {
$interpolateProvider.startSymbol('__');
$interpolateProvider.endSymbol('__');
}])
.factory('EmailParser', ['$interpolate', function($interpolate) {
// 處理解析的服務
return {
parse: function(text, context) {
var template = $interpolate(text);
return template(context);
}
};
}]);

現在,我們已經建立了一個模組,可以將它注入到應用中,並在郵件正文的文字中執行這個郵件解析器:

angular.module('myApp', ['emailParser'])
.controller('MyController', ['$scope', 'EmailParser',
function($scope, EmailParser) {
// 設定監聽
$scope.$watch('emailBody', function(body) {
if (body) {
$scope.previewText = EmailParser.parse(body, {
to: $scope.to
});
}
});
}]);

現在用自定義的 __ 符號取代預設語法中的 {{ }} 符號來請求插值文字。
由於我們將表示式開始和結束的符號都設定成了__,因此需要將HTML修改成用這個符號取代{{ }}的版本,

<div id="emailEditor">
<input ng-model="to"
type="email"
placeholder="Recipient" />
<textarea ng-model="emailBody"></textarea>
</div>
<div id="emailPreview">
<pre>__ previewText __</pre>
</div>

這裡寫圖片描述

相關推薦

AngularJS自學()——控制器表示式

控制器 控制器在AngularJS中的作用是增強檢視。 AngularJS中的控制器是一個函式,用來向檢視的作用域中新增額外的功能。我們用它來給作用域物件設定初始狀態,並新增自定義行為。 當我們在頁面上建立一個新的控制器時,AngularJS會生成並傳遞一

Python自學【第一篇】:Python簡介入門

youtube 通用 too 互聯網公司 python腳本 bar strong 重裝 排行 Python前世今生 python的創始人為吉多·範羅蘇姆(Guido van Rossum)。1989年的聖誕節期間,吉多·範羅蘇姆為了在阿姆斯特丹打發時間,決心開發一個新的腳本

【python3的學習】字符串編碼

而且 亂碼 \n spa 結果 雙引號 gb2312 span 大小寫 字符串編碼 由於計算機是美國人發明的,因此,最早只有127個字符被編碼到計算機裏,也就是大小寫英文字母、數字和一些符號,這個編碼表被稱為ASCII編碼,比如大寫字母A的編碼是65,小寫字母z

Python自學:遞迴、棧佇列遍歷目錄

遞迴呼叫的概念:一個函式呼叫了自身,稱為遞迴呼叫 遞迴函式的概念:一個能夠被自身呼叫的函式稱為遞迴函式 遞迴一般執行的是迴圈邏輯 編寫遞迴函式的方法: 1、找出臨界條件,比如最小值,最大值等等 2、找出這一次和上一次的關係 3、假設當前函式已經能用,呼叫自身計算

golang自學(Printf格式化輸出語句的常用方法控制檯輸入)2

package main import "fmt" func main() { a,b:=123,321 c,d:="dsds",5.5 fmt.Print("你好,世界!")//輸出控制檯 fmt.Println(a)//輸出控制檯後自動換行 fmt.Printf

Java自學-Java中級教程-5:Spring元件物件註解@Component屬性值註解@Value

除了註解@Autowired,還可以使用@Component註解,這個註解甚至可以把beans.xml中的bean全部省略。 如上,把beans.xml中的bean全部使用註釋符號註釋掉,或者直接把所有的bean刪除都可以。同時,Person、Head、Foot

angularJS學習十)---自定義指令---templateUrl

第一種寫法: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title&

Java自學-Java基礎教程-42:Java的UI介面AWTSwing元件

在學Java的初期,其實很不情願地學,這面向物件的語言還是和C語言似的,沒有什麼介面的呢,也不能在桌面上執行呢。能不能像電腦裡的桌面軟體一樣,雙擊就可以開啟?能不能像其他桌面軟體一樣,可以輸入文字,可以有按鈕或選單什麼的?能不能搞出一個滑鼠點點就能用的軟體出來?其實Java也

Java自學-Java中級教程-18:SpringMVC列表框select單選框radio、多選框checkbox的處理

頁面表單還有很多控制元件,這一節介紹比較常用的列表框和單選框、多選框。列表框就是下拉列表選項,用在有多個選擇的情況,比如一大堆的國家和城市可以做成一個下拉列表供使用者選擇。單選框用在只能選擇一種選項的情況,比如性別要麼選男,要麼選女。多選框即是可以多選,比如興趣愛好可以選電腦

Java自學-Java中級教程-12:SpringMVC的層架構:模型層、表現層、控制層

MVC即Model、View、Controller三者的縮寫。Model為模型層,View為表現層,Controller為控制層。其中M處於最底層,V在最上層,中間層為Controller。比如使用者訪問網站,首先接觸的是View,即是網頁。通過訪問網頁的url,就會傳到Co

Spring框架自學

(03Day)         (1)Bean的自動裝配             在前面的例子中我們為Bean裝配Bean很明顯都是手動裝配的,那麼既然有手動裝配那有沒有自動裝配呢?顯然是有的。那麼自動裝配又該如何使用呢?其實很簡單,來看看吧。            

【我的前端自學】【HTML5】.html.htm的區別

的區別 文件命名 web soc tps 技術 分享 log 限制 以下為自學筆記內容,僅供參考。 轉發請保留原文鏈接:https://www.cnblogs.com/it-dennis/p/10508171.html .htm 和 .html 的區別 .htm 和 .

我的Linux自學

記錄學習 作為一只菜鳥,並且沒有任何Linux的經驗,我從網上下載的視頻自學Linux。歡迎各位新手或大師指點,開通博客意在記錄自己學習的點滴。 由於項目在公安行業內,系統的版本無法跟現代的大型網後臺的系統版本相提並論。公安系統的版本大多停留在四五年前,因為是自學,下載的視頻也是四五年前的,所以後

java自學-day06

不同的 output 變量賦值 使用 計算 數列 nic 管理 center JAVA06 引用數據類型 數組 類 接口 也是一種引用類型 用於定義屬性和功能對現實中的事物的描述 例如 學生類的定義 格式 自定義數據類型 public calss 類名{ 屬性和方法

java自學-day11

局限 局限性 浮點運算 漢字 虛擬機 trac 檢查 copy intvalue JAVA11 正則表達式 l 概念 是一個字符串 滿足一定的規則 qq號碼檢查 [1-9] [0-9] [4,9] 檢查某些字符是否合規 例如 用戶名是否合規 一個[]代表一個或者多個

Python-目錄規範不同目錄間進行模塊調用

base 轉換成 arm 環境變量 規範 有環 路徑 pytho package 目錄規範: 預備知識: 要實現不同目錄間進行模塊調用必須在當前文件夾中創建一個空的__init__.py的文件(pycharm會在創建python package的時候自動創建),有

java自學-day18

收費 基本查詢 sqlite 他還 select 找到 之前 合計 表名 數據庫 數據庫 概念 l 對於一串數據修改其中的一條數據 l 如果用io流就需要全盤讀寫 然後找到其中的一行 進行修改 l 或者使用數組進行修改 l 這樣太復雜 效率慢 所以需要引用數據庫 l

python語言的自學

python 之前粗略看過一次python的語言,語法簡單。由於時間過了太久,好像有兩年了吧(之前就是純粹想知道python是什麽才看的),也不記得什麽了,只是記得它沒有c語言和java語言難就是了。 根據這個網站中的資料可以很快入手python:http://www.runoob.c

python語言的自學3

python正則匹配裏面方法分為兩種使用方法:方法1:import restr = ‘i am a bad hero‘re.search(‘am’,str)re.match(‘am’,str)re.findall(‘am’,str)re.finditer(‘am’,str)方法2:import restr =

java自學-day21

產生 方式 方法 可變參 main方法 ext 都是 破壞 類文件 JAVA21 類加載器 類的加載 l 當程序要使用某個類時,如果還沒被加載到內存中,系統會就會通過加載 連接 初始化三個步驟對這個類進行初始化 l 加載 類的加載器將class文件讀入內存,放入方法區