BEMは、迅速に作成して長期間維持する必要があるサイトの開発を簡素化します。 このテクノロジーは、ほぼすべてのYandexサービスのフロントエンドで使用されており、すでに多くのライブラリとツールを取得しています。
 
      この記事では、独立したブロックのレイアウトの利点とオーバーライドレベルについて説明し、既製のブロックライブラリとアセンブリを自動化するツールについて説明します。 autoprefixer 、css-preprocessor Stylus 、モジュラーシステムYModuleなどのさまざまなツールが開発者の生活をどのように簡素化し、BEM開発プロセスに組み込むと本当に便利なプラットフォームを作成するかを示します。
生きた例を使用して、CSSとJavaScriptの両方に同じアイデアを使用できる場合、宣言型アプローチの使用方法を説明します。 宣言型テンプレートBEMHTMLおよびBEMTREEについて個別に説明します。これらのテンプレートにより 、データをBEMJSON形式で記述されたHTMLで記述されたBEMツリーに変換できます。 BEM方法論に従って、アプリケーションのサーバー部分を作成する方法を詳細に検討してみましょう。
Twitter APIを使用してプロジェクトを作成します。 その結果、BEMテクノロジーの完全なスタックに関する作業サイトと、これらすべてを再現する方法に関する段階的な記事を入手できます。
特にマスタークラスについては、さまざまなソーシャルネットワークを検索し、結果を整然とした形式で表示するミニサービスを作成しました。 github.com/bem/sssrリポジトリのgithubに投稿しました-を参照してください 。
そして順番に行きます。
理論
BEMとは
BEM(単語の略語-ロック、要素、および修飾子)は、特定の実装テクノロジに関連付けられていないエンティティを記述する方法であるプログラムとインターフェイスを開発するための方法論です。
- ブロックは、アプリケーションの独立したコンポーネントです。 他のブロックから独立しており、他のブロックや要素を含む場合があります。
 - 要素は、個別の機能を担当するブロックの一部です。 ブロックから分離しても意味がありません。
 - 修飾子は、その外観または動作を担当するブロックまたは要素のプロパティです。 修飾子は、ブロックまたは要素の状態を記述します。
 
BEMは、DOMツリーを抽象化します。 ブロックは互いに独立しており、すべての機能と要素をカプセル化しています。 ブロックが実装されるHTMLタグ(
div
      
      または
form
      
      )は重要ではありません。これをいつでも変更したり、ラッパーを追加したりできます。 変更が残りのブロックに影響することはありません。 HTMLタグではなく、インターフェイスコンポーネントを使用してアプリケーションを説明します。
各ブロックは、ファイルシステム内の独自のフォルダーにあり、ブロック、その要素、および修飾子を記述するすべてのテクノロジーが含まれています。
 desktop.blocks/ input/ __box/ #  __clear/ #  __control/ #  _focused/ #  _type/ #  input.css # css   input.js # js   input.ru.md # markdown  …
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      BEMがどのように、なぜ登場したかについて詳しく知りたい場合は、Vitaly Kharisovの記事「 BEMの歴史 」を読み、 ビデオレポートをご覧ください。
BEM方法論の詳細な説明は、 当社のWebサイトにあります。
プロジェクトブランクの作成
仕事に必要なものをすべてインストールします。
まず、ターミナルと
git
      
      バージョン管理システムが必要です。 git-scm.comからインストールできます。
ほとんどすべてのツールはJavaScriptで記述されているため、 node.jsまたはio.jsが必要になります。
プロジェクトのブランクを作成するには、 generator-bem-stub generatorを使用します。
 > npm install -g generator-bem-stub
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      次に、ジェネレーター自体を実行します。
 > yo bem-stub
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      使用されている技術に関する質問に答えると、組み立てのために事前に組み立てられ、構成されたブランクが得られます。
次の問題を調べてみましょう。
      スクリーンショットは、質問に対する回答の結果を示しています。 最初の3つの質問は明らかです。その後、興味深い部分が始まります。
-   
Choose a toolkit to build the project
(使用するコレクター) -ENBツールを使用します。 これは、プロジェクトが組み立てるユーティリティです。スタイル、スクリプト、テンプレートを接着し、ページ宣言、ブロックの依存関係、構成ファイルに従ってコンパイルおよび最適化します。 -   
Specify additional libraries if needed
します(追加のライブラリを使用しますか)-このプロジェクトでは、 bem-componentsブロックライブラリを使用します。 オプションのスタイルテーマがあります。 
 
      
      何かを検討する時が
 
      
      。
再定義レベル
これはブロック実装のセットです。 プロジェクトには複数のレベルがあり、各レベルでブロックの実装が追加または変更されます。 ブロックの最終実装は、指定された順序ですべてのレベルから順番に収集されます。
プロジェクトの再定義レベルで、スタイル、テンプレート、ブロックのJavaScript実装を再定義および再定義できます。 同時に、ライブラリのソースファイルには何も変更しないため、更新された場合は変更を保存できます。
これがファイルシステムでどのように見えるかの例を次に示します。
 … libs/ bem-components/ desktop.blocks/ input/ input.css desktop.blocks/ input/ input.css …
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      プロジェクトの
desktop.blocks
      
      レベルでブロックを作成することにより、必要なテクノロジーをオーバーライドまたはオーバーライドできます。
上記の例では、
CSS
      
      テクノロジーに実装を追加することにより、
input
      
      ブロックのスタイルを編集できます。
