Durandal, как прикрепить вид и не показывать его, пока он не будет готов?

Вот необычный сценарий, который я пытаюсь реализовать: у меня есть модуль gmap, в котором асинхронно загружаются карты Google. Я использую следующий код для правильного подключения Google Maps:

 composition.addBindingHandler('googleMap', {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            gmap = ko.utils.unwrapObservable(valueAccessor());

            var latLng = new gmaps.LatLng(37.774107,-122.420853);
            var mapOptions = {
                center:latLng,
                zoom: 17,
                disableDefaultUI: true,
                mapTypeId: gmaps.MapTypeId.ROADMAP
            };

            gmap.googleMap = new gmaps.Map(element, mapOptions);
            navigator.geolocation.getCurrentPosition(onSuccess, onError);
        }
    });

Мой модуль gmap — это модуль, который я хочу показать первым после входа пользователя в систему. Поэтому я хочу выполнить некоторые действия, прежде чем этот модуль должен быть показан. В большинстве случаев я мог бы использовать метод Promise in canActivate, но! В этом случае мой модуль не прикрепляется. Но мне нужно подключить этот модуль, выполнить некоторые операции (получить записи в базе данных, определить геолокацию пользователя, добавить маркеры на карту Google на основе этих данных) и только после этого показать модуль пользователю. Как я могу заставить это работать? Я попробовал Promises, но у меня это не сработало, потому что я слушаю метод attached, который не срабатывает, пока модуль не будет показан. Итак, вопрос: как активировать и присоединить модель представления и соответствующее представление (активация и прикрепленные обратные вызовы должны срабатывать) но не показать его до условия?

ОБНОВЛЕНИЕ: Хорошо, кажется, мой вопрос не совсем понятен. Вот мой сценарий:
1. Мой модуль входа активирован. Пользователь должен ввести свои учетные данные.
2. После успешной авторизации модуль Gmap должен быть активирован и подключен, но не отображаться. Пользователь по-прежнему должен видеть модуль SignIn и сообщение типа "Загрузка данных..."
3. Модуль Gmap должен быть полностью загружен (должны срабатывать все обратные вызовы). Мне нужна возможность взаимодействия с DOM, а значит модуль должен быть подключен. Но не отображается.
4. При выполнении некоторых условий модуль SignIn становится невидимым и отображается Gmap.

Обещание в обратном вызове активации не подходит для этого сценария, потому что представление не прикрепляется до тех пор, пока обещание не будет разрешено. В основном мне нужно что-то вроде этого после авторизации успеха:

router.navigate('#gmap', { show: false });
...or...
router.attachModule('#gmap', ...{});

Зачем мне нужно взаимодействовать с DOM и почему промис в обратном вызове активации мне не подходит? Потому что мне нужен инициированный объект Gmap, который можно инициировать только с существующим холстом gmap на странице:

 composition.addBindingHandler('googleMap', {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel) {
            gmap = ko.utils.unwrapObservable(valueAccessor());
            var mapOptions = {
             ...

            };

            gmap.googleMap = new gmaps.Map(element, mapOptions);

        }
    });

И HTML:

<div id="gmap-section">
    <div id="gmap-canvas" data-bind="googleMap:map"></div>
</div>

Привязки будут работать только после прикрепления вида.


person BillyZ    schedule 29.08.2014    source источник
comment
Как насчет того, чтобы скрыть его с помощью видимой привязки нокаута и установить true, когда вы выполнили все задачи?   -  person zewa666    schedule 29.08.2014
comment
Когда маршрутизатор переходит к моему модулю gmap из моего модуля входа, он скрывает мой модуль входа. Итак, я не вижу модуль gmap, потому что он еще не готов (например, скрыт display:none), и я не вижу свой модуль входа, потому что он был скрыт маршрутизатором Durandal.   -  person BillyZ    schedule 29.08.2014


Ответы (2)


Я еще не до конца понял ваш вопрос, но я попытаюсь дать вам пищу для размышлений. ViewModel, в соответствии с передовой практикой, должен отвечать только за чисто визуальные вещи. Так что любой тяжелый расчет, подключение к БД или что-то еще нужно вытащить в отдельный сервис. В случае Durandal это будет просто простой модуль RequireJS. Он должен быть одноэлементным и может потребоваться нескольким виртуальным машинам.

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

Что касается того, как это сделать, либо вы запускаете вызов службы в SignIn, а после выполнения Promise переходите к GMapVM, либо используете Durandal, встроенный в процесс Pub/Sub, если вам не нравятся Promises.


