Google Code Prettify

顯示具有 Javascript 標籤的文章。 顯示所有文章
顯示具有 Javascript 標籤的文章。 顯示所有文章

在部分檢視中為何document.getElementsByTagName 只能取得undefined

在部分視圖(Partial View)中使用 document.getElementsByTagName 無法正確獲取元素時,可能的原因有以下幾種:

DOM 還未準備好:如果你嘗試在 DOM 尚未完全加載時訪問元素,可能會出現 undefined。
範圍問題:在 AJAX 加載部分視圖時,新的元素可能尚未添加到 DOM 中,導致選擇器無法找到它們。
元素不存在:確保你的部分視圖包含了你想要選擇的元素。
要解決這些問題,可以使用 jQuery 的 ready 方法或在 AJAX 請求完成後運行你的 JavaScript 代碼。以下是一個完整的範例:



1. HTML 和 JavaScript
假設我們有一個部分視圖 _PartialView.cshtml,其中包含一些元素:

_PartialView.cshtml
html
<div id="partial-content">
    <p>部分視圖的內容</p>
    <p>更多內容</p>
</div>




主視圖 (Index.cshtml) 在主視圖中,我們有一個按鈕,用於觸發 AJAX 加載部分視圖,並在成功加載後操作部分視圖中的元素: html
@{
    ViewData["Title"] = "Home Page";
}

<!DOCTYPE html>
<html>
<head>
    <title>@ViewData["Title"]</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>
<body>
    <h1>首頁</h1>
    <button id="load-partial" class="btn btn-primary">加載部分視圖</button>
    <div id="main-content"></div>

    <script>
        $(document).ready(function () {
            $('#load-partial').click(function () {
                $.ajax({
                    url: '@Url.Action("GetPartialView", "Home")',
                    type: 'GET',
                    success: function (result) {
                        $('#main-content').html(result);

                        // 確保在部分視圖加載完成後運行代碼
                        var paragraphs = document.getElementsByTagName('p');
                        console.log(paragraphs); // 應該能夠正確獲取元素

                        // 如果要使用 jQuery
                        // var paragraphs = $('#main-content').find('p');
                        // console.log(paragraphs);
                    },
                    error: function (xhr, status, error) {
                        console.error('Error loading partial view:', status, error);
                    }
                });
            });
        });
    </script>
</body>
</html>



2. 控制器 (HomeController.cs)
確保控制器返回部分視圖:

csharp
using Microsoft.AspNetCore.Mvc;

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [HttpGet]
    public IActionResult GetPartialView()
    {
        return PartialView("_PartialView");
    }
}



總結
這樣做可以確保在部分視圖加載完成後,元素存在於 DOM 中,並且 JavaScript 可以正確地選擇和操作這些元素。使用 jQuery 的 success 回調來運行代碼,可以確保操作僅在部分視圖完全加載並添加到 DOM 中後進行。

document.getElementsByTagName 取回的資料要如何區分識別

document.getElementsByTagName 返回的是一個 HTMLCollection,這個集合可以通過索引訪問到特定的元素。要區分和識別從 document.getElementsByTagName 返回的元素,可以使用以下幾種方法:

使用索引:可以通過索引直接訪問 HTMLCollection 中的元素。
檢查元素的屬性:如 id、class 等屬性,這些屬性可以幫助識別特定的元素。
使用 for 迴圈或 Array.prototype.forEach:迭代 HTMLCollection,並根據元素的屬性進行操作。
以下是一個具體範例:

範例 HTML 和部分視圖

_PartialView.cshtml
<div id="partial-content">
    <p id="para1">第一個段落</p>
    <p class="highlight">第二個段落</p>
    <p id="para3">第三個段落</p>
</div>




主視圖 (Index.cshtml)
在主視圖中,我們加載部分視圖並在成功加載後區分和識別段落元素:

html
@{
    ViewData["Title"] = "Home Page";
}

<!DOCTYPE html>
<html>
<head>
    <title>@ViewData["Title"]</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>
<body>
    <h1>首頁</h1>
    <button id="load-partial" class="btn btn-primary">加載部分視圖</button>
    <div id="main-content"></div>

    <script>
        $(document).ready(function () {
            $('#load-partial').click(function () {
                $.ajax({
                    url: '@Url.Action("GetPartialView", "Home")',
                    type: 'GET',
                    success: function (result) {
                        $('#main-content').html(result);

                        // 獲取所有段落元素
                        var paragraphs = document.getElementsByTagName('p');

                        // 使用 for 迴圈遍歷所有段落
                        for (var i = 0; i < paragraphs.length; i++) {
                            var paragraph = paragraphs[i];
                            console.log(paragraph);

                            // 根據 id 進行操作
                            if (paragraph.id === 'para1') {
                                paragraph.style.color = 'red';
                            } else if (paragraph.classList.contains('highlight')) {
                                paragraph.style.color = 'blue';
                            } else {
                                paragraph.style.color = 'green';
                            }
                        }
                    },
                    error: function (xhr, status, error) {
                        console.error('Error loading partial view:', status, error);
                    }
                });
            });
        });
    </script>
</body>
</html>




分析
按索引訪問:可以通過索引直接訪問 HTMLCollection 中的元素,如 paragraphs[0] 代表第一個段落。
檢查屬性:使用 id 和 class 屬性來區分和識別元素。在這個例子中,我們使用了 id 屬性來改變第一個段落的顏色,使用了 class 屬性來改變具有 highlight 類的段落的顏色。
使用迴圈:使用 for 迴圈遍歷 HTMLCollection 中的所有段落,並根據元素的屬性進行不同的操作。
這樣可以確保在部分視圖加載完成後,能夠正確地區分和識別特定的元素並進行操作。

ASP.NET Core 8 有那些方法可以開新窗口

