Привязать модель представления к пользовательскому интерфейсу после успешного вызова ajax Knockout

У меня есть переключатель selectall и флажок в каждой строке данных.

Теперь данные, которые возвращаются с сервера, не имеют isSelected Observable. Я добавил наблюдаемый isSelected для каждой строки. Но наблюдаемое isSelected не привязывается к флажкам в каждой строке.

Вот модель просмотра:

var folderViewModel = function () {
    var self = this;
    self.Folders = ['Inbox', 'Archive', 'Sent', 'Spam'];
    self.SelectedFolder = ko.observable();
    self.Mails = ko.observableArray([]);
    self.SelectedMail = ko.observable();
    self.SelectAll = ko.observable(false);

    self.navigate = function (folder) {
        self.SelectedFolder(folder);
        //$.get('/Api/MailBox', { folder: folder }, self.Mails);

        $.ajax({
            url: "/Api/Mailbox",
            data: { folder: folder },
            success: function (data) {
                ko.mapping.fromJS(data, {}, self.Mails);
                ko.utils.arrayForEach(self.Mails(), function (mail) {
                    mail.isSelected = ko.observable(true);
                    mail.isSelected.subscribe(function (myvalue) {
                        console.log(myvalue);
                    });
                });
                console.log(ko.toJSON(self.Mails()));
            },
            statusCode: {
                404: function () {
                    alert("No Mail");
                }
            }
        });

        //ko.mapping.fromJS(data, {}, self.Mails);
        //console.log(ko.toJSON(self.Mails));
    };

    self.SelectAll.subscribe(function (newValue) {
        ko.utils.arrayForEach(self.Mails(), function (mail) {
            console.log(mail.isSelected());
            mail.isSelected(newValue);

        });
        console.log(newValue);
    }, self);

    this.navigate("Inbox");
};
ko.applyBindings(new folderViewModel());

А вот и привязка.

<table class="table table-bordered table-striped table-condensed table-hover">
<thead>
    <tr>
        <th>
            <input type="checkbox" data-bind="checked: SelectAll"/>
            @*<input type="checkbox" />*@
        </th>
        <th>
            From
        </th>
        <th>
            To
        </th>
        <th>
            Subject
        </th>
        <th>
            Date
        </th>
    </tr>
</thead>
<tbody data-bind="foreach:Mails">
    <tr data-bind="click:$root.navigateToMail">
        <td style="width: 15px">
            <input type="checkbox" data-bind="checked: $root.isSelected">
            @*<input type="checkbox">*@
        </td>
        <td data-bind="text: From">
        </td>
        <td data-bind="text: To">
        </td>
        <td data-bind="text: Subject">
        </td>
        <td data-bind="text: MailDate">
        </td>
    </tr>
</tbody>

The checkbox <input type="checkbox" data-bind="checked: $root.isSelected"> is not getting bound to the ajax data in mails.isSelected=ko.obsevable(true). What might be the problem ?


person Joy    schedule 30.12.2012    source источник


Ответы (2)


Во-первых, спасибо за использование примера learn.knockoutjs.com, замечательного ресурса.

Ваша ошибка является распространенной ошибкой в ​​KnockoutJS: вы изменяете свою модель без обновления наблюдаемых, которые к ней привязаны. Смотрите следующие строки -

ko.mapping.fromJS(data, {}, self.Mails);
ko.utils.arrayForEach(self.Mails(), function (mail) { ... });

Если вы видите, вы сначала создаете свою модель для почты, а ЗАТЕМ добавляете дополнительную наблюдаемую. К какому контексту должна быть привязана эта наблюдаемая? Без обновления значений каждой почты ваш mail.isSelected никогда не будет добавлен в почтовый объект.

Есть два способа решить эту проблему. Во-первых, вы обновляете почтовую модель в массиве forEach:

ko.utils.arrayForEach(self.Mails(), function (mail, index) { 
    // Add up the isSelected observable
    self.Mails()[index] = mail; 
});

У этого есть проблема с производительностью на наблюдаемых массивах, которую вы можете прочитать здесь . По сути, вы хотите создать временный массив и обновить весь наблюдаемый массив, а не каждый раз вызывать Mails().

