# **[Chapter 3—Application Logic ](http://docs.nativescript.org/tutorial/chapter-3#chapter-3application-logic)**應用邏輯
在這一章中,您將學習如何添加JavaScript邏輯到你的nativescript APP,你會使用構建 NativeScript 框架的基本模式, MVVM ,或是“ **Model** **View** **ViewModel** ”。下面是這些詞的意思:
* **Model(模型):**模型定義和表示數據。將模型從各種可能使用的視圖中分離出來,就可以實現代碼重用。
* **View(視圖):**視圖代表UI,在 NativeScript 是寫在XML文件里的。視圖常與視圖模型數據綁定,使得在JavaScript里對視圖模型作的改動立即觸發UI組件的視覺效果變化。
* **View Model(視圖模型):** 視圖模型包含應用邏輯(通常包括模型),并把數據暴露給視圖。NativeScript提供一個叫做“ **Observable** ”的模塊,這有助于創建一個可以綁定到視圖的視圖模型對象。
獨立劃分模型、視圖和視圖模型的最大好處是,你可以使用雙向數據綁定;就是說,模型里面的數據變化會立即影響反應在視圖上,反之亦然。另一個好處是代碼重用,所以你經常可以跨視圖使用模型和視圖。
雜貨店里,目前你只接觸了視圖 \(`login.xml`\),在本章你將要創建一個視圖模型。為此,我們首先需要向你介紹另外一種文件: **code-behind**(代碼后置或是代碼隱藏技術)。
## [**目錄**](http://docs.nativescript.org/tutorial/chapter-3#table-of-contents)
* [3.1: The code-behind后臺代碼](#code-behind)
* [3.2: Navigating screens 導航頁面](#navigating-screens)
* [3.3: Accessing UI components 獲取UI組件](#accessing-ui-components)
* [3.4: Adding a view model 添加一個視圖模型](#34)
## **3.1: 后臺代碼** {#code-behind}
NativeScript里后臺代碼文件就是一份和視圖有相同名字的JavaScript文件。比如,登錄頁面的視圖叫作 `login.xml`,那么它的 臺代碼 文件就叫作 `login.js`。代碼后置文件是你放置所有與視圖本身進行交互的代碼的地方。
我們通過一個例子來看你在后臺代碼文件里能做什么。
> ### **操作: **構建登錄視圖模型
>
> ---
>
> 打開`login.xml` 在最上面添加一個`loaded`屬性到`<Page>`UI組件。 就像這樣:
>
> `<Page loaded="loaded">`
>
> 然后,在`app/views/login/login.js`里粘貼下面的代碼以定義一個`loaded()`函數:
>
> `exports.loaded = function() {`
>
> `console.log("hello");`
>
> `};`
>
> ### 提示:
>
> ---
>
> ** 關鍵字**`exports`**隸屬于\*\***[CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1)****, 在****[CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1)****這個標準基礎上 NativeScript 和 Node.js 才實現了模塊。在基于 ****[CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1)**\*\* 的 JavaScript 模塊里,名為**`exports`**的自由變量是一個對象,一個模塊可以向它添加屬性和方法來配置外部 API 。在**后臺代碼**文件里使用** `exports` 暴露方法便于在視圖(XML文件)中使用。即是,后臺代碼文件里定義的 `exports.loaded` 才讓視圖里的 `loaded="loaded"` 發生作用。
更改之后運行app, NativeScript 觸發了你在后臺代碼文件里創建的 `loaded()` 方法,就會在命令行記錄里看到“hello”。

這個簡單的例子展示了你如何能向UI組件添加屬性來運行視圖的伴生JS文件里的方法。我們來添加這些屬性里的另外一個: `tap` 。
> ### **操作: 激活按鈕**
>
> ---
>
> **你可以添加一個能在用戶點擊或觸摸按鈕時觸發的 **`tap`** 屬性。在**`app/views/login/login.xml`**文件里,替換屏幕底部的兩個按鈕為使用此標記:**
>
> `<Button text="Sign in" tap="signIn" />`
>
> `<Button text="Sign up for Groceries" class="link" tap="register" />`
>
> **接著,在**`app/views/login/login.js`** 文件底部,粘貼下面的**`signIn()`** and**`register()`** 方法:**
>
> `exports.signIn = function() {`
>
> `alert("Signing in");`
>
> `};`
>
> `exports.register = function() {`
>
> `alert("Registering");`
>
> `};`
到此, 如果你運行app 并點擊任一個按鈕,你會看到相應的警告框彈出:

既然你能看到點擊手勢生效,那就讓它們做一些比彈出警告更有意思的事情吧。
## **[3.2: Navigating screens](http://docs.nativescript.org/tutorial/chapter-3#32-navigating-screens)**導航頁面 {#navigating-screens}
當你點擊 “Sign up for Groceries” 按鈕,你期望一個導航切換到注冊頁面。這在 NativeScript 里是非常簡單就辦到的。
> ### **操作: 實現登錄頁面的** **“Sign up”** 按鈕的導航切換
>
> ---
>
> 在 `app/views/login/login.js`,添加這行到文件頂部:
>
> `var frameModule = require("ui/frame");`
>
> 然后, 用下面的版本替換現有的 `register()`方法:
>
> `exports.register = function() {`
>
> `var topmost = frameModule.topmost();`
>
> `topmost.navigate("views/register/register");`
>
> `};`
該方法使用了 [frame 模塊](http://docs.nativescript.org/api-reference/modules/_ui_frame_.html), 這個 NativeScript 模塊在你的app里 負責導航。現在,你通知頂層 frame ,或者用戶看到的 frame ,導航到注冊視圖。
如果你運行app并點擊 “Sign up for Groceries” 按鈕,你會被帶到我們已經為你預先搭建好的 注冊界面。

既然你能進入注冊界面,就跟著注冊一個賬戶用于后面的的教程。
> ### **操作: 注冊一個賬戶**
>
> ---
>
> **打開雜貨店點擊 “Sign up for Groceries” 按鈕進入注冊頁面。填寫email地址和密碼,點擊 “Sign up” 按鈕創建一個賬戶。**
>
> **你可以使用一個虛假的郵件地址和密碼,一定記住你的登錄憑據,因為后面會用到。**
>
> ### 提示:
>
> ---
>
> **雖然我們的雜貨店app不使用復雜的導航策略,你還有幾種可能來跳出這個框框,比如\*\***[TabView](http://docs.nativescript.org/ui-views#tabview)**** 和****[SegmentedBar](http://docs.nativescript.org/ui-views#segmentedbar)****。 SideDrawer 組件也可免費用在 Telerik公司的 ****[UI for NativeScript ](http://docs.telerik.com/devtools/nativescript-ui/introduction)**\*\*產品上。**
## **[3.3: Accessing UI components](http://docs.nativescript.org/tutorial/chapter-3#33-accessing-ui-components)**獲取UI組件 {#accessing-ui-components}
是時候看下數據如何在前段和后端之間來回流動的形式了。
### 操作:從前端傳遞數據到視圖模型
---
> 打開 `login.xml` 向email文本域添加一個 `id="email"` 屬性 。它的標記應該看起來像這樣:
>
> `<TextField id="email" hint="Email Address" keyboardType="email" autocorrect="false" autocapitalizationType="none" />`
>
> 里面有一個 `id` 屬性就位,你可以在你的后臺代碼文件獲取到這個 `TextField` 。為此,先打開`app/views/login/login.js` 然后在該文件頂部添加下面兩行代碼, 緊跟著 `frameModule` 的下面。
>
> `var page;`
>
> `var email;`
>
> 接著, 在 `login.js` 里編輯`loaded()` 方法獲得當前頁面的引用:
>
> `exports.loaded = function(args) {`
>
> `page = args.object;`
>
> `};`
>
> ### **提示:**
>
> ---
>
> **能這樣做是因為NativeScript在方法參數里傳遞了 一個**`<Page>`** 的引用 給 **`loaded`** 事件句柄,參數按慣例叫作 **`args`** 。**
最后,編輯 `signIn()` 方法獲取文本域 **TextField** 組件的引用并log它的內容:
`exports.signIn = function() {`
`email = page.getViewById("email");`
`console.log(email.text);`
`};`
所有的 NativeScript UI組件,包括 `<Page>`,都繼承于 `View` 類,這賦予它們很多操作UI的方法。在本例你使用 `getViewById()` 方法通過其 `id` 屬性來獲取email文本域的引用。
要看看這是如何實際工作的,啟動app,在email文本域輸入一些文本,然后點擊 “Sign in” 按鈕。如果一切正常,你會看到輸入的文本記錄在你的命令行了。

通過在 JavaScript 里獲取UI組件,你能操作這些組件在前端的外觀和行為。然而,單個地獲取這些UI組件是非常手動的程序,也使得跟蹤UI狀態比較困難。這里就該**視圖模型**出馬了。
## [**3.4: Adding a view model**](http://docs.nativescript.org/tutorial/chapter-3#34-adding-a-view-model)添加一個視圖模型 {#34}
NativeScript 通過一個叫做 'Observable'的模塊 提供**視圖模型**功能 。
**Observable**就是** MVVM** 設計模式里的**視圖模型**。它提供了一個機制用于雙向數據綁定,能夠讓UI和后臺代碼文件直接通訊。這意味著如果用戶改變了UI里的數據,這個改變會自動反饋到視圖模型,反之亦然。
### **操作: 創建一個視圖模型并綁定到視圖**
要實現雙向數據綁定需要使用 Observable ,打開 `login.xml` ,把現有的 TextField UI組件替換為下面的兩個,每個都包含一個新的 `text` 屬性:
> `<TextField id="email" text="{{ email }}" hint="Email Address" keyboardType="email" autocorrect="false" autocapitalizationType="none" />`
>
> `<TextField secure="true" text="{{ password }}" hint="Password" />`
>
> ### 注意:
>
> 兩對花括號包裹 `text` 屬性值的用法界定了一個數據綁定值。你將在視圖模型里用同樣的名字設置相應的屬性。
添加下面的代碼到 `app/views/login/login.js` 頂部。**該代碼從 observable 模塊獲得了 **`Observable`** 構造器的引用,并調用構造器定義了一個新的**`user`**對象,這就是你在本頁使用到的視圖模型**:
> `var Observable = require("data/observable").Observable;`
>
> `var user = new Observable({`
>
> `email: "user@domain.com",`
>
> `password: "password"`
>
> `});`
現在,用下面的代碼替換現有的 `loaded()`方法,它 `user` 設置為該頁面綁定的上下文。
> `exports.loaded = function(args) {`
>
> `page = args.object;`
>
> `page.bindingContext = user;`
>
> `};`
剛才發生了什么?
1. 你正在基于 NativeScript的 Observable 模塊創建一個 `user` 視圖模型。你創這個視圖模型帶有兩個屬性: `email` and `password`,它們預先設置了些雜亂的值。
2. 你通過設置page的 `bindingContext` 屬性將pege綁定到`user`視圖模型。這就明確地讓花括號語法生效了。
簡言之,放置在page的 binding context 里的屬性可以由XML元素使用{ {屬性名} }語法獲取到。因為 JavaScript 設置了視圖模型的 `email` 為 `"user@domain.com"` ,又因為你使用 `<TextField text="{{ email }}">` 綁定了這個email地址 text field 到屬性,所以運行app就會看到 "user@domain.com" 出現在前端。

真正酷的是這個綁定是雙向的。這意味著,用戶在文本域輸入文本的時候,這些改動會立即反映到你的視圖模型。
要使用這些值,并通過你的app 聯系上后臺服務讓這個登錄有用,你將需要發起 HTTP請求的能力。在NativeScript里發起 HTTP 就使用fetch模塊。我們就來看下 NativeScript模塊如何工作的。