在 ASP.NET Core 8 中,開啟新窗口(或標籤頁)的方式主要依賴於客戶端技術,如 JavaScript 和 HTML。以下是一些常見的方法來實現這一功能:

1. 使用 HTML 的 target="_blank"
最簡單的方法是使用 HTML 中的 target 屬性將連結設定為在新窗口(或標籤頁)中打開。

html
<a href="https://www.example.com" target="_blank">Open in new window</a>

這種方法是最基本的,只需在 標籤中添加 target="_blank" 即可。




2. 使用 JavaScript 的 window.open()
JavaScript 提供了更靈活的方式來打開新窗口,可以控制窗口的大小、位置、屬性等。

示例:基本用法
html
<button onclick="openNewWindow()">Open New Window</button>

<script>
function openNewWindow() {
    window.open('https://www.example.com', '_blank');
}
</script>


示例:更多控制選項
html
<button onclick="openNewWindow()">Open New Window</button>

<script>
function openNewWindow() {
    window.open('https://www.example.com', '_blank', 'width=600,height=400,scrollbars=yes,resizable=yes');
}
</script>


這個例子中,window.open 的第三個參數允許設置窗口的大小和特性。


3. 在 ASP.NET Core 中生成帶有 target="_blank" 的鏈接
在 Razor 視圖中,您可以動態生成帶有 target="_blank" 的鏈接。例如:

html
@{
    var url = "https://www.example.com";
}

<a href="@url" target="_blank">Open in new window</a>

這樣可以根據需要動態生成不同的 URL。



4. 在 Razor Pages 或 MVC 操作中返回 JavaScript
有時候,您可能希望在操作中返回 JavaScript 來打開新窗口。例如,當用戶執行某個操作後,自動打開一個新窗口。您可以在控制器或 Razor Page 中返回一個包含 JavaScript 的視圖。

在控制器中返回 JavaScript
csharp
public IActionResult OpenNewWindow()
{
    string script = "";
    return Content(script, "text/html");
}

在 Razor Page 中返回 JavaScript

csharp

public IActionResult OnGetOpenNewWindow()
{
    string script = "";
    return new ContentResult
    {
        Content = script,
        ContentType = "text/html"
    };
}



總結
在 ASP.NET Core 8 中,開啟新窗口的主要方法包括:

HTML 的 target="_blank":最簡單的方式,適合靜態鏈接。
JavaScript 的 window.open():提供更多控制和動態行為。
Razor 視圖中的動態鏈接生成:根據需要動態生成鏈接。
在控制器或 Razor Page 中返回 JavaScript:在特定操作後自動打開新窗口。
這些方法可以根據不同的需求來選擇和使用,以實現開啟新窗口的功能。

[MVVM]knockoutjs

官網: http://knockoutjs.com/index.html
下載: http://knockoutjs.com/downloads/index.html
互動式教程: http://learn.knockoutjs.com/#/?tutorial=intro
示例: http://knockoutjs.com/examples/
文件: http://knockoutjs.com/documentation/introduction.html



knockout.js
可幫助您使用 Model-View-ViewModel(MVVM)模式簡化動態 JavaScript UI。


Knockout是一個JavaScript庫,可幫助您使用乾淨的底層數據模型創建豐富的響應式顯示和編輯器用戶界面。 只要您有動態更新的UI部分(例如,根據用戶的操作或外部數據源更改而更改),KO可以幫助您更簡單,更可維護地實施它。


優雅的依賴關係跟踪 
在數據模型發生變化時自動更新UI的正確部分

聲明性綁定
將UI的各個部分連接到數據模型的簡單明了的方法。 您可以使用任意嵌套的綁定上下文輕鬆構建複雜的動態UI

簡單可擴展
將自定義行為實現為新的聲明性綁定,以便在幾行代碼中輕鬆重用。

純JavaScript庫 
適用於任何服務器或客戶端技術,可以添加到現有Web應用程序之上,而無需進行重大架構更改

緊湊 
gzipping 後約 13kb

適用於任何主流瀏覽器
IE 6 +,Firefox 2 +,Chrome,Safari,Edge等

全面的規範套件
開發的BDD風格,意味著它可以在新的瀏覽器和平台上輕鬆驗證其正確的功能


熟悉 Ruby on Rails,ASP.NET MVC 或其他 MV * 技術的開發人員可能會將 MVVM 視為具有聲明性語法的 MVC 實時形式。 在另一種意義上,您可以將 KO 視為製作用於編輯 JSON 數據的 U I 實作方法。



Bower

Knockout 可通過 Bower 包管理器獲得。 在 http://bower.io 上了解有關使用和安裝 Bower 的更多信息。 要將 Knockout 添加到您的應用程序,您可以運行:

     bower install knockoutckoutb



安裝

只需在 HTML 頁面的某處使用 <script> 標記引用 JavaScript 文件即可。 例如,

     <script type='text/javascript' src='knockout-3.4.2.js'></script>

你可以更新 src 屬性以匹配下載文件的位置。

接下來,如果您是Knockout的新手,請開始使用交互式教程,查看一些實時示例,或深入了解有關可觀察對象的文檔。



第三方 CDN 引用

為了給您的用戶提供最快的下載速度,您可以考慮在第三方 CDN 上引用 knockout.js:

CDNJS :
     https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js

Microsoft Ajax CDN :
     http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.4.2.js





使用observable創建視圖模型

Knockout 三個核心構建功能:
  1. 可觀察量和依賴性跟踪
  2. 聲明性綁定
  3. 模板


在此頁面上,您將了解這三個中的第一個。 但在此之前,讓我們來看看MVVM模式和視圖模型的概念。


Model-View-View Model (MVVM) 是用於構建用戶界面的設計模式。 它描述瞭如何通過將可能複雜的 UI 分為三個部分來簡化它們:


