Подтвердить что ты не робот

Делая поля наблюдаемыми после ajax-поиска в knockout.js

Мне интересно, как я могу сделать определенные поля наблюдаемыми в knockout.js, которые я получаю от вызова ajax без необходимости определять весь объект в моей модели viewmodel. Это возможно? Вот что я до сих пор:

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


    }

});
}
4b9b3361

Ответ 1

С плагином mapping для нокаута вы можете определить свою модель просмотра из простого объекта Javascript, возвращенного из вызова Ajax:

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

У меня была почти такая же ситуация, когда мои объекты были экземплярами классов Javascript. Классы определены без нокаута в виду, и я хотел изменить их, чтобы использовать наблюдаемые вместо этого.

Я создал небольшой помощник для преобразования обычных объектов в наблюдаемые объекты. Вы можете либо указать наблюдаемые поля, либо задать их в объекте (прототипе). (Я думал об этом автоматически, но я не мог определить, какое поле безопасно преобразовать, а какой нет.)

(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 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']);

Ответ 2

Я исправил эту проблему, выяснил, что было лучше объявить наблюдаемые поля на моем объекте, прежде чем устанавливать его в мое представление, например

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

Ответ 3

Вот мой CoffeeScript модуль RequireJS для преобразования того, что Я называю "данные представления" (значения JSON на основе сервера) для 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

Использование образца:

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

Конечно, если вы не делаете CoffeeScript, вы можете перевести на JavaScript, нажав "Try CoffeeScript" на связанном сайте. И если вы не используете RequireJS, просто возьмите соответствующие функции из моего модуля, не обернув их в define.