これで、ドラフトプロジェクトの準備ができました。 プロジェクトディレクトリに移動しましょう。
 > cd sssr-tutorial
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      レイアウト
まず、ページの静的プロトタイプを作成します。 その構造を説明するために、 BEMJSONテクノロジーを使用しています。
BEMJSONは、BEMツリーを説明します。ブロックの順序とネスト、BEMエンティティの名前と状態、追加の任意のフィールド。
生成されたプロジェクトを収集して、何が起こったのか見てみましょう。 ローカルにインストールされた
ENB
      
      パッケージを使用して便利に作業するには、次のコマンドを実行する必要があります。
 > export PATH=./node_modules/.bin:$PATH
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      または、。
./node_modules/.bin/
      
      サブディレクトリから手動で
enb
      
      コマンドを実行します
ビルドするには、
enb server
      
      コマンドを使用し
enb server
      
      。
 > enb server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      http:// localhost:8080 / desktop.bundles / index / index.html。コレクターは、必要なすべての依存関係を収集し、それらから必要なブロックとテクノロジーのファイルを収集します。
      ブラウザでインスペクターを開き、DOMツリーを確認します。 まだコードを記述していませんが、このページには既にHTMLが生成されています。 これは、ライブラリのテンプレートが使用されているためです。 たとえば、
bem-core
      
      ライブラリの
page
      
      ブロックテンプレートは、ページ
doctype
      
      (
doctype
      
      、
html
      
      、
head
      
      、
body
      
      など)を生成します。
プロジェクトの
./desktop.bundles/index/
      
      フォルダーに
index.bemjson.js
      
      ファイルが含まれて
./desktop.bundles/index/
      
      ます。
 ({ block: 'page', title: 'Hello, World!', styles: [ { elem: 'css', url: 'index.min.css' } ], scripts: [ { elem: 'js', url: 'index.min.js' } ], content: [ 'Hello, World!' ] })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      このファイルは、BEM用語でのページの説明です。 BEMツリーのルートブロックは
page
      
      です。
favicon
      
      追加キーワード-
title
      
      、
favicon
      
      などがあります。 このブロックのテンプレートはbem-coreライブラリにあります。
このアプリケーションは、キャップとコンテンツという2つの主要部分で構成されています。
sssr
      
      ブロックをページコンテンツに追加します。このコンテンツでは、インターフェイスの一部が要素として記述されます。 これを行うには、.
./desktop.bundles/index/index.bemjson.js
      
      /
./desktop.bundles/index/index.bemjson.js
      
      /
./desktop.bundles/index/index.bemjson.js
      
      編集し
