knockout 表單綁定 要怎么Mapping才好


問題

  之前有了解過knockout,學習過綁定語法,結合幫助文檔,做個Demo倒也不成問題,但是部分地方很不爽,不知道是我的用法不對,還是功力不夠。

  比如說,標簽里定義的data-bind屬性名,必須在調用 ko.applyBindings(viewModel) 前必須定義。而結合具體的示例來看,html有如下代碼:

    用戶名:<label data-bind="text:userName"></label>
    姓名:<label data-bind="text:realName"></label>
    畢業院校:<select data-bind="
        options:school,
        optionsText:'schoolName',
        optionsValue:'schoolID',
        value:bySchool,
        optionsCaption:'請選擇'
        "></select>

  初步寫法則是:

    var viewModel = 
{
userName: ko.observable(), realName: ko.observable(), value: ko.observable(), options: ko.observableArray() } ko.applyBindings(viewModel);
//后面ajax操作來修改viewModel setTimeout(function () { //加載可選擇院校 var school = [{ schoolID: 1, schoolName: "清華大學" }, { schoolID: 2, schoolName: "北京大學" }, { schoolID: 3, schoolName: "復旦大學" } ]; viewModel.school(school); //加載用戶信息 var user = { userName: 'codealone', realName: '沖動', bySchool: 1 }; viewModel.userName(user.userName); viewModel.realName(user.realName); viewModel.bySchool(user.bySchool); });

Mapping插件

  寫到這,有人可能要跟我說,mapping 插件可以解決這個問題。相關代碼如下:

    The time on the server is: <span data-bind='text: serverTime'></span>
    and <span data-bind='text: numUsers'></span>user(s) are connected.
    <script type="text/javascript">
        var data = { serverTime: '2010-01-07', numUsers: 3 };
        var viewModel = {};
        viewModel = ko.mapping.fromJS(data);
        ko.applyBindings(viewModel);
    </script>

  這樣的確是可以解決,可是問題是,我的數據是ajax加載的呀。那么要先定義屬性,於是修改后的代碼則是:

    The time on the server is: <span data-bind='text: serverTime'></span>
    and <span data-bind='text: numUsers'></span>user(s) are connected.
    <script type="text/javascript">
        var viewModel = {};
        viewModel.serverTime = ko.observable();
        viewModel.numUsers = ko.observable();
        ko.applyBindings(viewModel);
        setTimeout(function () { //模擬ajax取數據
            var data = { serverTime: '2010-01-07', numUsers: 3 };
            ko.mapping.fromJS(data, viewModel);
            document.title = "方法已執行";
        }, 1000);
    </script>

  看起來很好,不過直到數據並沒有更新。只有先在初始化之前mapping一次,后面才可以直接更改。

    The time on the server is: <span data-bind='text: serverTime'></span>
    and <span data-bind='text: numUsers'></span>user(s) are connected.
    <script type="text/javascript">
        var data = { serverTime: '', numUsers: '' };
        var viewModel = ko.mapping.fromJS(data);
        ko.applyBindings(viewModel);
        setTimeout(function () { //模擬ajax取數據
            var data = { serverTime: '2010-01-07', numUsers: 3 };
            ko.mapping.fromJS(data, viewModel);
            document.title = "方法已執行";
        }, 1000);
    </script>

  這樣的話,就沒有問題的,但是這種寫法讓我很郁悶的是,難不成,我要把事先用到的數據結構全部先定義出來,然后mapping。

我的半自動Mapping

  后來覺得ko在初始化的時候,去檢測每個需要綁定的屬性,是否已經定義,如果未定義,則拋出異常,這個邏輯讓我很不爽,但是不在初始化的時候定義,ko 根本就不知道需要監控哪些屬性的變化,而這些屬性到底是對象,還是數組。想到這里,覺得初始化的定義難以避免,就想了個方法,批量注冊初始化觀察對家和批量注冊。實現方式很簡單,看源碼就可以得知。這里貼一下調用方式,第一行代碼中的data參數是為了將批量注冊的屬性名存下來,便於后面直接取出這幾個屬性的值。

    ko.mapper.observable(viewModel, ['userName', 'realName', 'bySchool'], 'data');
    ko.mapper.observableArray(viewModel, ['school']);

  以上操作則是完成了初始化,那么后面的賦值如何批量來進行呢,調用方式如下:

        //加載可選擇院校
        var school = [{ schoolID: 1, schoolName: "清華大學" },
                { schoolID: 2, schoolName: "北京大學" },
                { schoolID: 3, schoolName: "復旦大學" }
        ];
        ko.mapper.extend(viewModel, {
            school: school
        });
        //加載用戶信息
        var users = {
            userName: 'codealone',
            realName: '沖動',
            bySchool: 1
        };
        ko.mapper.extend(viewModel, users);

  第一個extend,最終執行了 viewModel.school(school);

  第二個extend,最終執行了viewModel.userName(users.UserName),viewModel.realName(users.realName);等。

  這樣算是完成了一種mapping。

 

  再說說說剛剛的data參數問題,data參數是為了將屬性保存下來,便於后面取出這些屬性的值。Ko的取值是這樣的,拿上面的viewModel來說,定義了userName,realName,bySchool,那么取值方式則是viewModel.userName(),viewModel.realName(),viewModel.bySchool() ...

        //讀取頁面上的用戶信息
        var userInfo = ko.mapper.getValue(viewModel, 'data');
        var userInfo2 = ko.mapper.getValue(viewModel, ['userName', 'realName', 'bySchool']);

  這樣得到的數據則為:

  {

    userName:'xxxx',

    realName:'xxxxxx',

    bySchool:1

  }

完整示例

  完整頁面代碼如下:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <script src="../knockout-3.0.0.debug.js"></script>
    <script src="../knockout.mapper.js"></script>

</head>
<body>
    用戶名:<label data-bind="text:userName"></label>
    姓名:<label data-bind="text:realName"></label>
    畢業院校:<select data-bind="
        options:school,
        optionsText:'schoolName',
        optionsValue:'schoolID',
        value:bySchool,
        optionsCaption:'請選擇'
        "></select>
</body>
</html>
<script type="text/ecmascript">
    //半自動Mapping
    var viewModel = {};
    ko.mapper.observable(viewModel, ['userName', 'realName', 'bySchool'], 'data');
    ko.mapper.observableArray(viewModel, ['school']);
    ko.applyBindings(viewModel);
    //所有的數據都是基於ajax讀取的,這里使用setTimeout進行模擬
    setTimeout(function () {
        //加載可選擇院校
        var school = [{ schoolID: 1, schoolName: "清華大學" },
                { schoolID: 2, schoolName: "北京大學" },
                { schoolID: 3, schoolName: "復旦大學" }
        ];
        ko.mapper.extend(viewModel, {
            school: school
        });
        //加載用戶信息
        var users = {
            userName: 'codealone',
            realName: '沖動',
            bySchool: 1
        };
        ko.mapper.extend(viewModel, users);
        //讀取頁面上的用戶信息
        var userInfo = ko.mapper.getValue(viewModel, 'data');
        var userInfo2 = ko.mapper.getValue(viewModel, ['userName', 'realName', 'bySchool']);
    }, 1000);
</script>
完整的半自動Mapping頁面

  測試代碼下載

 

  希望能對於ajax加載的數據結構有更好的解決方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM