Куките на рутера Meteor Iron се пускат многократно

РЕДАКТИРАНЕ: Ето репото на github. И можете да тествате сайта тук.

На началната страница просто отворете конзолата на браузъра и ще забележите, че WaitOn и data се изпълняват два пъти. Когато няма WaitOn, тогава данните се изпълняват само веднъж.


Настроих страниците си, като разширих RouteController и допълнително разширих тези контролери. Например:

    ProfileController = RouteController.extend({
        layoutTemplate: 'UserProfileLayout',
        yieldTemplates: {
            'navBarMain': {to: 'navBarMain'},
            'userNav': {to: 'topUserNav'},
            'profileNav': {to: 'sideProfileNav'}
        },
        // Authentication
        onBeforeAction: function() {
            if(_.isNull(Meteor.user())){
              Router.go(Router.path('login'));
            } else {
                this.next();
            } 
          }
     });

ProfileVerificationsController = ProfileController.extend({
    waitOn: function() {
        console.log("from controller waitOn");
        return Meteor.subscribe('userProfileVerification');
    },

    data: function() {
        // If current user has verified email
        console.log("from controller data start");
        var verifiedEmail = Meteor.user().emails && Meteor.user().emails[0].verified ? Meteor.user().emails[0].address : '';
        var verifiedPhoneNumber = Meteor.user().customVerifications.phoneNumber && Meteor.user().customVerifications.phoneNumber.verified ? Meteor.user().customVerifications.phoneNumber.number : '';

        var data = {
            verifiedEmail: verifiedEmail,
            verifiedPhoneNumber: verifiedPhoneNumber
        };
        console.log("from controller data end");
        return data;
    }
});

При наблюдение на конзолата в клиента изглежда, че куките се изпълняват 2-3 пъти. И също така получавам грешка в един от случаите, защото данните не са налични. Следното е конзолата при еднократно заявяване на страницата:

