English 中文(简体)
making fields observable after ajax retrieval in knockout.js
原标题:

I am wondering how I can make certain fields observables in knockout.js that I get from an ajax call without having to define the whole object in my viewmodel. Is this possible? Here is what I have so far:

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);


    }

});
}
问题回答

With the mapping plugin for Knockout you can define your view model from a plain Javascript object, as returned from an Ajax call:

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

I had an almost similar situation, where my objects are instances of Javascript classes. The classes are defined without Knockout in mind, and I wanted to modify them to use observables instead.

I created a small helper to convert regular objects to observable objects. You can either specify the observable fields, or set them in the object (prototype). (I thought of doing this automatically, but I could not determine which field was safe to convert and which one was not.)

(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;
    };
})();

You can use it with classes or with plain objects.

// 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 Westbrook");

// 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 ]);

I fixed this issue, found out it was better to declare the observable fields on my object before setting it to my view, like

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

Here is my CoffeeScript RequireJS module for converting what I call "view data" (server-derived JSON values) to KO ViewModels:

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

Sample usage:

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

Of course, if you don t do CoffeeScript, you can translate to JavaScript by clicking "Try CoffeeScript" on the linked site. And if you don t use RequireJS, then just take the relevant functions from my module without wrapping them in define.





相关问题
selected text in iframe

How to get a selected text inside a iframe. I my page i m having a iframe which is editable true. So how can i get the selected text in that iframe.

How to fire event handlers on the link using javascript

I would like to click a link in my page using javascript. I would like to Fire event handlers on the link without navigating. How can this be done? This has to work both in firefox and Internet ...

How to Add script codes before the </body> tag ASP.NET

Heres the problem, In Masterpage, the google analytics code were pasted before the end of body tag. In ASPX page, I need to generate a script (google addItem tracker) using codebehind ClientScript ...

Clipboard access using Javascript - sans Flash?

Is there a reliable way to access the client machine s clipboard using Javascript? I continue to run into permissions issues when attempting to do this. How does Google Docs do this? Do they use ...

javascript debugging question

I have a large javascript which I didn t write but I need to use it and I m slowely going trough it trying to figure out what does it do and how, I m using alert to print out what it does but now I ...

Parsing date like twitter

I ve made a little forum and I want parse the date on newest posts like twitter, you know "posted 40 minutes ago ","posted 1 hour ago"... What s the best way ? Thanx.

热门标签