KnockOutJS - несколько ViewModels в одном представлении

201

Я думаю, что мое приложение становится довольно большим, слишком большим, чтобы обрабатывать каждое представление с помощью одной ViewModel.

Поэтому мне интересно, насколько сложно было бы создать несколько моделей ViewModel и загрузить их все в одно представление. С примечанием, что мне также нужно иметь возможность передавать данные X ViewModel в данные Y ViewModel, чтобы отдельные ViewModel должны были иметь возможность общаться друг с другом или, по крайней мере, знать друг о друге.

Например, у меня есть <select>раскрывающийся список , который в раскрывающемся списке выбора имеет выбранное состояние, которое позволяет мне передать идентификатор выбранного элемента в <select>другой вызов Ajax в отдельной ViewModel ....

Любые замечания по работе с многочисленными моделями в одном представлении приветствуются :)

CLiown
источник
12
Для тех, кто приходит к этому вопросу, просмотрите прошедший ответ. Knockout теперь поддерживает несколько контекстов привязки . Там нет необходимости для гиганта masterVM.
Кэрри Кендалл

Ответы:

150

Если все они должны находиться на одной странице, одним из простых способов сделать это является создание модели основного представления, содержащей массив (или список свойств) других моделей представления.

masterVM = {
    vmA : new VmA(),
    vmB : new VmB(),
    vmC : new VmC(),
}

Тогда у вас masterVMмогут быть другие свойства, если необходимо, для самой страницы. В этой ситуации связь между моделями представлений не составит труда, так как вы можете ретранслировать через masterVM, или можете использовать привязки $parent/ $rootin, или некоторые другие пользовательские опции.

Джон Папа
источник
2
Поэтому я мог бы сделать что-то вроде: data-bind = "text: masterVM.vmA", я полагаю, что я все еще мог бы использовать ko.applyBindings с прикрепленным элементом DOM. Предположим, это также означало бы, что я мог бы сделать: data-bind = "$ parent.masterVm"?
CLiown
12
@ CLiown Вы можете использовать with:привязку, чтобы не повторяться
AlfeG
4
@ CLiown Да, вы можете сделать это, если подключитесь к masterVM. Вы также можете использовать привязку «с», чтобы избежать синтаксиса точек при погружении в модели подвидов.
Джон Папа
1
Я думаю, что этот подход является очень ограничительным ... Теперь в моем случае я использую ASP.Net MVC4, это не помогает, так как будут частичные представления, имеющие свои собственные ViewModel, и разделы part / Content не должны мешать друг другу и из-за условного рендеринга будет действительно сложно использовать этот подход.
Бхувин
1
@bhuvin с помощью <! - ko stopBinding: true -> поможет вам с несколькими представлениями моделей и частичными представлениями разделов. См. Knockmeout.net/2012/05/quick-tip-skip-binding.html для получения дополнительной информации.
Микаэль Феликс
285

Knockout теперь поддерживает привязку нескольких моделей. ko.applyBindings()Метод принимает необязательный параметр - элемент и его потомков , к которому будет активирован связывание.

Например:

ko.applyBindings(myViewModel, document.getElementById('someElementId'))

Это ограничивает активацию элементом с идентификатором someElementIdи его потомками.

Смотрите документацию для более подробной информации.

sanatgersappa
источник
72
Если вы хотите использовать селектор jQuery, вам нужно добавить, [0]чтобы указать фактический элемент DOM (вместо объекта jQuery), например так:ko.applyBindings(myViewModel, $('#someElementId')[0])
MrBoJangles
3
Это должен быть принятый ответ. Вы по-прежнему можете использовать главный объект, как в настоящее время принятый ответ, а затем привязать отдельные модели представления к их соответствующим элементам на странице. Это сэкономит на производительности и ограничит область, необходимую для привязки данных.
Кевин Хайдт,
Можно ли общаться ViewModels друг с другом с этим подходом? т.е. у меня есть TaskVM и NoteVM. Задача может иметь Заметки. Поэтому мой TaskVM должен иметь массив observableArray, а именно примечания, тип которых - TaskVM. Можете ли вы поделиться примером для такого случая?
Ахмет
Вероятно, лучше спросить о связи между виртуальными машинами в новом вопросе.
Ричард Налезински
21

Это мой ответ после завершения очень большого проекта с множеством ViewModels в одном представлении.

Просмотр HTML

    <!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <div id="container1">
        <ul>
            <li >Container1 item</li>
            <!-- ko foreach: myItems -->
            <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <div id="container2">
        <ul>
            <li >Container2 item</li>
            <!-- ko foreach: myItems -->
                <li>Item <span data-bind="text: $data"></span></li>
            <!-- /ko -->
        </ul>
    </div>

    <script src="js/jquery-1.11.1.js"></script>
    <script src="js/knockout-3.0.0.js"></script>
    <script src="js/DataFunction.js"></script>
    <script src="js/Container1ViewModel.js"></script>
    <script src="js/Container2ViewModel.js"></script>

</body>
</html>

Для этого представления я создаю 2 модели представления для id = container1 и id = container2 в двух отдельных файлах javascript.

Container1ViewModel.js

function Container1ViewModel()
{
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("ABC");
    self.myItems.push("CDE");

} 

Container2ViewModel.js

function Container2ViewModel() {
    var self = this;
    self.myItems = ko.observableArray();
    self.myItems.push("XYZ");
    self.myItems.push("PQR");

}

Затем после этих двух моделей представления регистрируются как отдельные модели представления в DataFunction.js.

var container1VM;
var container2VM;

$(document).ready(function() {

    if ($.isEmptyObject(container1VM)) {
        container1VM = new Container1ViewModel();
        ko.applyBindings(container1VM, document.getElementById("container1"));
    }

    if ($.isEmptyObject(container2VM)) {
        container2VM = new Container2ViewModel();
        ko.applyBindings(container2VM, document.getElementById("container2"));
    }
});

Таким образом, вы можете добавить любое количество моделей представления для отдельных элементов div. Но убедитесь, что не создаете отдельную модель представления для div внутри зарегистрированного div.

Джанит Видаршана
источник
Возможно ли сделать это вид модели внутри другого вместо того, чтобы быть отдельными элементами DOM?
UserEsp
4

Проверьте плагин MultiModels для Knockout JS - https://github.com/sergun/Knockout-MultiModels

Сергей Звездин
источник
6
Какое преимущество это имеет по сравнению только с ko.applyBindings (viewModel, document.getElementById ("divName"))? Разве это не просто синтаксический сахар?
Паоло дель Мундо
1
@Paolo del Mundo Это также добавляет зависимость от плагина LiveQuery.
Ларс Гируп Бринк Нильсен
@PaolodelMundo Плагин предназначен для того, чтобы иметь возможность использовать набор видовых моделей в виде декларации
Сергей Звездин,
1

Для этого мы используем компоненты. ( http://knockoutjs.com/documentation/component-overview.html )

Например, у нас есть библиотека компонентов, которую мы разрабатываем: https://github.com/EDMdesigner/knobjs

Если вы углубитесь в код, то увидите, что, например, мы повторно используем компонент кнопки-регулятора в нескольких местах.

gyula.nemeth
источник