Целью этой заметки будет объяснение применения изолированных компонент в Blaze фронтендах.
Компонентами я буду называть обособленные части js/html/css кода, не зависящие от глобального состояния или от скрытого состояния внешнего кода, а зависящие только от переменных/реактивных переменных/событий, явно указанных при определении API компонента.
Update в комментариях к англоязычной версии было предложено рассмотреть библиотеку flow-components, которая следует тому же подходу, и базируется на чистом Blaze.
Объяснение, зачем это надо и почему это лучше распостраненного в сообществе подхода с глобальным Session, целью этой заметки не ставится.
Свой последний проект я решил сделать на чистом Meteor. До этого долгое время работал с AngularJS, и выработал там подход, похожий на подход React, где состояние максимально отделено от DOM компонента и максимально явно.
Для этого я использовал директивы и располагал код не по папкам директив/контроллеров/темплейтов, а по папкам для конкретной «фичи» или компонента, как описано в Johnpapa’s Angular Styleguide.
Пробуя воссоздать похожий подход для Blaze, я поставил перед собой цели:
1) Сделать реактивные параметры, которые отправляются в компонент из родительских компонентов при инициализации
2) Реализовать события, которые могут «всплыть» из дочерних компонентов.
В качестве реактивных параметров я использовал отправку ReactiveVars в компонент. Для событий был использован EventEmitter. Использование коллбеков также является возможным вариантом решения.
// client/myTemplate.js 'use strict'; var templateClass = Template.myTemplate; templateClass.onCreated(function() { // set initial state var template = this; template.data = template.data || {}; // sic! *1 template.state = { myDualBindingVar: new ReactiveVar('default'), // sic! *2 myEventEmitter: new EventEmitter(), parentEmitter: template.data.eventEmitter // now I can get parent events }; }); templateClass.helpers({ myDualBindingVar: function() { // if I want dual binding return Template.instance().state.myDualBindingVar; // sic! *3 }, myDualBinding: function() { // if I want to just pass this variable in component on initialisation or if I want to draw in in Blaze template of current component return Template.instance().state.myDualBindingVar.get(); }, eventEmitter: function() { return Template.instance().state.myEventEmitter; } });
<!-- client/myTemplate.html --> <template name="myTemplate"> <div class="my-template"> <!-- root element with class corresponding to component name --> {{myDualBinding}} {{> mySecondTemplate myDualBindingVar=myDualBindingVar eventEmitter=myEventEmitter}} </div> </template>
// client/mySecondTemplate.js 'use strict'; var templateClass = Template.mySecondTemplate; mySecondTemplate.onCreated(function() { // set initial state var template = this; template.data = template.data || {}; template.state = { myDualBindingVar: template.data.myDualBindingVar, parentEmitter: template.data.eventEmitter }; template.autorun(function() { var myDualBinding = template.state.myDualBindingVar.get(); // do stuff with parent var usage }); }); mySecondTemplate.events({ 'click .someButton': function() { var emitter = Template.instance().state.parentEmitter; emitter.emit('someButtonClicked'); } });
Объяснение кода выше:
*1: Я пишу template.data = template.data || {}; в каждом компоненте. Причина — если дан 1 или больше параметров, data будет объектом. Если параметров не дано, data будет не пустым объяктом, а null.
*2: ReactiveVar — дополнительный пакет, должен быть установлен отдельно. Я вижу множество туториалов по Метеору, где вместо этого используется Session. Я не рекомендую использовать Session, так как это глобальное состояние. Возникнут дополнительные проблемы, если на странице рисуется больше одного одинакового компонента.
*3: Чтобы получить инстанс темплейта, который используется в данном коде в данный момент, используется вызов на синглтон Template.instance(). Этот подход явно неправильный с точки зрения архитектуры, но я не нашел другого способа это сделать.
За кадром остались другие способы — использование React или Angular для фронтенда. Я опишу их в следующей статье. Скажу только, что в последнее время я перешел на использование React для фронтенда на данном проекте.
Англоязычный вариант моей статьи лежит здесь: loskutoff.com/blog/isolated-blaze-components-in-meteorjs-application/; я не стал добавлять это как перевод, так как довольно сильно изменил некоторые параграфы.
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
ссылка на оригинал статьи http://habrahabr.ru/post/254347/
Добавить комментарий