模型 (Model):
應用程序的存儲數據。此數據表示業務域中的對象和操作(例如,可以執行匯款的銀行帳戶),並且獨立於任何 UI。使用 KO 時,通常會對某些服務器端代碼進行 Ajax 調用,以讀取和寫入此存儲的模型數據。

視圖模型 (View Model):
UI 上數據和操作的純代碼表示。例如,如果要實現列表編輯器,則視圖模型將是一個包含項列表的對象,並公開添加和刪除項的方法。

請注意
這不是 UI 本身:它沒有按鈕或顯示樣式的任何概念。它也不是持久數據模型 - 它保存用戶正在使用的未保存數據。使用 KO 時,您的視圖模型是純 JavaScript objects,不了解 HTML 。以這種方式保持視圖模型的抽象使其保持簡單,因此您可以管理更複雜的行為而不會丟失。

視圖(View):
表示視圖模型狀態的可見交互式 UI 。它顯示來自視圖模型的信息,將命令發送到視圖模型(例如,當用戶單擊按鈕時),並且每當視圖模型的狀態改變時更新。

使用KO時,您的視圖只是具有聲明性綁定的 HTML 文檔,以將其鏈接到視圖模型。或者,您可以使用視圖模型中的數據生成 HTML 的模板。


View Model
var myViewModel = {
    personName: 'Bob',
    personAge: 123
};

ko.applyBindings(myViewModel);


View
The name is <span data-bind="text: personName"></span>




applyBindings 的參數:
第一個參數是 View Model 物件與它激活的聲明性綁定
第二個參數(可選)定義要搜索數據綁定屬性的文檔的哪個部分。 如果您希望擁有多個視圖模型並將每個視圖模型與頁面的不同區域相關聯,這將非常有用。

例:
ko.applyBindings(myViewModel, document.getElementById('someElementId'))



好的,您已經了解瞭如何創建基本視圖模型以及如何使用綁定顯示其中一個屬性。 但 KO 的一個主要優點是它可以在視圖模型更改時自動更新 UI



KO 如何知道視圖模型的哪些部分發生變化?

答:
您需要將模型屬性聲明為可觀察對象,因為這些是特殊的 JavaScript 對象,可以通知訂閱者有關更改的信息,並且可以自動檢測依賴關係。

例如,重寫前面的視圖模型對象,如下所示:

var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123)
};



並非所有瀏覽器都支持 JavaScript getter 和 setter(* cough * IE * cough *),因此為了兼容性, ko.observable 對象實際上是函數。

要讀取 observable 的當前值,只需調用沒有參數的 observable 。 在此示例中,myViewModel.personName() 將返回 'Bob', myViewModel.personAge() 返回 123。

要向 observable 寫入新值,請調用 observable 並將新值作為參數傳遞。 例如,調用 myViewModel.personName('Mary') 會將名稱值更改為 “Mary”。

要將值寫入 View objects 上的多個可觀察屬性,可以使用鏈接語法。 例如,myViewModel.personName('Mary').personAge(50) 會將名稱值更改為 “Mary”,將年齡值更改為50。


可觀察的整個觀點是它們可以被觀察到,即,其他代碼可以說它希望被通知變化。 這就是 KO的 許多內置綁定在內部所做的事情。 因此,當您編寫 data-bind =“text:personName” 時,文本綁定會自動註冊,以便在 personName 更改時得到通知。

通過調用 myViewModel.personName('Mary') 將名稱值更改為“Mary”時,文本綁定將自動更新關聯 DOM 元素的文本內容。 這就是 View Model 的變更會自動更新到 View。



例:

View Model
var myViewModel = {
    personName: ko.observable('Bob'),
    personAge: ko.observable(123),
};

ko.applyBindings(myViewModel );

if (myViewModel.personName() == 'Bob')
{
    myViewModel.personName('Morse').personAge(50);
}


View
The name is <span data-bind="text: personName"></span>.
age is <span data-bind="text: personAge"></span>


Result
The name is Morse. age is 50



訂閱subscribe observables

在一般情況下您通常不需要手動設置訂閱,因此初學者應跳過此部分。

對於高級用戶,如果要註冊自己的訂閱以通知可觀察對象的更改,則可以調用其訂閱功能。 例如:


myViewModel.personName.subscribe(function(newValue) {
    alert("The person's new name is " + newValue);
});

訂閱功能是 KO 在內部工作的部分。 大多數情況下,您不需要使用它,因為內置綁定和模板系統負責管理訂閱。



subscribe 函數接受三個參數:
   callback 是在通知發生時調用的函數
   target(可選)在回調函數中定義 this 的值
   event(可選;默認為“change”)是事件的名稱 收到通知。

如果您願意,也可以終止訂閱:首先將返回值捕獲為變量,然後可以調用其 dispose 函數,例如:

var subscription = myViewModel.personName.subscribe(function(newValue) { /* do stuff */ });
// ...then later...
subscription.dispose(); // I no longer want notifications


如果您希望在即將更改之前收到可觀察值的通知,您可以訂閱beforeChange事件。 例如:

myViewModel.personName.subscribe(function(oldValue) {
    alert("The person's previous name is " + oldValue);
}, null, "beforeChange");

注意:Knockout 不保證 beforeChange 和 change 事件成對出現,因為代碼的其他部分可能會單獨引發任一事件。 如果您需要跟踪可觀察量的先前值,則可以使用訂閱來捕獲和跟踪它。


強制觀察者始終通知訂閱者
當寫入包含原始值(數字,字符串,布爾值或空值)的 observable 時,通常只有在實際更改值時才會通知 observable 的依賴項。 但是,可以使用內置的通知擴展器來確保在寫入時始終通知可觀察的訂戶,即使該值是相同的。 您可以將擴展器應用於這樣的可觀察量:

myViewModel.personName.extend({ notify: 'always' });