from controller waitOn
profileController.js?966260fd6629d154e38c4d5ad2f98af425311b71:44 from controller data start
debug.js:41 Exception from Tracker recompute function: Cannot read property 'phoneNumber' of undefined
TypeError: Cannot read property 'phoneNumber' of undefined
    at ProfileController.extend.data (http://localhost:3000/lib/router/profileController.js?966260fd6629d154e38c4d5ad2f98af425311b71:46:62)
    at bindData [as _data] (http://localhost:3000/packages/iron_controller.js?b02790701804563eafedb2e68c602154983ade06:226:50)
    at DynamicTemplate.data (http://localhost:3000/packages/iron_dynamic-template.js?d425554c9847e4a80567f8ca55719cd6ae3f2722:219:50)
    at http://localhost:3000/packages/iron_dynamic-template.js?d425554c9847e4a80567f8ca55719cd6ae3f2722:252:25
    at null.<anonymous> (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2445:26)
    at http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1808:16
    at Object.Blaze._withCurrentView (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:2043:12)
    at viewAutorun (http://localhost:3000/packages/blaze.js?efa68f65e67544b5a05509804bf97e2c91ce75eb:1807:18)
    at Tracker.Computation._compute (http://localhost:3000/packages/tracker.js?517c8fe8ed6408951a30941e64a5383a7174bcfa:296:36)
    at Tracker.Computation._recompute (http://localhost:3000/packages/tracker.js?517c8fe8ed6408951a30941e64a5383a7174bcfa:310:14)
from controller data start
from controller data end
from controller waitOn
from controller data start
from controller data end

Не съм ли използвал контролерите правилно?


person Ayrton Senna    schedule 12.03.2015    source източник


Отговори (1)


Без да мога да видя останалата част от дефинирания от вас код, който използва тези контролери на маршрути (като шаблони или дефиниции на маршрути), не мога да говоря точно за причината за многократното извикване на функцията за данни. Подозирам, че може да използвате ProfileVerificationsController с множество маршрути, в който случай дефиницията data за този контролер ще бъде изпълнена няколко пъти, по един за всеки маршрут, който използва контролера. Тъй като дефиницията data е реактивна, докато преглеждате вашето приложение и промените в данните, това може да доведе до дефинирания за повторно изпълнение код.

Що се отнася до вашите дефиниции на контролера, бих предложил да направите няколко модификации, за да направите кода по-здрав и брониран. Първо, дефиницията ProfileController:

    ProfileController = RouteController.extend({
        layoutTemplate: 'UserProfileLayout',
        yieldRegions: {
            'navBarMain': {to: 'navBarMain'},
            'userNav': {to: 'topUserNav'},
            'profileNav': {to: 'sideProfileNav'}
        },
        onBeforeAction: function() {
            if(!Meteor.user()) {
                Router.go(Router.path('login'));
                this.redirect('login'); // Could do this as well
                this.render('login'); // And possibly this is necessary
            } else {
                this.next();
            }
        }
    });

Забележете първото нещо, което промених, yieldTemplates на yieldRegions. Тази правописна грешка би попречила на регионите от вашите шаблони, използващи този контролер за маршрути, да бъдат правилно запълнени с желаните подшаблони. Второ, в дефиницията onBeforeAction бих предложил да се провери не само дали обектът Meteor.user() е null, използвайки долна черта, но също така да се провери дали е undefined или не. Модификацията, която направих, ще ви позволи да проверите и двете състояния на обекта Meteor.user(). И накрая, не толкова корекция на печатна грешка, колкото алтернативно предложение за насочване на потребителя към маршрута login, можете да използвате функциите this.redirect() и this.render() вместо функцията Router.go(). За допълнителна информация относно всички налични опции, които могат да бъдат дефинирани за маршрут/контролер на маршрут, проверете това навън.

Сега за дефиницията ProfileVerificationsController:

    ProfileVerificationsController = ProfileController.extend({
        waitOn: function() {
            return Meteor.subscribe('userProfileVerification');
        },
        data: function() {
            if(this.ready()) {
                var verifiedEmail = Meteor.user().emails && Meteor.user().emails[0].verified ? Meteor.user().emails[0].address : '';
                var verifiedPhoneNumber = Meteor.user().customVerifications.phoneNumber && Meteor.user().customVerifications.phoneNumber.verified ? Meteor.user().customVerifications.phoneNumber.number : '';

                var data = {
                    verifiedEmail: verifiedEmail,
                    verifiedPhoneNumber: verifiedPhoneNumber
                };
                return data;
            }
        }
    });

Обърнете внимание на едно нещо, което промених, което е да обвия целия ви код, дефиниран в опцията data за вашия контролер с if(this.ready()){}. Това е критично, когато използвате опцията waitOn, тъй като опцията waitOn добавя един или повече манипулатори за абонамент към списък с чакащи за маршрута и проверката this.ready() връща true само когато всички манипулатори в списъка с чакащи са готови. Използването на тази проверка ще предотврати всякакви случаи на неочаквано зареждане на данни, когато изграждате своя контекст на данни за маршрута. За допълнителна информация относно определянето на абонаменти за вашите маршрути/контролери на маршрути проверете това навън.

Като последно предложение, за вашата дефиниция на опция onBeforeAction във вашия ProfileController, бих предложил да преместите това в собствена глобална кука по следния начин:

    Router.onBeforeAction(function() {
        if(!Meteor.user()) {
            Router.go(Router.path('login'));
        } else {
            this.next();
        }
    });

Дефинирането на тази проверка в глобалната кука гарантира, че не е нужно да се притеснявате за добавяне на вашия ProfileController към всички ваши маршрути, само за да сте сигурни, че тази проверка се изпълнява за всички тях. Проверката ще се изпълнява за всеки маршрут всеки път, когато има достъп до него. Само едно предложение обаче, тъй като може да имате причини да не правите това. Просто исках да го предложа, тъй като се уверявам, че го правя за всяко приложение Meteor, което разработвам за допълнителна сигурност.

person Keith Dawson    schedule 13.03.2015
comment
Благодаря за изчерпателния отговор. Ще обърна внимание на нещата, които споменахте. Просто съм малко нов в Meteor, както виждате :). Както и да е, актуализирах въпроса с Github repo и тестовия сайт. Куките все още се изпълняват два пъти дори след промените. - person Ayrton Senna; 13.03.2015
comment
Няма проблем, напълно разбирам. Проверих вашия сайт и виждам как функциите waitOn и data се изпълняват два пъти на началната страница. Може ли да предоставите вашия код, който използва тези контролери, по-специално вашия код за дефиниране на маршрут? Без да можем да видим това, е трудно да се определи точната причина за изпълнението на логиката два пъти. - person Keith Dawson; 14.03.2015
comment
Разбира се, поставих репото в Github. Ето файла с маршрута - github.com/niranjans/ testRouter/blob/master/testRouter/lib/ - person Ayrton Senna; 14.03.2015
comment
Моите извинения, напълно пропуснах да поставите тази връзка там по-рано. Мислех това по-рано, но сега съм сигурен, че е свързано с факта, че използвате Meteor.user() в опцията data на контролера на маршрута. Meteor.user() е реактивен източник на данни, за който можете да прочетете тук. По принцип логиката за маршрутизиране се изпълнява веднъж първоначално, когато страницата се зареди, и се изпълнява отново, след като се заредят данните от реактивния източник на данни. Ето защо виждахте грешката undefined, защото при първото зареждане данните все още не съществуваха на клиента. - person Keith Dawson; 14.03.2015
comment
Можете да разберете, че това е така, когато гледате как се отпечатват съобщенията на конзолата. Страницата се зарежда първа, като първоначално се изпълнява логиката за маршрутизиране. Първият набор от конзолни съобщения се отпечатва. По това време се показва въртящият се шаблон за зареждане, докато маршрутът чака данните от Meteor.user() да стигнат до клиента. След като данните пристигнат, логиката за маршрутизиране се изпълнява отново, този път с всички данни на място. След това се отпечатва вторият набор от конзолни съобщения. - person Keith Dawson; 14.03.2015
comment
вярно Забелязах как Meteor.user отнема малко време за зареждане. И това води до повторно изпълнение на изчислението. Това наистина има смисъл. Така че по принцип това не е проблем и е доста често срещан сценарий? Във функцията data трябва просто да проверя кога Meteor.user() е зареден и след това да изтегля цялата друга информация като имейл или phoneNumber и т.н.? - person Ayrton Senna; 14.03.2015
comment
Точно така, това е просто нормално поведение на Метеор и наистина много често. В опцията data не забравяйте да обвиете целия си код в израз if(this.ready()){}, ако чакате някакви абонаменти, както сте в това приложение. С тази проверка можете да сте сигурни, че данните, за които сте се абонирали, както и вашият Meteor.user() обект са разрешени и заредени на клиента. Моля, не забравяйте да маркирате отговора ми като правилен, ако съм помогнал да отговоря на вашите въпроси. И се надявам да се насладите на бъдещето си с Meteor и да продължите да се наслаждавате да научавате повече за него! :-) - person Keith Dawson; 14.03.2015
comment
Схванах го. Наистина оценявам помощта ти. - person Ayrton Senna; 14.03.2015