Что касается внутренней системы Durandals, возможно, canActivate из GMapVM также будет подходящим решением или, альтернативно, canDeactivate из SignInVM.

person zewa666    schedule 29.08.2014
comment
Обещание в методе canActivate не является решением, потому что вложение не срабатывает. canDeactivate тоже не является решением, потому что он выполняется до canActivate, поэтому даже canActivate не срабатывает. Я хочу сделать это, потому что я хочу добавить маркеры на карту до того, как будет показан модуль gmap. Таким образом, пользователь увидит только загруженную карту со всеми маркерами на ней. Как видите, в этом случае логика представления тесно связана с моделью представления. Или я могу быть уверен, что пользователь не увидит процесс добавления маркеров, если я загружу все данные при «активации», а добавлю маркеры на основе этих данных при «прикреплении»? - person BillyZ; 29.08.2014
comment
Извините за двойной комментарий, но вот еще один пример: ссылка как вы можете видеть здесь, мне нужен объект gmap для использования Directions Services. Например, я хочу рассчитать дорогу на основе некоторых условий и сначала показать ее по мере загрузки моего модуля gmap. Таким образом, пользователь не должен видеть процесс построения маршрута. Я могу использовать расчетное время (например, ~ 5 минут до точки A) для некоторых целей, прежде чем будет отображаться gmap. Но я не могу, потому что я могу получить доступ к gmap только ПОСЛЕ того, как мой модуль gmap подключен и обработчик композиции завершен. - person BillyZ; 29.08.2014
comment
ваш файл макета index.html или что-то еще должно иметь div с идентификатором applicationHost, так как насчет добавления туда видимой привязки и установки для него значения false, пока выполняется расчет, а затем true? - person zewa666; 30.08.2014
comment
Делая applicationHost невидимым, все мои представления также становятся невидимыми, потому что они прикреплены внутри applicationHost. Я хочу показать окно авторизации и только в случае успешного входа в систему сделать все расчеты. - person BillyZ; 31.08.2014

Если ваш метод активации возвращает обещание, маршрутизатор Durandal будет ждать, пока это обещание не будет разрешено, прежде чем перенаправляться на новый маршрут, как указано в документах — http://durandaljs.com/documentation/Using-The-Router.html.

Если вы хотите, можете даже просто создать свое собственное обещание и использовать setTimeout для демонстрации в своем приложении.

function activate () {
    var promise = Q.deferred();
    setTimeout(function () { promise.resolve(true); }, 5000);
    return promise;
}

Это вернет обещание маршрутизатору Дюрандаля, а затем через 5 секунд оно разрешит обещание. Вы должны видеть старый маршрут, пока обещание не будет разрешено.

Изменить

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

var showGmapStuff = ko.observable(false);

function activate () {
    var promise = Q.deferred();
    initializeStuff(promise);
    return promise;
}

function initializeStuff(promise) {
    connecttoserver();
    dootherstuff().then(finished);

    function finished() {
        promise.resolve(true);
    }
}

function attached () {
    showGmapStuff(true);
}

А на ваш взгляд -

<div data-bind="if: showGmapStuff">
    <div data-bind="gmap: mapdata"></div>
</div>

В этом случае привязка gmap не будет существовать до тех пор, пока активация не вернет обещание, а прикрепленный обратный вызов не установит для showGmapStuff значение true. Таким образом, вместо того, чтобы быть видимым false, он вообще не будет оцениваться, пока вы этого не захотите.

person PW Kad    schedule 31.08.2014
comment
да. Но, как упоминалось в документах, активация модуля остановилась на этапе активации. прикрепленная фаза, которая мне нужна, идет позже, и с использованием обещания она не срабатывает. Здесь в самом низу указан порядок обратных вызовов - ссылка - person BillyZ; 31.08.2014
comment
Что вы имеете в виду, что вам это нужно, прикрепленный всегда срабатывает после. - person PW Kad; 31.08.2014
comment
Я тоже это сказал. Поэтому, если я отправлю обещание при активации, мой прикрепленный обратный вызов не сработает до тех пор, пока обещание не будет разрешено. Мне нужно, чтобы мой модуль был подключен, чтобы он мог работать с DOM. - person BillyZ; 31.08.2014
comment
Для справки, мне не нужны ссылки на документацию Durandal, но я ценю этот жест. Также причина, по которой мы не можем понять, какое решение вы ищете, заключается в том, что мы не понимаем проблему. Я отредактировал свой вопрос с некоторыми довольно тривиальными практиками, которые должны работать в ситуации, которую, как я думаю, вы описываете. - person PW Kad; 01.09.2014