延遲和/或禁止更改通知
通常情況下,一個觀察者一改變就立即通知其訂戶。 但是,如果反復更改observable或觸發昂貴的更新,則可以通過限製或延遲observable的更改通知來獲得更好的性能。 這是使用rateLimit擴展器完成的,如下所示:

myViewModel.personName.extend({ rateLimit: 50 });





使用可觀察數組

如果要檢測並響應一個對象的更改,則可以使用 observable。 如果要檢測並響應事物集合的更改,請使用 observableArray 。 這在您正在顯示或編輯多個值並且需要重複的 UI 部分顯示和消失時添加和刪除項目的許多情況下都很有用。

var myObservableArray = ko.observableArray();    // Initially an empty array
myObservableArray.push('Some value');            // Adds the value and notifies observers

要了解如何將observableArray綁定到UI並讓用戶修改它,請參閱簡單列表示例

關鍵點:
observableArray 跟踪數組中的對象,而不是這些對象的狀態,簡單地將對象放入 observableArray 並不會使該對象的所有屬性本身都可觀察到。 當然,如果您願意,您可以觀察這些屬性,但這是一個獨立的選擇。 observableArray 只跟踪它所擁有的對象,並在添加或刪除對象時通知偵聽器。


預先設定 observableArray
如果希望您的可觀察數組不是空的,而是包含一些初始項,請將這些項作為數組傳遞給構造函數。 例如,

// This observable array initially contains three objects
var anotherObservableArray = ko.observableArray([
    { name: "Bungle", type: "Bear" },
    { name: "George", type: "Hippo" },
    { name: "Zippy", type: "Unknown" }
]);


從 observableArray 中讀取信息
在幕後, observableArray 實際上是一個可觀察的,其值是一個數組(另外, observableArray 添加了下面描述的一些附加功能)。 因此,您可以通過調用 observableArray 作為沒有參數的函數來獲取底層 JavaScript 數組,就像任何其他可觀察對像一樣。 然後,您可以從該基礎數組中讀取信息。 例如,

alert('The length of the array is ' + myObservableArray().length);
alert('The first element is ' + myObservableArray()[0]);


從技術上講,您可以使用任何本機 JavaScript 數組函數來操作該底層數組,但通常有更好的替代方案。 KO 的 observableArray 具有自己的等效功能,它們更有用,因為:

它們適用於所有目標瀏覽器。 (例如,本機 JavaScript indexOf 函數在 IE 8 或更早版本上不起作用,但 KO 的 indexOf 無處不在。)

對於修改數組內容的函數,例如 push 和 splice ,KO 的方法會自動觸發依賴關係跟踪機制,以便所有已註冊的偵聽器都會收到更改通知,並且 UI 會自動更新,這意味著使用之間存在顯著差異 KO 的方法(即 observableArray.push(...))和 JavaScript 本機數組方法(即observableArray().push(...))作為後者不向數組的訂閱者發送任何通知,其內容有改變。


本頁的其餘部分描述了 observableArray 用於讀取和寫入數組信息的函數



indexOf
indexOf 函數返回與您的參數相等的第一個數組項的索引。 例如,myObservableArray.indexOf('Blah') 將返回第一個數組條目的從零開始的索引,該索引等於 Blah ,如果未找到匹配的值,則返回值-1。

slice
slice 函數是與本機 JavaScript 切片函數等效的 observableArray(即,它將給定的起始索引中的數組條目返回到給定的結束索引)。 調用 myObservableArray.slice(...) 等效於在底層數組上調用相同的方法(即 myObservableArray().slice(...))。

操縱 observableArray
observableArray 公開了一組熟悉的函數,用於修改數組的內容並通知偵聽器。

pop, push, shift, unshift, reverse, sort, splice
所有這些函數等同於在底層數組上運行本機 JavaScript 數組函數,然後通知偵聽器有關更改:

  • push(value) — 將新項添加到數組的末尾。
  • pop() — 從數組中刪除最後一個值並返回它。
  • unshift(value) — 在數組的開頭插入一個新項。
  • shift() — 從數組中刪除第一個值並返回它。
  • reverse() — 反轉數組的順序並返回 observableArray(不是底層數組)。
  • sort() — 對數組內容進行排序並返回 observableArray。
    • 默認排序是按字母順序排列的,但您可以選擇傳遞一個函數來控制數組的排序方式。 你的函數應該接受數組中的任何兩個對象,如果第一個參數較小則返回負值,正值是第二個較小,或者為零以將它們視為相等。 例如,要按姓氏對“人”對像數組進行排序,您可以編寫 
    myObservableArray.sort(function (left, right) { 
        return left.lastName == right.lastName ? 0 : (left.lastName < right.lastName ? -1 : 1) 
    })
  • splice() — 從給定索引開始刪除並返回給定數量的元素。 例如,
    myObservableArray.splice(1, 3)
    從索引位置1(即第2,第3和第4個元素)開始刪除三個元素,並將它們作為數組返回。
有關這些observableArray函數的更多詳細信息,請參閱標準 JavaScript 數組函數的等效文檔

replace, remove and removeAll

observableArray 添加了一些默認情況下在JavaScript數組中找不到的更有用的方法:
  • replace( oldItem, newItem ) — 用 newItem 替換等於 oldItem 的第一個值。
  • remove( someItem ) — 刪除所有等於someItem的值,並將它們作為數組返回。
  • remove( function (item) { return item.age < 18; } ) — 刪除ageproperty小於18的所有值,並將它們作為數組返回。
  • removeAll( ['Chad', 132, undefined] ) — 刪除所有等於'Chad',123或undefined的值,並將它們作為數組返回。
  • removeAll() — 刪除所有值並將其作為數組返回。

destroy and destroyAll (Note: Usually relevant to Ruby on Rails developers only)

