bem-site / bem-forum-content-ru

Content BEM forum for Russian speak users
MIT License
56 stars 6 forks source link

Singletone Service i-bem #169

Closed KlonD90 closed 9 years ago

KlonD90 commented 9 years ago

Здравствуйте. Хочу сделать сервисный блок, чтобы блок фактически генерил только один элемент и при повторном обращение его просто обновлял, но генерил эту штуку только если его вообще потребовали. Столкнулся с проблемой, что я без понятия как засейвить этот элемент и как с ним правильно обращаться. В сам по себе блок не хочется вставлять эту логику, так что синглтонизацию вынес в отдельный модуль, он инитится(я пользуюсь bem-stack'ом и инитится он i-bemdom) и выводится, но как его дальше взять я без понятия, и как послать событие от моего модуля к таким образом сгенеренному блоку тоже без понятия. Помогите плиз ._.

И еще вопрос в догонку есть ли какой-то cheatsheet с шаблонами реализации для i-bem. И еще один вопрос в http://ru.bem.info/technology/i-bem/v2/i-bem-js/ тут написано Пример: Вызов статического метода close блока popup — закрыть все попапы на странице: можно полностью этот пример, а то я пытался как-то реализовать, но если метод статический то никакой this.delMod('visible') не сработал и что либо еще не смог сделать с объектом, нужно видимо еще какие-то настройки в случае объявление такого делать, можно их привести полностью и сам метод close?

qfox commented 9 years ago

Если я правильно понял, то:

// первый раз:
if (!this.elem('your-elem')) {
  DOM.update(
    this.domElem,
    BEMHTML.apply({ block : 'your-block', elem : 'your-elem', content : 'your-content' })
  );
  this.dropElemCache('your-elem');
}
this.elem('your-elem').html('asdasdasdasd');

Если нужно генерировать блок динамически:

var bHtml = BEMHTML.apply({ block : 'your-block', content : 'your-content' });
var initedBlock = BEMDOM.init($(bHtml));

Послать событие:

initedBlock.emit('sobytie');

Про попапы. Судя по всему, упущен следующий момент:

BEMDOM.decl('popup', {
  onSetMod : {
    'js' : function () {
      this.__self.popups.push(this); // сохраняем инстанс
    }
  },
  close : function () {
    // закрываем попап
  }
}, {
  popups : [],
  close : function () {
    this.popups.forEach(function (el) { el.close(); });
  }
});

Вроде, не сильно наврал ;)

KlonD90 commented 9 years ago

Спасибо, со статическим методом понял момент :) Вот что-то не сильно работает такой способ брать динамически объект. После init он возвращает просто jQuery объект, видимо придется сделать через этот статический способ чтобы его получить или есть какой-то метод получше?

tadatuta commented 9 years ago

@KlonD90 Получить инстанс БЭМ-блока в примере от @zxqfox можно так:

var bHtml = BEMHTML.apply({ block : 'your-block', content : 'your-content' });
var initedBlock = BEMDOM.init($(bHtml));
var yourBlock = initedBlock.bem('your-block');

Еще можно использовать findBlock*-методы и передать им в качестве контекста jQuery-chain, который вернется из init().

Но вообще мне кажется, что исходную задачу нужно решать чуть по-другому. Вместо того, чтобы вставлять блок в DOM на первое обращение, потом находить и обновлять на последующие, лучше изначально вставить его в DOM, но инициализировать в момент первого обращения и дальше обновлять. И API прямее получится и лишних депендов на клиент не тянуть и кода меньше писать ;)

KlonD90 commented 9 years ago

@tadatuta Спасибо, так заработало :)

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

qfox commented 9 years ago

@KlonD90 Я бы сделал так (и кажется, что @tadatuta предлагает примерно тож самое):

[
  ...,
  { block : 'your-hidden-block' }
  ...
]
.your-hidden-block { display : none }
.your-hidden-block_js_inited { display : block }
// ...
BEMDOM.decl('your-hidden-block', {
  onSetMod : {
    'js' : function () {
      // initialization
      console.log('inited');
    }
}, {
  live : true
});

inited в этом случае выведется только тогда, когда кто-то явно позовет твой блок. Например, $('.your-hidden-block').bem('your-hidden-block').

p.s. Я не знаю, используешь ли ты bem-core с модулями, или i-bem без всего, поэтому не заворачивал в модули. Если используешь, то чтобы получить $ нужно будет пример выше завернуть в modules.require(['jquery'], function ($) { ... });, и блок с BEMDOM в modules.define('your-hidden-block', ['i-bem__dom'], function (provide, BEMDOM) { provide(BEMDOM...); });`

@tadatuta спасибо за правку, забыл, что DOM.init возвращает jquery-chain.

qfox commented 9 years ago

По поводу

Так чтобы по условию если эта библиотека есть в депсе то в body вставлялся блок куском хтмля с указанием того чтобы он относился к этому блоку?

Не очень понятно, в каком именно депсе он может быть. Если в том депсе, что собирается из bemjson — то вариант из предыдущего сообщения. Если в каком-то другом — то у тебя уже зависимость от этого блока ЯВНО описана и вставить твой блок можно в том блоке, который его просит, и «не порождая какой-то скрытой зависимости» при этом соблюдается.

Для второго варианта можно вставить, например, через доопределение page. Для этого мы в page.bemhtml или page.bh (в зависимости от того, что у тебя используется) должны будем на уровне проектный блоков описать шаблоны примерно такого толка:

block('page').content()([
  applyNext(), // или applyCtx?
  { block : 'your-hidden-block' }
]);

или в bh:

module.exports = function(bh) {
  bh.match('page', function (ctx) {
    ctx.content([
      ctx.content(),
      { block : 'your-hidden-block' }
    ]);
  });
};

При этом css и js можно взять из предыдущего примера.

KlonD90 commented 9 years ago

@zxqfox Спасибо :) Хочется выработать подход какой-то правильный, а если его вставлять в каждый блок то не получится синглтон. Ну в принципе да видимо сделаю так что вставлю в page, а потом .bem инициализирую.

qfox commented 9 years ago

@KlonD90 Я честно не очень понял что именно ты хочешь, но еще есть вариант сделать этот блок, например, элементом page или просто слушать события на page, и инициализировать блок по событиям на page. Хотя, на мой взгляд это мало чем отличается от blocks['your-hidden-block'].staticMethod() в котором ты свой блок и позовешь, а он инициализируется, если надо, и сделает что нужно.