./desktop.bundles/index/index.bemjson.js
      
      。
 ({ block: 'page', //… content: [ { block: 'sssr', content: [ { elem: 'header' }, { elem: 'content' } ] } ] });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ヘッダーには、検索フォームとロゴ付きのサイト名が含まれます。
 { block: 'sssr', content: [ { elem: 'header', content: [ { elem: 'logo', content: 'Social Services Search Robot:' }, { block: 'form', content: [ { elem: 'search' }, { elem: 'filter', content: '[x] twitter' } ] } ] }, { elem: 'content' } ] }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      
      bem-components
      
      ライブラリの
input
      
      、
button
      
      、
spin
      
      、および
checkbox
      
      ブロックを使用し
input
      
      。 プロジェクトでは、このライブラリは
./libs/bem-components
      
      フォルダーにあります。 これらの各ブロックには独自のAPIがあり、API はドキュメントに記載されています 。
BEMJSONに必要なブロックを追加します。
 { block: 'sssr', content: [ { elem: 'header', content: [ { elem: 'logo', content: [ { block: 'icon', mods: { type: 'sssr' } }, 'Social Services Search Robot:' ] }, { block: 'form', content: [ { elem: 'search', content: [ { block: 'input', mods: { theme: 'islands', size: 'm', 'has-clear' : true }, name: 'query', val: '#b_', placeholder: 'try me, baby!' }, { block: 'button', mods: { theme: 'islands', size: 'm', type: 'submit' }, text: '' }, { block: 'spin', mods: { theme: 'islands', size : 's' } } ] }, { elem: 'filter', content: '[] twitter [] instagram' } ] } ] } ] }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      mods
      
      フィールドはこのBEMJSONスニペットにあります。 使用される修飾子とその意味を示します。
mods
      
      フィールドには
: 
      
      -
mods: { type: 'sssr' }
      
      ます。
BEMJSONで任意のJavaScript式を使用できます。
checkbox
      
      ブロックを繰り返すための
map
      
      コンストラクトを
filter
      
      要素の
content
      
      フィールドに追加し
map
      
      。
 //… { elem: 'filter', content: ['twitter', 'instagram'].map(function(service) { return { block: 'checkbox', mods: { theme: 'islands', size: 'l', checked: service === 'twitter' }, name: service, text: service }; }) } //…
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      完全な
index.bemjson.js
      
      ファイル:
 ({ block: 'page', title: 'Social Services Search Robot', favicon: '/favicon.ico', head: [ { elem: 'meta', attrs: { name: 'description', content: 'find them all' }}, { elem: 'css', url: '_index.css' } ], scripts: [{ elem: 'js', url: '_index.js' }], content: { block: 'sssr', content: [ { elem: 'header', content: [ { elem: 'logo', content: [ { block: 'icon', mods: { type: 'sssr' } }, 'Social Services Search Robot:' ] }, { block: 'form', content: [ { elem: 'search', content: [ { block: 'input', mods: { theme: 'islands', size: 'm', 'has-clear' : true }, name: 'query', val: '#b_', placeholder: 'try me, baby!' }, { block: 'button', mods: { theme: 'islands', size: 'm', type: 'submit' }, text: '' }, { block: 'spin', mods: { theme: 'islands', size : 's' } } ] }, { elem: 'filter', content: ['twitter', 'instagram'].map(function(service) { return { block: 'checkbox', mods: { theme: 'islands', size: 'l', checked: service === 'twitter' }, name: service, text: service }; }) } ] } ] }, { elem: 'content' } ] } })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      インターフェイスの構造を説明した後、ブロックのスタイルを記述および再定義する必要があります。 すべての主要なスタイルは、
bem-components
      
      ライブラリで提供されます。 そのため、かなり追加する必要があります。
Stylus CSSプリプロセッサを使用してスタイルを記述します。
*.styl
      
      を持つすべてのファイルは、プリプロセッサによって処理され、最終的なCSSファイルに接着されます。 プリプロセッサで処理する必要のないスタイルには、
*.css
      
      拡張子を使用することもでき
*.css
      
      。
ファイル
./desktop.blocks/form/form.styl
      
      form
      
      ブロックのスタイルを
./desktop.blocks/form/form.styl
      
      します。
 .form { display: flex; &__search { margin-right: auto; } .input { width: 400px; } .checkbox { display: inline-block; margin-left: 15px; user-select: none; vertical-align: top; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ./desktop.blocks/page/page.css
      
      ファイルの
page
      
      ブロックの場合:
 .page { font-family: Tahoma, sans-serif; min-height: 100%; margin: 0; padding-top: 100px; background: #000; }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ファイル
./desktop.blocks/sssr/sssr.styl
      
      sssr
      
      ブロックの
./desktop.blocks/sssr/sssr.styl
      
      :
 .sssr { &__header { position: fixed; z-index: 1; top: 0; box-sizing: border-box; width: 100%; padding: 10px 10%; background: #f6f6f6; box-shadow: 0 0 0 1px rgba(0,0,0,.1), 0 10px 20px -5px rgba(0,0,0,.4); .button { margin-left: 10px; } } &__logo { font-size: 18px; margin: 0 0 10px; } &__content { padding: 10px 10%; column-count: 4; column-gap: 15px; transition: opacity .20s linear; } a[rel='nofollow'], a[xhref], [name][server] { text-decoration: none; color: #038543; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      そして、
user
      
      ブロック
desktop.blocks/user/user.styl
      
      :
 .user { &__name { display: inline-block; margin-right: 10px; text-decoration: none; color: #000; &:hover { text-decoration: underline; color: #038543; } } &__post-time { font-size: 14px; display: inline-block; color: #8899a6; } &__icon { position: absolute; right: 5px; bottom: 5px; width: 30px; height: 30px; border-radius: 3px; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      CSSレイアウトの問題については詳しく説明しません。先に進みましょう。
見つかったメッセージにブロックを追加することは残ります。 それらを
index.bemjson.js
      
      説明し、JavaScript機能を使用してプロトタイプを作成します。
 { elem: 'content', content: (function() { return 'BEM is extermly cool'.split('').map(function() { var service = ['twitter', 'instagram'][Math.floor(Math.random()*2)]; return { service: service, user: [{ login: 'tadatuta', name: 'Vladimir', avatar: 'https://raw.githubusercontent.com/bem/bem-identity/master/sign/_theme/sign_theme_batman.png' }, { login: 'dmtry', name: 'Dmitry', avatar: 'https://raw.githubusercontent.com/bem/bem-identity/master/sign/_theme/sign_theme_captain-america.png' }, { login: 'sipayrt', name: 'Jack Konstantinov', avatar: 'https://raw.githubusercontent.com/bem/bem-identity/master/sign/_theme/sign_theme_ironman.png' }, { login: 'einstein', name: 'Slava', avatar: 'https://raw.githubusercontent.com/bem/bem-identity/master/sign/_theme/sign_theme_robin.png' }][Math.floor(Math.random()*4)], time: Math.floor((Math.random()*12)+1) + 'h', img: service === 'instagram' ? 'http://bla.jpg' : undefined, text: [ ' —    .       (  ).', ' —    .', '        .'][Math.floor(Math.random()*3)] }; }).map(function(dataItem) { return { block: 'island', content: [ { elem: 'header', content: { block: 'user', content: [ { block: 'link', mix: { block: 'user', elem: 'name' }, url: 'https://www.yandex.ru', target: '_blank', content: dataItem.user.name }, { elem: 'post-time', content: dataItem.time }, { block: 'image', mix: { block: 'user', elem: 'icon' }, url: dataItem.user.avatar, alt: dataItem.user.name } ] } }, { elem: 'text', content: dataItem.text }, { elem: 'footer', content: [ { block: 'service', mods: { type: dataItem.service } } ] } ] }; }); })() }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      island
      
      ブロックのスタイルを
./desktop.blocks/island/island.styl
      
      ファイルに
./desktop.blocks/island/island.styl
      
      ます。
 .island { font-size: 18px; line-height: 140%; position: relative; display: inline-block; box-sizing: border-box; width: 100%; margin-bottom: 15px; padding: 15px 5px 5px 15px; border-radius: 3px; background: #fff; box-shadow: inset 0 0 1px rgba(0, 0, 0, .4); &__footer { margin-top: 10px; } &__image { display: block; width: 100%; border-radius: 3px; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      結果を見てみましょう:
      BEMHTMLテンプレートエンジン
宣言的な標準化
Yandexは宣言性が非常に好きです-CSSだけでなく、テンプレートとJavaScriptでも。
CSSの宣言性は次のようになります。
 .menu__item { display: inline-block; }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      menu
      
      display: inline-block;
      
      すべての
item
      
      要素に対して
display: inline-block;
      
      スタイルが適用され
display: inline-block;
      
      、つまり 処理方法を宣言します
条件によって選択されたDOMノード:
{ }
条件に一致するDOMツリーのすべてのノードを選択し、テンプレート本体をそれらに適用します。
宣言的なテンプレート化のために、Yandexは独自のBEMHTMLテンプレートエンジンを作成しました。 アーキテクチャの詳細については、記事bem-coreのデータテンプレートを参照してください。
BEMHTMLの宣言型テンプレートの例:
 block('menu').elem('item').tag()('span');
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      条件に一致するすべてのBEMツリーブロックが選択され、テンプレート本体がそれらに適用されます。
()( )
BEMHTMLはJavaScriptで記述されています。 その構文は純粋なJavaScriptです。 JavaScript関数をサブ述語とテンプレートの本文で使用できます。 プロダクションモードの場合、テンプレートは最適化されたJavaScriptにコンパイルされます。
BEMHTMLは、BEMツリーをHTML文字列に変換する方法を担当します。 入力は、BEMツリーまたはBEMJSONテクノロジで記述されたそのフラグメントです。 このBEMJSONは、BEMHTMLテンプレートに重ねられます。 そして、出力はHTML文字列です。
一般的に、テンプレートは次のとおりです。
 match(1, 2, 3)();
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      サブレディケートは、パターンが適用される条件です。 例:
 match(1, 2, 3)();
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      このテンプレートは、現在のブロックが
link
      
      ブロックであるか、
this.ctx
      
      のコンテキストに
url
      
      変数があるかどうか、および現在のモードが
tag
      
      modであるかどうかを
this.ctx
      
      ます。 これらのすべての条件が満たされると、タグがブロックに適用されます。
ファッション
ファッションは、HTML出力を生成するステップです。 各modは、結果のHTMLコードの独自の部分を担当します。
default
      
      モードは、残りのモードの通過のセットと順序を記述します。 この図は、各モードの役割を示しています。
      BEMHTMLテンプレートエンジンリファレンスガイドに記載されているBEMHTMLドキュメントを注意深く読むことをお勧めします。
プロジェクトに戻りましょう。
form
      
      ブロックが必要です。
<form>
      
      として表示され、
JavaScript
      
      実装が必要です。
このようなブロックをページにもう1つ追加する場合、BEMJSONファイルでこれらのパラメーターを直接編集する必要があります。 これは、HTMLでのインラインスタイルの使用に似ています。 すべてを正しく行い、ブロックパラメーターをテンプレートに入れましょう。
./desktop.blocks/form/form.bemhtml
      
      :
 block('form')( tag()('form'), js()(true) );
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      これで、ブロックテンプレートを1か所で編集し、このブロックを簡単に転送して再利用できます。
インスペクターでDOMツリーを見てみましょう-
form
      
      ブロックは、
i-bem
      
      クラスの
<form>
      
      として表示されるようになりました。 このクラスは、ブロックにJavaScriptの実装があることを示しています。
      BEMブロックをHTMLに変換する方法について説明しました。 それでは、twitterデータがどのように受信され、処理されるかを見てみましょう。
アプリケーションアーキテクチャ
2段階の標準化
アプリケーションは次のように機能します。
- 最初の段階では、サービスからデータを収集し、このデータに基づいてBEMツリーを構築します。
 - 2番目では、BEMツリー(ビュー指向データ)をDOMツリーに変換し、HTMLをクライアント側にレンダリングします。
 
ベントレー
BEMツリーをHTMLに変換する方法について説明しました。 これはフロントエンドサーバーのタスクです。 また、BEMTREEテンプレートエンジンは、BEMツリーを構築してデータで飽和させるタスクを処理します。 BEMHTMLと同じ構文です。 主な違いは、利用可能な標準modの数です。 BEMTREEには、
default
      
      と
content
      
      のみがあり
default
      
      。
BEMTREEへの入力は、ブロックテンプレートが飽和している生データです。 出力では、BEMツリーの既製のフラグメントを取得し、BEMHTMLテンプレートに渡します。
戦いに直行します。
{ type: 'twitter' }
      
      修飾子、
island
      
      ブロックのBEMTREEテンプレートを書きましょう:
desktop.blocks/island/_type/island_type_twitter.bemtree
      
       block('island').mod('type', 'twitter').content()(function() { var data = { postLink: '#', userName: 'user@name', userNick: 'user@nick', createdAt: '19 of July', avatar: '#avatar', text: 'message going here', type: 'twitter' }; return [ { elem: 'header', content: { block: 'user', content: [ { block: 'link', mods: { theme: 'islands' }, mix: { block: 'user', elem: 'name' }, url: data.postLink, content: [data.userName, ' @', data.userNick] }, { elem: 'post-time', content: data.createdAt.toString() }, { block: 'image', mix: { block: 'user', elem: 'icon' }, url: data.avatar, alt: data.userName } ] } }, { elem: 'text', content: data.text }, { elem: 'footer', content: [ { block: 'service', mods: { type: data.type } } ] } ]; });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      このブロックのコンテンツに必要なパラメーターを指定して
image
      
      ブロックを転送し、
island
      
      ブロックの
image
      
      要素を混合します。
将来、静的オブジェクトをテンプレートに渡されるデータに置き換えます。 しかし、最初に、サーバーコードの編成方法と、このデータの送信方法を見てみましょう。
サーバー上
このアプリケーションは、 エクスプレスフレームワークで動作します-検索クエリに応答してHTMLをレンダリングします。
サービスからデータを収集するブロックを作成します。
*.node.js
      
      持つファイルにサーバーコードを記述します。これは、アセンブリ中に1つのファイルに接着されます。
node.js
      
      を使用して起動し
node.js
      
        service_type_twitter
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ブロック 
      twitterでの作業を簡単にするために、 twitモジュールを使用します。
npm
      
      を使用してインストールします。
 > npm i twit --save
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
      
      twitterでの作業に必要な認証データは、 別のファイルに入れます 。 同じ名前のファイルでその内容を自分自身にコピーします。
./desktop.blocks/service/_type/service_type_twitter.node.js
      
      編集し
./desktop.blocks/service/_type/service_type_twitter.node.js
      
      。
 var twitter = require('twit'), config = require('./service_type_twitter.config'), twit = new twitter(config); var query = '#b_', results = []; twit.get('search/tweets', { q: query, count: 20 }, function(err, res) { if (err) { console.error(err); return []; } results = res.statuses.map(function(status) { var user = status.user; return { avatar: user.profile_image_url, userName: user.name, userNick: user.screen_name, postLink: 'https://twitter.com/' + user.screen_name, createdAt: status.created_at, text: status.text, type: 'twitter' }; }); console.log(results); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      このアプリケーションは、キーワード
#b_
      
      を検索し、結果をコンソールに表示します。
プロジェクトを再構築し、
node.js
      
      を使用して実行します
 > enb make > node ./desktop.bundles/index/index.node.js
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      実行の結果は、コンソールのツイートのリストになります。
ここで、さらなる作業のために、実行の結果を何らかの方法で転送する必要があります-標準化とクライアントへの転送。
promiseを使用した非同期作業には、 vowライブラリを使用します。
サーバーとクライアントのJSコードの構成-モジュラーシステムYModules 。
モジュラーシステム
bem-core
      
      ライブラリは、モジュラーシステムymodulesを使用します。
ブロックのコードをモジュールラッパーにラップし、必要に応じて他のモジュールから呼び出すことができます。
次の追加に従って
service_type_twitter.node.js
      
      ファイルを編集します。
 modules.define('twitter', function(provide) { var vow = require('vow'), moment = require('moment'), twitter = require('twit'), twitterText = require('twitter-text'), config = require('./service_type_twitter.config'), twit = new twitter(config); provide({ get: function(query) { var dfd = vow.defer(); twit.get('search/tweets', { q: query, count: 20 }, function(err, res) { if(err || !res.statuses) { console.error(err); dfd.resolve([]); } dfd.resolve(res.statuses.map(function(status) { return { avatar: status.user.profile_image_url, userName: status.user.name, userNick: status.user.screen_name, postLink: 'https://twitter.com/' + status.user.screen_name, createdAt: moment(status.created_at), text: twitterText.autoLink(twitterText.htmlEscape(status.text)), type: 'twitter' }; })); }); return dfd.promise(); } }); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ご覧のとおり、すべてのコードを
modules.define
      
      コンストラクトにラップしました。 これは
twitter
      
      モジュール宣言であり、
modules
      
      名前空間を介してアプリケーションで後で利用可能になります。
結果の非同期転送では、クエリの結果に応じて、エラーが発生した場合は空の配列、または検索結果の配列を渡すプロミスを返します。
日付を処理するには、モジュール
moment.js
      
      追加します。
Twitterはメッセージでプレーンテキストを返すため、
twitter-text
      
      ライブラリを使用してハッシュタグとリンクを強調表示します。
さらに、上記のように、
express
      
      が必要になります。
これらのモジュールをインストールしましょう。
 > npm i vow moment twitter-text express --save
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
        server
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
    ブロック 
      サーバーブロックは、アプリケーションのサーバー側の操作を担当します。 フォルダー
./desktop.blocks/server/
      
      を追加し、その中に
server.node.js
      
      ファイルを作成します。
これは、URL
/search
      
      をリッスンし、要求に応じてデータを返す
express
      
      アプリケーションになります。
 modules.require(['twitter'], function(twitter) { var fs = require('fs'), PATH = require('path'), express = require('express'), app = express(), url = require('url'), querystring = require('querystring'), Vow = require('vow'); app.get('/search', function(req, res) { var dataEntries = [], searchObj = url.parse(req.url, true).query, queryString = querystring.escape(searchObj.query), servicesEnabled = []; searchObj.twitter && servicesEnabled.push(twitter.get(queryString)); Vow.all(servicesEnabled) .then(function(results) { res.end(JSON.stringify(results, null, 4)); }) .fail(function() { console.error(arguments); }); }); var server = app.listen(3000, function() { console.log('Listening on port %d', server.address().port); }); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      次の内容で
./desktop.blocks/sssr/sssr.deps.js
      
      ファイルを作成します。
 ({ shouldDeps: [ { block: 'server' }, { block: 'island', mods: { type: ['twitter'] }} ] })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ここでは、
sssr
      
      ブロックが
server
      
      するために
type: 'twitter'
      
      修飾子を持つ
server
      
      ブロックと
island
      
      ブロックが必要であると述べてい
server
      
      。
server
      
      ブロックに応じて
service_type_twitter
      
      修飾子も追加し
server
      
      。 これを行うには、ファイル
./desktop.blocks/server/server.deps.js
      
      作成します。
 ({ shouldDeps: [ { block: 'service', mods: { type: ['twitter'] } }, { block: 'sssr', } ] })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      これで、必要なすべてのブロックがアセンブリに分類されます。 プロジェクトを再構築し、サーバーを起動します。
 > enb make && node ./desktop.bundles/index/index.node.js
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      アドレスhttp:// localhost:3000 / search?Query =%23b_&twitter = onで、JSONデータオブジェクトを含むページが開き、
service_type_twitter
      
      ブロックが提供します。
      次に、BEMTREEを使用して、このデータのBEMJSONへの変換を追加します。
server.node.js
      
      :
 modules.require(['twitter'], function(twitter) { var fs = require('fs'), PATH = require('path'), VM = require('vm'), express = require('express'), app = express(), url = require('url'), querystring = require('querystring'), moment = require('moment'), Vow = require('vow'), pathToBundle = PATH.join('.', 'desktop.bundles', 'index'); app.use(express.static(pathToBundle)); var bemtreeTemplate = fs.readFileSync(PATH.join(pathToBundle, 'index.bemtree.js'), 'utf-8'); var context = VM.createContext({ console: console, Vow: Vow }); VM.runInContext(bemtreeTemplate, context); var BEMTREE = context.BEMTREE; app.get('/search', function(req, res) { var dataEntries = [], searchObj = url.parse(req.url, true).query, queryString = querystring.escape(searchObj.query), servicesEnabled = []; searchObj.twitter && servicesEnabled.push(twitter.get(queryString)); Vow.all(servicesEnabled) .then(function(results) { //      , //     Object.keys(results).map(function(idx) { dataEntries = dataEntries.concat(results[idx]); }); //     dataEntries.sort(function(a, b) { return b.createdAt.valueOf() - a.createdAt.valueOf(); }); //  BEMJSON     BEMTREE  BEMTREE.apply(dataEntries.map(function(dataEntry) { dataEntry.createdAt = moment(dataEntry.createdAt).fromNow(); return { block: 'island', data: dataEntry, mods: { type: dataEntry.type } }; })) .then(function(bemjson) { //   JSON res.end(JSON.stringify(bemjson, null, 4)); }); }) .fail(function() { console.error(arguments); }); }); var server = app.listen(3000, function() { console.log('Listening on port %d', server.address().port); }); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      BEMTREE- ,
vow
      
      , .
, , .
BEMTREE.apply()
      
      , , - , BEMTREE-.
./desktop.blocks/island/_type/island_type_twitter.bemtree
      
      :
 block('island').mod('type', 'twitter').content()(function() { var data = this.ctx.data; return [ //     ]; });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      this.ctx.data
      
      ,
BEMTREE.apply()
      
      .
http://localhost:3000/search?query=%23b_&twitter=on . BEMJSON, BEMTREE.
BEMJSON HTML
BEMHTML.apply()
      
      . server.node.js :
 var BEMHTML = require(PATH.join('../../' + pathToBundle, 'index.bemhtml.js')).BEMHTML; //… BEMTREE.apply(dataEntries.map(function(dataEntry) { dataEntry.createdAt = moment(dataEntry.createdAt).fromNow(); return { block: 'island', data: dataEntry, mods: { type: dataEntry.type } }; })) .then(function(bemjson) { if (searchObj.json) { return res.end(JSON.stringify(bemjson, null, 4)); } res.end(BEMHTML.apply(bemjson)); }); //…
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , HTML, — AJAX.
json=on
      
      — BEMJSON- — http://localhost:3000/search?query=%23b_&twitter=on&json=on .
        JavaScript  i-bem.js
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      JavaScript JavaScript- - - –
i-bem.js
      
      .
bem-core
      
      .
i-bem.js
      
      —
i-bem
      
      js
      
      .
jQuery
      
      API .
, i-bem.js .
:
- ;
 - ;
 - .
 
js-
js-, . , , js-, BEMHTML
js
      
      , BEMJSON —
js
      
      :
 // bemhtml block('form').js()(true);
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
       // bemjson { block: 'form', js: true }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
       // bemjson with js params { block: 'form', js: { p1: 'v1', p2: 'v2' } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      js
      
      , , js- . HTML:
 <div class="form i-bem" data-bem="{form: {p1: 'v1', p2 : 'v2'}}"></div>
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      i-bem
      
      , DOM- js-. -
data-bem
      
      , js-, — , .
   js
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
        form
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ./desktop.blocks/form/form.js
      
      :
 modules.define('form', ['i-bem__dom'], function(provide, BEMDOM) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.bindTo('submit', this._onSubmit); } } }, _onSubmit: function(e) { e.preventDefault(); this.emit('submit'); }, getVal: function() { return this.domElem.serialize(); } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      bem-core
      
      .
i-bem
      
      — .
i-bem__dom
      
      — , DOM .
form
      
      ,
i-bem__dom
      
      , DOM-.
BEMDOM
      
      .
form
      
      . ,
js
      
      inited
      
      —
i-bem.js
      
      . ,
_onSubmit
      
      , ,
getVal
      
      , .
_onSubmit()
      
      e.preventDefault()
      
      , -
submit
      
      , . API
form
      
      . -.
  sssr
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , .
./desktop.blocks/sssr/sssr.js
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery'], function(provide, BEMDOM, $) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.findBlockInside('form').on('submit', this._sendRequest, this); } } }, _sendRequest: function() { $.ajax({ type: 'GET', dataType: 'html', cache: false, url: '/search/', data: this.findBlockInside('form').getVal(), success: this._onSuccess.bind(this) }); }, _onSuccess: function(html) { BEMDOM.update(this.elem('content'), html); } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      .
sssr
      
      i-bem__dom
      
      , DOM-,
jquery
      
      AJAX.
submit
      
      form
      
      .
_sendRequest
      
      , AJAX-. ,
_onSuccess
      
      ,
sssr__content
      
      .
,
i-bem.js
      
      ,
sssr
      
      js-:
 // desktop.blocks/sssr/sssr.bemhtml block('sssr').js()(true);
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , , .
index.node.js
      
      :
 $ enb make && node ./desktop.bundles/index/index.node.js
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      . localhost:3000 , - , . , .
      , .
borschik
      
      .
.borschik
      
      :
 { "freeze_paths": { "libs/**": ":base64:", "libs/**": ":encodeURIComponent:" } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      production
      
      :
 > YENV=production enb make && node desktop.bundles/index/index.node.js
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      .
        .  spin
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      - , . , «».
spin
      
      , . BEMJSON-.
bem-components
      
      API. :
 modules.require(['jquery'], function($) { $('.spin').bem('spin').setMod('visible'); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      
      spin_visible
      
      true
      
      .
,
js
      
      - .
./desktop.blocks/sssr/sssr.styl
      
      :
 .sssr { .spin { margin-left: 1em; vertical-align: middle; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , .
./desktop.blocks/sssr/sssr.js
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery'], function(provide, BEMDOM, $) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.findBlockInside('form').on('submit', this._doRequest, this); } }, loading: function(modName, modVal) { console.log('visible: ', modVal); this.findBlockInside('spin').setMod('visible', modVal); } }, // … _doRequest: function() { this.setMod('loading'); this._sendRequest(); }, _onSuccess: function(html) { this.delMod('loading'); BEMDOM.update(this.elem('content'), html); } })) })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      JS-, CSS- . , , .
./desktop.bundles/sssr/sssr.styl
      
      :
 .sssr { .spin { margin-left: 1em; vertical-align: middle; } &_loading .content { opacity: 0.5; } }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      : localhost:3000 .
spin
      
      , — .
      , , .
isEmpty()
      
      :
./desktop.blocks/form/form.js
      
      :
 isEmpty: function() { return !this.findBlockInside('input').getVal().trim() || this.findBlocksInside('checkbox').every(function(checkbox) { return !checkbox.hasMod('checked'); }); }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      input
      
      checkbox_checked
      
      .
, ,
sssr
      
      :
./desktop.blocks/sssr/sssr.js
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery'], function(provide, BEMDOM, $) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.findBlockInside('form').on('submit', this._doRequest, this); } }, loading: function(modName, modVal) { this.findBlockInside('spin').setMod('visible', modVal); } }, _doRequest: function() { if (this.findBlockInside('form').isEmpty()) { return; } this.setMod('loading'); this._sendRequest(); }, _sendRequest: function() { //… })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      _doRequest()
      
      .
, , .
_sendRequest()
      
      clear()
      
      _updateContent()
      
      .
./desktop.blocks/sssr/sssr.js
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery'], function(provide, BEMDOM, $) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.findBlockInside('form').on('submit', this._doRequest, this); } }, loading: function(modName, modVal) { this.findBlockInside('spin').setMod('visible', modVal); } }, _doRequest: function() { if (this.findBlockInside('form').isEmpty()) { return; } this.setMod('loading'); this._sendRequest(); }, clear: function() { this._xhr && this._xhr.abort(); this._updateContent(''); this.delMod('loading'); }, _sendRequest: function() { this._xhr && this._xhr.abort(); this._xhr = $.ajax({ type: 'GET', dataType: 'html', cache: false, url: '/search/', data: this.findBlockInside('form').getVal(), success: this._onSuccess.bind(this) }); }, _onSuccess: function(result) { this.delMod('loading'); this._updateContent(result); }, _updateContent: function(html) { BEMDOM.update(this.elem('content'), html); } })); })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , ,
.
form
      
      change
      
      input
      
      :
 modules.define('form', ['i-bem__dom'], function(provide, BEMDOM) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.bindTo('submit', this._onSubmit); this.findBlockInside('input').on('change', this._onChange, this); } } }, _onChange: function() { this.emit('change'); }, // … })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      change
      
      sssr
      
      ,
./desktop.blocks/sssr.js
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery'], function(provide, BEMDOM, $) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.findBlockInside('form').on('submit change', this._doRequest, this); } }, // … })); })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      ,
./desktop.blocks/form.js
      
      :
 modules.define('form', ['i-bem__dom'], function(provide, BEMDOM) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.bindTo('submit', this._onSubmit); this.findBlockInside('input').on('change', this._onChange, this); BEMDOM.blocks.checkbox.on(this.domElem, 'change', this._onChange, this); } } }, // … })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      :
      . .
debounce
      
      bem-core
      
      .
sssr
      
      sssr.deps.js
      
      :
 ({ shouldDeps: [ { block: 'server' }, { block: 'island', mods: { type: ['twitter'] }}, { block: 'functions', elem: 'debounce' } ] })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      - . ,
functions__debounce
      
      debounce
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery', 'functions__debounce'], function(provide, BEMDOM, $, debounce) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this.findBlockInside('form').on('submit change', this._doRequest, this); this._debounceRequest = debounce(this._sendRequest, 500, this); } }, loading: function(modName, modVal) { this.findBlockInside('spin').setMod('visible', modVal); } }, _doRequest: function(e) { this.setMod('loading'); if (this.findBlockInside('form').isEmpty()) { this._clear(); return; } e.type === 'change' ? this._debounceRequest(): this._sendRequest(); }, _clear: function() { this._xhr && this._xhr.abort(); this._updateContent(''); this.delMod('loading'); }, _sendRequest: function() { this._xhr && this._xhr.abort(); this._xhr = $.ajax({ type: 'GET', dataType: 'html', cache: false, url: '/search/', data: this.findBlockInside('form').getVal(), success: this._onSuccess.bind(this) }); }, _onSuccess: function(result) { this.delMod('loading'); this._updateContent(result); }, _updateContent: function(html) { BEMDOM.update(this.elem('content'), html); } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , . — .
.
sssr
      
      params
      
      .
index.bemjson.js
      
      :
 { block: 'sssr', mods: { autorefresh: true }, js: { url: '/search/', refreshInterval: 10000 }, // ... }
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      :
./desktop.blocks/sssr/_autorefresh/sssr_autorefresh.js
      
      :
 modules.define('sssr', ['tick'], function(provide, tick, Sssr) { provide(Sssr.decl({ modName: 'autorefresh' }, { onSetMod: { loading: function(modName, modVal) { //    this.__base.apply(this, arguments); //    — , //    —   modVal ? this._clearTimer(): this._setTimer(); } }, _setTimer: function() { this._counter = 0; tick.on('tick', this._onTick, this); }, _onTick: function() { //       (++this._counter * 50) % this.params.refreshInterval || this._sendRequest(); }, _clearTimer: function() { tick.un('tick', this._onTick, this); }, getDefaultParams: function() { return { refreshInterval: 10000 }; } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      this.__base
      
      sssr_loading
      
      .
tick
      
      .
tick
      
      50 .
sssr_loading
      
      , , .
refreshInterval
      
      sssr
      
      , .
getDefaultParams
      
      . , .
sssr
      
      .
desktop.blocks/sssr/sssr.deps.js
      
      :
 ({ shouldDeps: [ 'server', { block: 'functions', elem: 'debounce' }, { block: 'island', mods: { type: ['twitter'] } } ] })
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      . 10 .
, ,
.
this.findBlockInside('form')
      
      this._form
      
      .
spin
      
      .
sssr
      
      , .
 modules.define('sssr', ['i-bem__dom', 'jquery', 'functions__debounce'], function(provide, BEMDOM, $, debounce) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this._spin = this.findBlockInside('spin'); this._form = this.findBlockInside('form') .on('submit', this._doRequest, this); this._debounceRequest = debounce(this._sendRequest, 500, this); } }, loading: function(modName, modVal) { this._spin.setMod('visible', modVal); } }, _doRequest: function(e) { this.setMod('loading'); if (this._form.isEmpty()) { this._clear(); return; } e.type === 'change' ? this._debounceRequest(): this._sendRequest(); }, _clear: function() { this._abortRequest(); this._updateContent(''); this.delMod('loading'); }, _abortRequest: function() { this._xhr && this._xhr.abort(); }, _sendRequest: function() { this._abortRequest(); this._xhr = $.ajax({ type: 'GET', dataType: 'html', cache: false, url: this.params.url, data: this._form.getVal(), success: this._onSuccess.bind(this) }); }, _onSuccess: function(result) { this.delMod('loading'); this._updateContent(result); }, _updateContent: function(result) { BEMDOM.update(this.elem('content'), result); } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      , , ,
_abortRequest()
      
      .
遅延初期化
, , .
.
      
      live
      
      -. i-bem.js .
sssr
      
      form
      
      , . :
./desktop.blocks/sssr/sssr.js
      
      :
 modules.define('sssr', ['i-bem__dom', 'jquery', 'functions__debounce'], function(provide, BEMDOM, $, debounce) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this._form = this.findBlockInside('form'); this._spin = this.findBlockInside('spin'); this._debounceRequest = debounce(this._sendRequest, 500, this); } }, loading: function(modName, modVal) { this._spin.setMod('visible', modVal); } }, _clear: function() { this._abortRequest(); this._updateContent(''); this.delMod('loading'); }, _doRequest: function(needDebounce) { if (this._form.isEmpty()) { this._clear(); return; } this.setMod('loading'); needDebounce? this._debounceRequest() : this._sendRequest(); }, _sendRequest: function() { this._abortRequest(); this._xhr = $.ajax({ type: 'GET', dataType: 'html', url: this.params.url, data: this._form.getVal(), cache: false, success: this._onSuccess.bind(this) }); }, _abortRequest: function() { this._xhr && this._xhr.abort(); }, _onSuccess: function(result) { this._updateContent(result); this.delMod('loading'); }, _updateContent: function(html) { BEMDOM.update(this.elem('content'), html); } }, { live: function() { this.liveInitOnBlockInsideEvent('submit change', 'form', function(e) { this._doRequest(e.type === 'change'); }); } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      live
      
      -
form
      
      :
./desktop.blocks/form/form.js
      
      :
 modules.define('form', ['i-bem__dom'], function(provide, BEMDOM) { provide(BEMDOM.decl(this.name, { onSetMod: { js: { inited: function() { this._input = this.findBlockInside('input'); this._checkboxes = this.findBlocksInside('checkbox'); } } }, // … isEmpty: function() { return !this._input.getVal().trim() || this._checkboxes.every(function(checkbox) { return !checkbox.hasMod('checked'); }); } }, { live: function() { var ptp = this.prototype; this .liveBindTo('submit', ptp._onSubmit) .liveInitOnBlockInsideEvent('change', 'input', ptp._onChange) .liveInitOnBlockInsideEvent('change', 'checkbox', ptp._onChange); } })); });
      
      
        
        
        
      
    
        
        
        
      
      
        
        
        
      
    
     
      input
      
      checkbox
      
      , ,
findBlockInside
      
      .
, -.
i-bem.js
      
      , BEMTREE - BEMHTML - HTML. sssr
service__type__*
      
      API Instagram .. , . , .
, . info@bem.info .