destroy和destroyAll函數主要是為了方便使用Ruby on Rails的開發人員:
  • destroy( someItem ) — Finds any objects in the array that equal someItem and gives them a special property called _destroy with value true.
  • destroy( function (someItem) { return someItem.age < 18; } ) — Finds any objects in the array whose age property is less than 18, and gives those objects a special property called _destroy with value true.
  • destroyAll( ['Chad', 132, undefined] ) — Finds any objects in the array that equal 'Chad'123, or undefined and gives them a special property called _destroy with value true.
  • destroyAll() — Gives a special property called _destroy with value true to all objects in the array.
那麼,這個_destroy的東西到底是什麼? 這對Rails開發人員來說真的很有趣。 Rails中的約定是,當您將動作傳遞給JSON對像圖時,框架可以自動將其轉換為ActiveRecord對像圖,然後將其保存到數據庫中。 它知道數據庫中已有哪些對象,並發出正確的INSERT或UPDATE語句。 要告訴框架刪除記錄,只需將_destroy設置為true即可。
請注意,當KO呈現 foreach 綁定時,它會自動將標記為 _destroyequal 的任何對象隱藏為true。 因此,您可以使用某種“刪除”按鈕來調用陣列上的 destroy(someItem)方法,這將立即導致指定的項目從可見的 UI 中消失。 稍後,當您將JSON對像圖提交給Rails時,該項也將從數據庫中刪除(而其他數組項將像往常一樣插入或更新)。

延遲和/或禁止更改通知

通常,observableArray會在更改後立即通知其訂閱者。 但是,如果observableArray重複更改或觸發昂貴的更新,您可以通過限製或延遲更改通知來獲得更好的性能。 這是使用rateLimit擴展器完成的,如下所示:
// Ensure it notifies about changes no more than once per 50-millisecond period
myViewModel.myObservableArray.extend({ rateLimit: 50 });



綁定 (Bindings)

控製文字和外觀
可見的綁定 - The visible binding
文本綁定The - text binding
html綁定The - html binding
css綁定The - css binding
樣式綁定The - style binding
attr綁定The - attr binding

控制流 - Control flow
foreach綁定 - The foreach binding
if綁定  - The if binding
ifnot綁定 - The ifnot binding
有約束力 - The with binding
組件綁定 - The component binding

使用表單域 - Working with form fields
點擊綁定 - The click binding
事件綁定 - The event binding
提交綁定 - The submit binding

提交綁定添加了一個事件處理程序,以便在提交關聯的 DOM 元素時調用所選的 JavaScript 函數。 通常,您只會在表單元素上使用它。

當您在表單上使用提交綁定時,Knockout 將阻止瀏覽器對該表單的默認提交操作。 換句話說,瀏覽器將調用您的處理程序函數,但不會將表單提交給服務器。 這是一個有用的默認值,因為當您使用提交綁定時,通常是因為您使用表單作為視圖模型的接口,而不是常規 HTML 表單。 如果您確實希望讓表單像普通的 HTML 表單一樣提交,只需從提交處理程序返回 true 即可。

Example
<form data-bind="submit: doSomething">
    ... form contents go here ...
    <button type="submit">Submit>/button>
</form>
 
<script type="text/javascript">
    var viewModel = {
        doSomething : function(formElement) {
            // ... now do something
        }
    };
</script>
如此示例所示,KO 將表單元素作為參數傳遞給提交處理函數。 如果需要,可以忽略該參數,或者有多種方法可以使用它,例如:

  • 從表單元素中提取其他數據或狀態
  • 使用類似於以下代碼段的代碼,使用 jQuery Validation 等庫觸發 UI 級驗證: 
    if ($(formElement).valid()) { /* do something */ }
    .

您可以引用任何JavaScript函數 - 它不必是視圖模型上的函數。 您可以通過編寫 submit:someObject.someFunction 來引用任何對像上的函數。

視圖模型上的函數稍微有點特殊,因為您可以按名稱引用它們,即,您可以編寫 submit:doSomething 而不必編寫 submit:viewModel.doSomething (雖然從技術上講也是有效的)。

啟用綁定 - The enable binding
禁用綁定 - The disable binding
價值綁定 - The value binding
textInput綁定 - The textInput binding
hasFocus綁定 - The hasFocus binding
檢查綁定 - The checked binding
選項綁定 - The options binding
selectedOptions綁定 - The selectedOptions binding
uniqueName綁定 - The uniqueName binding

渲染模板 - Rendering templates
模板綁定 - The template binding

綁定語法 - Binding syntax
數據綁定語法 - The data-bind syntax
綁定上下文 - The binding context

創建自定義綁定 - Creating custom bindings
創建自定義綁定 - Creating custom bindings
控制後代綁定 - Controlling descendant bindings
支持虛擬元素 - Supporting virtual elements
定制處理邏輯 - Custom disposal logic
預處理:擴展綁定語法 - Preprocessing: Extending the binding syntax

組件 - Components
概述:哪些組件和自定義元素提供 - Overview: What components and custom elements offer
定義和註冊組件 - Defining and registering components
組件綁定 - The component binding
使用自定義元素 - Using custom elements
高級:自定義組件加載器 - Advanced: Custom component loaders

更多技術 - Further techniques
加載和保存JSON數據 - Loading and saving JSON data
加載和保存JSON數據
Knockout允許您實現複雜的客戶端交互,但幾乎所有Web應用程序還需要與服務器交換數據,或者至少為本地存儲序列化數據。 交換或存儲數據最方便的方式是JSON格式 - 大多數Ajax應用程序目前使用的格式。

加載或保存數據
Knockout不會強制您使用任何特定技術來加載或保存數據。 您可以使用任何適合您所選服務器端技術的機制。 最常用的機制是jQuery的Ajax輔助方法,例如getJSON,post和ajax。 您可以從服務器獲取數據:

$.getJSON("/some/url", function(data) { 
    // Now use this data to update your view models, 
    // and Knockout will update your UI automatically 
})

