Длительные операции загрузки в Durandal

Я пытаюсь выяснить, где лучше всего выполнять длительную операцию загрузки с помощью Durandal.

Насколько я могу судить, общая рекомендация по загрузке данных содержится в методе ViewModel activate, что я обычно и делаю — что-то вроде:

viewModel.activate = function () {
    var loadPromise = myService.loadData();

    return $.when(loadPromise).then(function (loadedData) {
        viewModel.data(data);
    });
};

Я знаю, что если я не верну здесь обещание, то обычно возникают проблемы с привязками - как этот вопрос и ответ указывают.

Однако выполнение длительной операции загрузки в методе activate приводит к тому, что приложение «зависает» во время завершения операции загрузки. Например, что, если моя нагрузка теперь была примерно такой?

viewModel.activate = function () {
    // All loads return a promise
    var firstLoad = myService.loadFirstData();
    var secondLoad = myService.loadSecondData();
    var thirdLoad = myService.loadThirdDataWhichTakesAges();

    return $.when(firstLoad, secondLoad, thirdLoad).then(function (one, two, three) {
        viewModel.one(one);
        viewModel.two(two);
        viewModel.three(three);
    });
};

В этом случае URL-адрес обновляется, чтобы отразить загружаемую страницу, но содержимое страницы по-прежнему показывает предыдущую страницу (это то, что я имею в виду под «зависаниями»).

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

Есть ли рекомендуемый способ достижения этого внутри Дюрандаля?

Мое текущее решение состоит в том, чтобы начать загрузку в методе activate, а затем заполнить данные в методе viewAttached:

var loadPromise;

viewModel.activate = function () {
    // All loads return a promise
    var firstLoad = myService.loadFirstData();
    var secondLoad = myService.loadSecondData();
    var thirdLoad = myService.loadThirdDataWhichTakesAges();

    loadPromise = $.when(firstLoad, secondLoad, thirdLoad);

    // Don't return the promise - let activation proceed.
};

viewModel.viewAttached = function () {
    $.when(loadPromise).then(function (one, two, three) {
        viewModel.one(one);
        viewModel.two(two);
        viewModel.three(three);
    });
};

Кажется это работает, но я помню, что где-то читал, что полагаться на viewAttached было не лучшим решением. Я также не уверен, есть ли вероятность состояния гонки, поскольку я разрешаю активацию.

Любые другие рекомендации?


person gerrod    schedule 30.07.2013    source источник
comment
В общем, для своих SPA-приложений я создаю прозрачный div с загрузкой текста и gif. Этот div накладывается на всю страницу, так что пользователи получают обратную связь о том, что приложение пытается что-то сделать, но в то же время они не должны иметь возможности щелкнуть что-то еще или выполнить какие-либо манипуляции с данными, пока моя страница открыта. осуществляется навигация. Вы можете создать общую службу, которую любое представление может вызвать до начала навигации, и удалить div после завершения навигации.   -  person Yogesh    schedule 31.07.2013
comment
Спасибо @Yogesh, я делаю что-то подобное, хотя стараюсь избегать полных наложений страниц, потому что они плохо работают на мобильных устройствах.   -  person gerrod    schedule 01.08.2013


Ответы (3)


Вам не нужно возвращать обещание, но в этом случае вы должны обработать это в своих нокаутирующих привязках, чтобы вы не привязывались к элементам, которые не определены. Вы можете попытаться избавиться от этого «возврата» при активации, но добавить свойство, указывающее, загружается ли модель. Что-то вроде этого:

viewModel.isLoading = ko.observable(false);
viewModel.activate = function () {
   isLoading(true);
   var loadPromise = myService.loadData();

   $.when(loadPromise).then(function (loadedData) {
      viewModel.data(data);
      isLoading(false);
   });
};

И затем, в вашем представлении, у вас может быть раздел, который отображается, когда представление все еще загружается, и тот, который появляется, когда загрузка завершена. Что-то вроде:

<div data-bind:"visible: isLoading()">Loading Data....</div>
<div data-bind:"visible: !isLoading()">Put your regular view with bindings here. Loading is done so bindings will work.</div>
person karol.barkowski    schedule 30.07.2013
comment
Я не уверен, что это предотвратит проблему неправильного применения привязок. visible: !isLoading() не предотвращает применение дочерних привязок (где, как привязка if, потому что дочерние узлы удаляются из dom). Из того, что я видел, проблемы возникают, если вы обновляете наблюдаемые модели представления между вызовом activate и вызовом viewAttached. - person gerrod; 01.08.2013
comment
При повторном прочтении моего вопроса выясняется, что я недостаточно конкретизировал то, о чем спрашивал, - я хотел узнать, как лучше всего избежать проблем с привязкой данных, а не как лучше всего показать пользователю, что загрузка шла операция. Итак, теперь я лучше понимаю характер вашего ответа; поэтому я отмечу это как правильное! - person gerrod; 01.08.2013
comment
Здорово! Так что нам не нужно возвращать промис в движок. - person Ashraf Sayied-Ahmad; 21.11.2014

Какую версию Дюрандаля вы используете? В Durandal 2.0.0pre вам было разрешено НЕ возвращать обещание в activate, чтобы композиция представления (без данных) могла происходить немедленно.

Вы можете подумать о рефакторинге viewModel.one и т. д. в модуль, который возвращает функцию-конструктор, чтобы каждый, два, три отвечали за получение своих собственных данных. Таким образом, вам не придется ждать первых двух вызовов loadThirdDataWhichTakesAges. Это имело бы смысл в сценариях, где один, два, три не сильно зависят друг от друга.

person RainerAtSpirit    schedule 30.07.2013
comment
Однако проблема здесь в том, что если один/два/три все разрешаются где-то между activate и viewAttached, вы в конечном итоге столкнетесь с состоянием гонки, которое нарушит ваши привязки. Проверьте ссылку, на которую я ссылался в своем вопросе, для объяснения этого. Спасибо за ваш ответ! - person gerrod; 01.08.2013

Для справки; Я разместил аналогичный вопрос в группе Durandal Google (фактически спрашивая, является ли использование активации и viewAttached таким образом приемлемой идеей) и получил следующий ответ от Роба Айзенберга:

Это, вероятно, сработает. Проблема в том, что Knockout уничтожит привязки данных к элементам, если свойства обновлены, а элемент в данный момент отсутствует в документе. Это может произойти в зависимости от времени выполнения асинхронного кода. Из-за того, как композиция работала в версии 1.x, это могло вызвать проблемы, если вы не вернули обещание из своей функции активации. Это должно работать лучше в viewAttached, но в зависимости от характера вашей композиции представление может быть прикреплено к своему родителю, но все еще не в документе. Это зависит от глубины композиции. Таким образом, вы также можете столкнуться с проблемами, если у вас есть это в глубоко составленном модуле. К сожалению, в Durandal 1.x нет четкого способа сделать это из-за нокаутирующего поведения. В Durandal 2.x мы переработали композицию, чтобы эта проблема исчезла и возвращать промис больше не нужно (хотя вы все еще можете это сделать). Durandal 2.0 выйдет примерно через две недели.

person gerrod    schedule 01.08.2013