在 knockout.js 中进行 ajax 检索后使字段可观察

Posted

技术标签:

【中文标题】在 knockout.js 中进行 ajax 检索后使字段可观察【英文标题】:making fields observable after ajax retrieval in knockout.js 【发布时间】:2011-05-18 15:24:41 【问题描述】:

我想知道如何使我从 ajax 调用中获得的 knockout.js 中的某些字段可观察,而无需在我的视图模型中定义整个对象。这可能吗?这是我目前所拥有的:

var viewModel = 
    lines: new ko.observableArray([])
;
function refreshList(ionum) 
    var data = ;
    data['IONum'] = ionum;
    $.ajax(
    url: 'handlers/getlines.ashx',
    data: data,
    cache: false,
    dataType: 'json',
    success: function(msg) 

        viewModel.lines(msg);
        //here is where I am attempting to make the email address field observable
        /*for (var i = 0; i < msg.length; i++) 
            viewModel.lines()[i].emailAddress = new ko.observable(msg[i].emailAddress);

        */

        //alert(viewModel.lines[0].emailAddress);
        //ko.applyBindings(viewModel);


    

);

【问题讨论】:

+1 解决好问题:淘汰赛 + ajax 方法 【参考方案1】:

使用the mapping plugin for Knockout,您可以从一个普通的 javascript 对象定义您的视图模型,就像从 Ajax 调用返回的那样:

var viewModel = ko.mapping.fromJS(data);
// Every time data is received from the server:
ko.mapping.updateFromJS(viewModel, data);

我遇到了几乎类似的情况,我的对象是 Javascript 类的实例。这些类是在没有考虑 Knockout 的情况下定义的,我想修改它们以使用 observables。

我创建了一个小助手来将常规对象转换为可观察对象。您可以指定可观察字段,也可以在对象(原型)中设置它们。 (我想自动执行此操作,但我无法确定哪个字段可以安全转换,哪个字段不安全。)

(function() 
    ko.observableObject = function(object, ko_fields) 
        ko_fields = ko_fields || object._ko_fields;
        if (!ko_fields) 
            return object;
        
        for (var f_idx = 0; f_idx < ko_fields.length; f_idx++) 
            var field_name = ko_fields[f_idx];
            if (object[field_name] && object[field_name].__ko_proto__ !== undefined) 
                continue;
            
            if (object[field_name] instanceof Array) 
                var field_array = object[field_name];
                for (var a_idx = 0; a_idx < field_array.length; a_idx++) 
                    field_array[a_idx] = ko.observableObject(field_array[a_idx]);
                
                object[field_name] = ko.observableArray(field_array);
             else 
                object[field_name] = ko.observable(object[field_name]);
            
        

        return object;
    ;
)();

您可以将它与类或普通对象一起使用。

// With classes. We define the classes without Knockout-observables
// User.subscriptions is an array of Subscription objects
User = function(id, name) 
    this.id = id;
    this.name = name;
    this.subscriptions = [];
;

Subscription = function(type, comment) 
    this.type = type;
    this.comment = comment;
);

// Create some objects
var jan = new User(74619, "Jan Fabry");
jan.subscriptions.push(new Subscription("Stack Overflow", "The start"));
jan.subscriptions.push(new Subscription("Wordpress Stack Exchange", "Blog knowledge"));
var chris = new User(16891, "Chris West***");

// We would like to convert fields in our objects to observables
// Either define the fields directly:
ko.observableObject(jan, ['id', 'name', 'subscriptions']);
ko.observableObject(chris, ['id', 'name', 'subscriptions']);
// This will only convert the User objects, not the embedded subscriptions
// (since there is no mapping)

// If you define it in the class prototype, it will work for embedded objects too
User.prototype._ko_fields = ['id', 'name', 'subscriptions'];
Subscription.prototype._ko_fields = ['type', 'comment'];
ko.observableObject(jan);
ko.observableObject(chris);

// It also works with objects that are not created from a class, like your Ajax example
var observable = ko.observableObject('id': 74619, 'name':'Jan', ['id', 'name']);

【讨论】:

【参考方案2】:

我解决了这个问题,发现最好先在我的对象上声明可观察字段,然后再将其设置到我的视图中,例如

for (var i=0;i<msg.lenth;i++)
msg[i].emailAddress=ko.observable(msg[i].emailAddress);

viewModel.lines(msg);

【讨论】:

【参考方案3】:

这是我的CoffeeScriptRequireJS module,用于将我所谓的“视图数据”(服务器派生的 JSON 值)转换为 KO ViewModel:

define "infrastructure/viewModels", [], (viewModels) ->
    exports = 

    isDate = (x) ->
        typeof x is "string" and
        x.startsWith "/Date("

    deserializeDate = (dateString) ->
        new Date(parseInt(dateString.substr(6)))

    isScalar = (x) ->
        x is null or
        typeof x is "string" or
        typeof x is "number" or
        typeof x is "boolean"

    exports.fromViewData = (viewData) ->
        if isDate viewData
            return ko.observable deserializeDate viewData
        if isScalar viewData
            return ko.observable viewData

        viewModel = 
        for own key, value of viewData
            if key is "id" then continue

            viewModel[key] = 
                if Array.isArray value
                    ko.observableArray (exports.fromViewData el for el in value)
                else
                    exports.fromViewData value

        return viewModel

    return exports

示例用法:

require ["infrastructure/viewModels"], (viewModels) ->
    $.getJSON "/url/to/data", (viewData) ->
        viewModel = viewModels.fromViewData viewData
        ko.applyBindings viewModel

当然,如果您不使用 CoffeeScript,您可以通过单击链接站点上的“Try CoffeeScript”来翻译成 JavaScript。如果你不使用 RequireJS,那么只需从我的模块中获取相关函数,而不用将它们包装在 define 中。

【讨论】:

我不明白您为什么认为发布 CoffeeScript 会有帮助?

以上是关于在 knockout.js 中进行 ajax 检索后使字段可观察的主要内容,如果未能解决你的问题,请参考以下文章

在 Knockout.js 中异步应用绑定

Knockout js在第二次绑定后继续显示/隐藏元素

knockout.js 数据中的嵌套 foreach 显示但未正确显示

我是不是有理由使用 Knockout MVC 而不是 Knockout JS?

GroupBy,在 knockout.js 中过滤

用于在 url 中查找部分字符串的 Knockout.js 数据绑定