var data = /* Your data in JSON format - see below */;
$.post("/some/url", data, function(returnedData) {
    // This callback is executed if the post was successful     
})

或者,如果您不想使用jQuery,則可以使用任何其他機制來加載或保存JSON數據。 所以,所有Knockout需要幫助你做的是:
  • 為了保存,請將您的視圖模型數據轉換為簡單的JSON格式,以便您可以使用上述技術之一發送它
  • 要加載,請使用您使用上述技術之一收到的數據更新視圖模型

將視圖模型數據轉換為Plain JSON
您的視圖模型是JavaScript對象,因此從某種意義上說,您可以使用任何標準JSON序列化程序(例如JSON.stringify(現代瀏覽器中的本機函數)或json2.js庫)將它們序列化為JSON。 但是,您的視圖模型可能包含可觀察對象,計算可觀察對象和可觀察數組,這些數組實現為JavaScript函數,因此不會在沒有代表您的額外工作的情況下乾淨地序列化。

為了便於序列化視圖模型數據,包括observable等,Knockout包含兩個輔助函數:

ko.toJS - 這會克隆你的視圖模型的對像圖,用每個observable替換該observable的當前值,這樣你就得到一個只包含你的數據而沒有Knockout相關工件的普通副本

ko.toJSON - 這會生成一個表示視圖模型數據的JSON字符串。 在內部,它只是在視圖模型上調用ko.toJS,然後在結果上使用瀏覽器的本機JSON序列化程序。 注意:為了在沒有本機JSON序列化程序(例如,IE 7或更早版本)的舊瀏覽器上工作,您還必須引用json2.js庫。

例如,定義視圖模型如下:

var viewModel = {
    firstName : ko.observable("Bert"),
    lastName : ko.observable("Smith"),
    pets : ko.observableArray(["Cat", "Dog", "Fish"]),
    type : "Customer"
};
viewModel.hasALotOfPets = ko.computed(function() {
    return this.pets().length > 2
}, viewModel)

它包含可觀察量,計算可觀察量,可觀察數組和普通值的混合。 您可以將其轉換為適合使用ko.toJSON發送到服務器的JSON字符串,如下所示:
var jsonData = ko.toJSON(viewModel);
 
// Result: jsonData is now a string equal to the following value
// '{"firstName":"Bert","lastName":"Smith","pets":["Cat","Dog","Fish"],"type":"Customer","hasALotOfPets":true}'

或者,如果您只想在序列化之前使用純JavaScript對像圖,請使用ko.toJS,如下所示:

var plainJs = ko.toJS(viewModel);
 
// Result: plainJS is now a plain JavaScript object in which nothing is observable. It's just data.
// The object is equivalent to the following:
//   {
//      firstName: "Bert",
//      lastName: "Smith",
//      pets: ["Cat","Dog","Fish"],
//      type: "Customer",
//      hasALotOfPets: true
//   }

請注意,ko.toJSON接受與JSON.stringify相同的參數。 例如,在調試Knockout應用程序時,對視圖模型數據進行“實時”表示會很有用。 要為此目的生成格式良好的顯示,可以將spaces參數傳遞給ko.toJSON並綁定到視圖模型,如:

<pre data-bind="text: ko.toJSON($root, null, 2)"</pre>

使用JSON更新視圖模型數據
如果您已從服務器加載了一些數據並希望使用它來更新視圖模型,那麼最直接的方法就是自己完成。 例如,

// Load and parse the JSON
var someJSON = /* Omitted: fetch it from the server however you want */;
var parsed = JSON.parse(someJSON);
 
// Update view model properties
viewModel.firstName(parsed.firstName);
viewModel.pets(parsed.pets);

在許多情況下,這種直接方法是最簡單和最靈活的解決方案。 當然,當您更新視圖模型上的屬性時,Knockout將負責更新可見的UI以匹配它。

但是,許多開發人員更喜歡使用更基於約定的方法來使用傳入數據更新其視圖模型,而無需為每個要更新的屬性手動編寫代碼行。 如果您的視圖模型具有許多屬性或深層嵌套的數據結構,這可能是有益的,因為它可以大大減少您需要編寫的手動映射代碼的數量。 有關此技術的更多詳細信息,請參閱knockout.mapping插件

擴展可觀察量 - Extending observables
延期更新 - Deferred updates
限速可觀察量 - Rate-limiting observables
不引人注意的事件處理 - Unobtrusive event handling
使用fn添加自定義函數 - Using fn to add custom functions
Microtasks - Microtasks
異步錯誤處理 - Asynchronous error handling

插件 - Plugins
映射插件The - mapping plugin

更多信息More - information
瀏覽器支持 - Browser support
獲得幫助 - Getting help
教程和示例的鏈接 - Links to tutorials & examples
AMD使用RequireJs(異步模塊定義)- Usage with AMD using RequireJs (Asynchronous Module Definition)
















參考來源: http://knockoutjs.com/documentation/observables.html

AngularJS 最佳實踐

AngularJS 是一個 Web 應用框架,它實現了前端的 MVC 架構,能讓開發人員很方便地實現業務邏輯。
舉個栗子,要做到下面的效果,以前可能需要寫一連串的 JavaScript 代碼綁定 N 多事件。而使用 AngularJS 框架,一句 JavaScript 都不用寫就能實現了,神奇吧?查看演示
angularjs-demo
    <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <div data-ng-app>
        單價: <input type="number" min=0 ng-model="price" ng-init="price = 299">
        <br>
        數量: <input type="number" min=0 ng-model="quantity" ng-init="quantity = 1">
        <br>
        總價: {{ quantity * price }}
    </div>