Другой способ довольно прост: поменяйте местами порядок создания почтовой модели:

ko.utils.arrayForEach(self.Mails(), function (mail) { ... });
ko.mapping.fromJS(data, {}, self.Mails);

СНАЧАЛА вы прикрепляете к объекту данных свой наблюдаемый объект, а ЗАТЕМ вы говорите своей модели представления: «Эй! У нас есть новая модель для сохранения, она имеет эти свойства из data ПЛЮС наблюдаемый объект isSelected, который я только что добавил, не забудьте связать его?»

При этом свойство в HTML равно checked: isSelected, а не checked: $root.isSelected, что было причиной того, что оно работало над SelectAll (вроде как), поскольку isSelected привязывался к вашей ViewModel, а не к вашей почтовой модели. Вы могли бы разобраться с отладкой с помощью этого удобного оператора:

// In any row inside your data
<td data-bind="text: ko.toJSON($data)">DEBUG DATA</td>

Это должно быть так, вы можете увидеть, как ваш код работает здесь, а также все его решения: http://jsfiddle.net/jjperezaguinaga/VTuHA/. Я добавил некоторые образцы данных для экспериментов (с функцией /echo/json/ из JsFiddle) и удалил некоторые вещи, а также дополнительный столбец, который я использовал для отладки с последним кодом, который я добавил. В этом последнем столбце вы можете видеть, что значение isSelected обновляется каждый раз, когда вы нажимаете на флажок.

person jjperezaguinaga    schedule 30.12.2012
comment
отличный ответ, но не забудьте объяснить одну часть. Я понимаю тот факт, что вы на самом деле прикрепили наблюдаемое свойство к данным, а затем сопоставили его с помощью нокаутирующего сопоставления. Извините за нуб, но разве они оба не одно и то же? если вы сначала создадите наблюдаемый массив, а затем присоедините наблюдаемое свойство (очевидно, что я сделал, но не работал). Так потрудитесь объяснить, в чем была моя вина здесь? - person Joy; 30.12.2012
comment
Нет проблем, и нет нубских вопросов :) Во-первых, это ViewModel (подумайте обо всем вашем функциональном объекте, доступ к которому осуществляется с помощью тега $root, а другой — к вашей почтовой модели, которая является просто выражением для описания определенного класса с помощью куча атрибутов.В вашей ViewModel есть коллекция (ko.observableArray) почтовых моделей; ваша проблема заключалась в том, что вы привязывали isSelected к своей ViewModel ($root.isSelected работало), в то время как вам нужно было привязать ее к каждой почтовой модели и соответствующей области. - person jjperezaguinaga; 31.12.2012
comment
Это связано со знаменитым словом this в Javascript. Каков был контекст ko.observable, когда вы добавили его к объекту Mail в foreach? Это была ViewModel, поэтому вы привязывали ее ко всей своей ViewModel, а не к модели, которую хотели. Кстати, модель — это просто синтаксис, скопированный из Backbone, который представляет класс, на самом деле он не используется в KnockoutJS; надеюсь, использование этого слова помогло вам увидеть, что ваша почта является конкретным Объектом из определенного класса, и не запутать вас :) - person jjperezaguinaga; 31.12.2012
comment
о хорошо спасибо. Думаю, я понял. На самом деле я привязывал его к базовой функции, но не к каждому почтовому объекту. Мне нужно провести исследование, я думаю, прежде чем я буду уверен. Однако большое спасибо. Я бы сохранил ваши заявления в качестве справочного материала, учитывая это и в будущем :) - person Joy; 31.12.2012
comment
Это правильно, и нет проблем, не стесняйтесь продолжать спрашивать вокруг, так что мы поможем, чем сможем. - person jjperezaguinaga; 31.12.2012

Разве isSelected не добавляется к каждому элементу mail, а не в модели представления root? Если это так, ваша привязка должна быть просто:

<input type="checkbox" data-bind="checked: isSelected">

ПРИМЕЧАНИЕ. Я также не вижу метода navigateToMail, доступного в модели представления root, что также вызовет проблемы.

person John Earles    schedule 30.12.2012
comment
Я действительно сделал это. Но при этом он говорит, что isSelected не определен - person Joy; 30.12.2012