這得益於 AngularJS 中的雙向數據綁定特性(Two Way Data-Binding),將 Model 和 View 自動關聯了起來,在更複雜的業務場景下,還有代碼分離的好處,將 DOM 操作和應用邏輯解耦,非常實用。
不過沒有銀彈,和其他框架一樣,AngularJS 也有它的局限。CRUD 類型的操作是它所擅長的,想想看以前寫過的管理後台,幾乎大部分都是從數據庫中讀取數據,然後呈現在頁面上,進行各種增刪改查。AngularJS 約定了一套規範(約定優於配置),於是你可以很便捷地操作數據。而在其他方面,例如開發複雜的 Web 遊戲,AngularJS 則是無用武之地了。

一、AngularJS 中的精美特性

雙向綁定

上面的例子已經說明了,我們可以像 PHP Smarty 模板一樣在 HTML 中寫表達式,用 {{ 和 }} 包起來。在 AngularJS 里,View 和 Model 是在 Controller 裡面綁定的,所以無論你在 View 的表單中修改了內容,還是在 Controller 里通過代碼修改了 Model 值,兩邊都會即時發生變化,同步更新。因為 AngularJS 會監控 (watch) Model 對象的變化,隨時反映到 View 中。

Filter

Filter 類似 Unix 裡面的 | 管道概念,AngularJS 把它搬到了前端。還是舉個例子,你們感受一下——
<div>{{ 9999 | number }}</div>
<div>{{ 9999+1 | number:2 }}</div>
<div>{{ 9*9 | currency }}</div>
<div>{{ 'Hello World' | uppercase }}</div>
輸出結果:
9,999
10,000.00
$81.00
HELLO WORLD

二、AngularJS 中的一些“坑”

由於過去寫 JavaScript 的習慣使然,人們很容易掉進一些 AngularJS 的陷阱里。下面的內容假設你已經了解前端 MVC 概念,並對 AngularJS 有了一定經驗,初學者讀起來可能比較艱深晦澀。

DOM 操作

避免使用 jQuery 來操作 DOM,包括增加元素節點,移除元素節點,獲取元素內容,隱藏或顯示元素。你應該使用 directives 來實現這些動作,有必要的話你還要編寫自己的 directives。
如果你感到很難改變習慣,那麼考慮從你的網頁中移除 jQuery 吧。真的,AngularJS 中的 $http 服務非常強大,基本可以替代 jQuery 的 ajax 函數,而且 AngularJS 內嵌了 jQLite —— 它內部實現的一個 jQuery 子集,包含了常用的 jQuery DOM 操作方法,事件綁定等等。但這並不是說用了AngularJS 就不能用 jQuery 了。如果你的網頁有載入 jQuery 那麼 AngularJS 會優先採用你的 jQuery,否則它會 fall back 到 jQLite。
需要自己編寫 directives 的情況通常是當你使用了第三方的 jQuery 插件。因為插件在 AngularJS 之外對錶單值進行更改,並不能即時反應到 Model 中。例如我們用得比較多的 jQueryUI datepicker 插件,當你選中一個日期後,插件會將日期字符串填到 input 輸入框中。View 改變了,卻並沒有更新 Model,因為 $('.datepicker').datepicker(); 這段代碼不屬於 AngularJS 的管理範圍。我們需要編寫一個directive 來讓 DOM 的改變即時更新到 Model 里。
var directives = angular.module('directives', []);
 
directives.directive('datepicker', function() {
   return function(scope, element, attrs) {
       element.datepicker({
           inline: true,
           dateFormat: 'dd.mm.yy',
           onSelect: function(dateText) {
               var modelPath = $(this).attr('ng-model');
               putObject(modelPath, scope, dateText);
               scope.$apply();
           }
       });
   }
});
然後在 HTML 中引入這個 direcitve
<input type="text" datepicker ng-model="myObject.myDateValue" />
說白了 directive 就是在 HTML 里寫自定義的標籤屬性,達到插件的作用。這種聲明式的語法擴展了 HTML。
需要說明的是,有一個 AngularUI 項目提供了大量的 directive 給我們使用,包括 Bootstrap 框架中的插件以及基於 jQuery 的其他很熱門的 UI 組件。我之前說過 AngularJS 的社區很活躍嘛,生態系統健全。

ngOption 中的 value

這是個大坑。如果你去查看 ngOption 生成的 <select> 中的 <option> 的選項值(每個 <option value="xxx"> 的 value 部分),那絕對是枉費心機。因為這裡的值永遠都會是 AngularJS 內部元素的索引,並不是你所指定的表單選項值。
還是要轉變觀念,AngularJS 已經不再用表單進行數據交互了,而是用 Model。使用 $http 來提交 Model,在 php 中則使用 file_get_contents('php://input') 來獲取前端提交的數據。

{{ }} 的問題

在頁面初始化的時候,用戶可能會看到 {{ }},然後閃爍一下才出現真正的內容。
解決辦法:
  1. 使用 ng-cloak directive 來隱藏它
  2. 使用 ng-bind 替代 {{ }}

將界面與業務邏輯分離

Controller 不應該直接引用 DOM,而應該控制 view 的行為。例如“如果用戶操作了 X,應該發生什麼事情”,“我從哪裡可以獲得 X?”
Service 在大部分情況下也不應該直接引用 DOM,它應該是一個單例(singletons),獨立於界面,與 view 的邏輯無關。它的角色只是“做 X 操作”。
DOM 操作應該放在 directives 裡面。

盡量復用已有功能

你所寫的功能很可能 AngularJS 已經實現了,有一些代碼是可以抽象出來複用的,使用更 Angular 的方式。總之就是很多 jQuery 的繁瑣代碼可以被替代。
1. ng-repeat
ng-repeat 很有用。當 Ajax 從服務器獲得數據後,我們經常使用 jQuery (比如上面講過的例子) 向某些 HTML 容器節點中添加更多的元素,這在 AngularJS 里是不好的做法。有了 ng-repeat 一切就變得非常簡單了。在你的 $scope 中定義一個數組 (model) 來保存從服務器拉取的數據,然後使用 ng-repeat 將它與 DOM 綁定即可。下面的例子初始化定義了 friends 這個 model
<div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
    I have {{friends.length}} friends. They are:
    <ul>
        <li ng-repeat="friend in friends">
            [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
        </li>
    </ul>
</div>
顯示結果
I have 2 friends. They are:
  [1] John who is 25 years old.
  [2] Mary who is 28 years old.
2. ng-show
ng-show 也很有用。使用 jQuery 來根據條件控制界面元素的顯示隱藏,這很常見。但是 Angular 有更好的方式來做到這一點。ng-show (以及 ng-hide) 可以根據布爾表達式來決定隱藏和顯示。在 $scope 中定義一個變量:
<div ng-show="!loggedIn">
    點擊 <a href="#/login">這裡</a> 登錄
</div>
類似的內置 directives 還有 ng-disabled, ng-switch 等等,用於條件控制,語法簡潔,都很強大。
3. ng-class
ng-class 用於條件性地給元素添加 class,以前我們也經常用 jQuery 來實現。Angular 中的 ng-class 當然更好用了,例子:
<div ng-class="{ errorClass: isError, warningClass: isWarning, okClass: !isError && !isWarning }">...</div>
在這裡 ng-class 接受一個 object 對象,key 為 CSS class 名,值為 $scope 變量控制的條件表達式,其他類似的內置 directives 還有 ng-class-even 和 ng-class-odd,很實用。

$watch 和 $apply

AngularJS 的雙向數據綁定是最令人興奮的特性了,然而它也不是全能的魔法,在某些情況下你需要做一些小小的修正。
當你使用 ng-model, ng-repeat 等等來綁定一個元素的值時, AngularJS 為那個值創建了一個 $watch,只要這個值在 AngularJS 的範圍內有任何改變,所有的地方都會同步更新。而你在寫自定義的 directive 時,你需要定義你自己的 $watch 來實現這種自動同步。
有時候你在代碼中改變了 model 的值,view 卻沒有更新,這在自定義事件綁定中經常遇到。這時你就需要手動調用 scope.$apply() 來觸發界面更新。上面 datepicker 的例子已經說明了這一點。第三方插件可能會有 call back,我們也可以把回調函數寫成匿名函數作為參數傳入$apply()中。

將 ng-repeat 和其他 directives 結合起來

ng-repeat 很有用,不過它和 DOM 綁定了,很難在同一個元素上使用其他 directives (比如 ng-show, ng-controller 等等)。
如果你想對整個循環使用某個 directive,你可以在 repeat 外再包一層父元素把 directive 寫在那兒;如果你想對循環內部的每一個元素使用某個 directive,那麼把它放到 ng-repeat 的一個子節點上即可。

Scope

Scope 在 templates 模板中應該是 read-only 的,而在 controller 里應該是 write-only 的。Scope 的目的是引用 model,而不是成為 model。model 就是我們定義的 JavaScript 對象。

$rootScope 是可以用的,不過很可能被濫用

Scopes 在 AngularJS 中形成一定的層級關係,樹狀結構必然有一個根節點。通常我們用不到它,因為幾乎每個 view 都有一個 controller 以及相對應的自己的 scope。
但偶爾有一些數據我們希望全局應用在整個 app 中,這時我們可以將數據注入 $rootScope。因為其他 scope 都會繼承 root scope,所以那些注入的數據對於 ng-show 這類 directive 都是可用的,就像是在本地 $scope 中的變量一樣。
當然,全局變量是邪惡的,你必須很小心地使用 $rootScope。特別是不要用於代碼,而僅僅用於注入數據。如果你非常希望在 $rootScope 寫一個函數,那最好把它寫到 service 里,這樣只有用到的時候它才會被注入,測試起來也方便些。
相反,如果一個函數的功能僅僅是存儲和返回一些數據,就不要把它創建成一個 service。

三、AngularJS 項目的目錄結構

怎樣組織代碼文件和目錄?這恐怕是初學者一開始就會遇到的問題。AngularJS 應用開發的官方入門項目 angular-seed,其文件結構是這樣的:
  • css/
  • img/
  • js/
    • app.js
    • controllers.js
    • directives.js
    • filters.js
    • services.js
  • lib/
  • partials/
這種結構對於一個簡單的單頁 app 來說是可行的,只是一旦代碼中存在多個 Controller 或者 Service,就很難找到想要尋找的對象了。我們可以對文件按照業務邏輯進行拆分,就像下面這樣:
  • controllers/
    • LoginController.js
    • RegistrationController.js
    • ProductDetailController.js
    • SearchResultsController.js
  • directives.js
  • filters.js
  • models/
    • CartModel.js
    • ProductModel.js
    • SearchResultsModel.js
    • UserModel.js
  • services/
    • CartService.js
    • UserService.js
    • ProductService.js
這種結構把不同的業務功能拆分為獨立的文件,條理清晰,但是仍有一定的局限性。最大的問題是一個業務功能的代碼分布在controllers, models, servers 三個不同目錄下,要從中挑出正確的文件,建立起代碼關聯,還是有些麻煩。按照功能進行模塊化劃分目錄結構,應該要更為合理一些:
  • cart/
    • CartModel.js
    • CartService.js
  • common/
    • directives.js
    • filters.js
  • product/
    • search/
      • SearchResultsController.js
      • SearchResultsModel.js
    • ProductDetailController.js
    • ProductModel.js
    • ProductService.js
  • user/
    • LoginController.js
    • RegistrationController.js
    • UserModel.js
    • UserService.js
這樣也是適合 RequireJS 等模塊加載器的自然直觀的代碼組織方式。
參考鏈接:





From: https://www.lovelucy.info/angularjs-best-practices.html