Derby 0.6の孊習の䟋2

トドス

この投皿は、 ここから始たったシリヌズの続きです 前の郚分を読む必芁がありたす。 今日は、いわゆる「to-doリスト」 TodoMVCプロゞェクトのTodoリストを䜜成したす。 Angularで䜜成されたバヌゞョンに基づいお、 ダヌビヌで機胜を再䜜成しおください。

䜜業オプションを調べる


それでは、Angularバヌゞョンの内容ずその仕組みを芋おみたしょう機胜を理解するために5分かかりたす。


基瀎ずなるもの


より良いderbyjsを孊習するための目暙に基づいお、ここでスタむルを考え出すこずはしたせん。スタむルはすでに蚘述されおおり、ほずんどのTodoMVC実装で倉曎なしで䜿甚されたす。 cssファむルを取埗したす 。 それをざっず芋おみるず、背景bg.pngの写真も撮る必芁があるこずがわかりたす。 たた、angularによっお生成されたhtmlをフレヌムワヌクずしお䜿甚したすブラりザヌの開発者ツヌルを䜿甚しおコピヌし、Angularディレクティブから少し削陀したした。
基本的なhtmlコヌド
  <section id="todoapp"> <header id="header"> <h1>todos</h1> <form id="todo-form"> <input id="new-todo" placeholder="What needs to be done?" autofocus> </form> </header> <section id="main"> <input id="toggle-all" type="checkbox"> <label for="toggle-all">Mark all as complete</label> <ul id="todo-list"> <li> <div class="view"> <input class="toggle" type="checkbox"> <label>hello</label> <button class="destroy"> </button> </div> <form > <input class="edit"> </form> </li> </ul> </section> <footer id="footer"> <span id="todo-count"><strong>0</strong> <span>items left</span> </span> <ul id="filters"> <li><a href="/" class="selected">All</a></li> <li><a href="/active">Active</a></li> <li><a href="/completed">Completed</a></li> </ul> <button id="clear-completed">Clear completed (0)</button> </footer> </section> 


ご芧のずおり、このhtmlは3぀のメむンブロックで構成されおいたす。

  1. ヘッダヌ-ここがメむン入力です。 新しいタスクを導入するために必芁です。
  2. main-メむンブロック。タスクリスト自䜓はここに栌玍されたす。
  3. フッタヌ-ステヌタス行、ここの情報、フィルタヌず「クリア完了」ボタンの切り替え


プロゞェクト構造


それでは、理由を考えたしょう。 私たちのプロゞェクトには䜕がありたすか スタむルシヌトがあり、静的デヌタ背景画像が返され、htmlテンプレヌトがあり、少なくずも2぀のファむルがありたす-アプリケヌションダヌビヌの2぀の郚分サヌバヌ郚分ずダヌビヌアプリケヌション自䜓。 このすべおに基づいお、私はそのようなアプリケヌションファむル構造を投げたしたあなたは他のものをするこずができたす

 public/ bg.png app # - views/ index.html css/ index.css index.js #  - server.js #    package.json 

cssファむルは、パブリックフォルダヌ内ではなく、アプリフォルダヌ内にあるこずに泚意しおください。 これは、ダヌビヌが特別な方法でスタむルを操䜜するためです。 その結果、Googleの調査derbyの䜜成者によるで瀺されおいるように、スタむルタグのペヌゞの先頭に盎接挿入されたす。これは、スタむルを高速に配眮するための最良の方法です。

したがっお、前回のレッスンで述べたように、アプリフォルダヌ内のすべおは同圢のダヌビヌアプリケヌションです。 「同圢」ずいう蚀葉は奜きではないので省略したす。「ダヌビヌのサヌバヌ郚分」ではなく、ダヌビヌアプリケヌションずだけ蚀いたす。 ここでのポむントは、これらすべおのファむルアプリ内のすべおを1぀にたずめお、クラむアントブラりザヌに1぀のバンドルピヌスを提䟛するため、それらをたずめたものです。

䞀般的に将来、プロゞェクトを耇数のダヌビヌアプリケヌションクラむアントパヌトや管理パネルなどに分割できたす。 これは、䞍芁なデヌタテンプレヌト、スタむル、コヌドを提䟛しないこずず、接続性を枛らすこずの2぀の理由で正圓化されたす。 ぀たり、次のようになりたす。プロゞェクトには、1぀のサヌバヌパヌツず耇数のダヌビヌアプリケヌションこの堎合は2぀がありたす。

package.jsonファむルでは、䟝存関係は同じ2぀のモゞュヌルderby@0.6.0-alpha5ずderby-starterになりたす。

はじめに


ファむル構造を䜜成したす。 最初に瀺したリンクから背景画像ずスタむルをダりンロヌドし、 npm initを䜿甚しおpackage.jsonを䜜成しnpm init 前のレッスンで確認できたす。

前の䟋のように、Htmlは少し調敎されおいたす。たず、定矩枈みのBody:テンプレヌトにある必芁がありたすBody:次に、ヘッダヌ、メむン、フッタヌを別々のダヌビヌテンプレヌトに配眮したす。

最終的なindex.html
 <Body:> <section id="todoapp"> <view name="header"/> <view name="main"/> <view name="footer"/> </section> <header:> <header id="header"> <h1>todos</h1> <form id="todo-form"> <input id="new-todo" placeholder="What needs to be done?" autofocus> </form> </header> <main:> <section id="main"> <input id="toggle-all" type="checkbox"> <label for="toggle-all">Mark all as complete</label> <ul id="todo-list"> <li> <div class="view"> <input class="toggle" type="checkbox"> <label>hello</label> <button class="destroy"> </button> </div> <form > <input class="edit"> </form> </li> </ul> </section> <footer:> <footer id="footer"> <span id="todo-count"><strong>0</strong> <span>items left</span> </span> <ul id="filters"> <li><a href="/" class="selected">All</a></li> <li><a href="/active">Active</a></li> <li><a href="/completed">Completed</a></li> </ul> <button id="clear-completed">Clear completed (0)</button> </footer> 

お気づきかもしれたせんが、独自のテンプレヌトの呌び出しは、 viewタグを䜿甚しお行われview 。viewタグでは、テンプレヌトの名前によっおテンプレヌトの名前が蚭定されたす。

たず、ブラりザで結果を確認しお機胜を匷化できるように、最小限の䜜業コヌドを䜜成したす。

前の䟋のserver.jsファむルは、プロゞェクトの構造を考慮しお静的ファむルをレンダリングするためにわずかに拡匵されおいたす。
server.js
 var server = require('derby-starter'); var appPath = __dirname + '/app'; var options = { static: __dirname + '/public' }; server.run(appPath, options); 

プロゞェクトの教育的性質により、derby-starterモゞュヌルをサヌバヌ郚分ずしお䜿甚しおいるこずを思い出させおください。 内偎を芋るず、静的ファむルの戻りは、express-static static-middlwareの叀兞的な䜿甚法です。 自分で芋おください 。

最小index.js
 var derby = require('derby'); var app = module.exports = derby.createApp('todos', __filename); //  app ,         // (    ) global.app = app; app.loadViews (__dirname+'/views'); app.loadStyles(__dirname+'/css'); app.get('/', getTodos); function getTodos(page, model){ page.render(); } 

すべお、npm startたたは盎接ノヌドserver.jsを起動し、ブラりザにhttp// localhost3000 /結果が衚瀺されたす



レむアりトのスタむルが远い぀いた。 開始されたした。

デザむンURL


前回のレッスンでは、ダヌビヌ開発者はプロゞェクトをURLに分割しお開発を開始する必芁があるず述べたした。 これは、怜玢゚ンゞンが奜むクラむアントずサヌバヌの䞡方でペヌゞを生成するダヌビヌの胜力によるものです。 そのため、Angularバヌゞョンの孊習䞭に、フッタヌにURLを倉曎する3぀のリンクがあり、それに応じおタスクごずのフィルタヌがあるこずに気付きたした。 ここでは、アプリケヌションに3぀のget芁求ハンドラヌが必芁であるこずを理解しおいたす。 次のようなもの

 app.get('/', getAllTodos); app.get('/active', getActiveTodos); app.get('/completed', getCompletedTodos); 

これらのペヌゞがすべお異なる堎合、これは正圓化されたすが、私たちにずっお、それらの間の唯䞀の違いはフィルタヌであるため、コヌドを最小限に耇補しようずしたす。

蚭蚈デヌタ


タスク自䜓は、 todosコレクションに保存されたす。 各タスクは2぀のフィヌルドで衚されたす。
  1. テキスト-タスクの説明
  2. completed-タスクが完了したこずの兆候

これに、もちろん各タスクにもidフィヌルドがあるこずを远加する必芁がありたす-ダヌビヌはコレクションにアむテムを远加するずきに自動的に远加したす。

したがっお、ダヌビヌの方法論に埓っお、コントロヌラヌurlぞの芁求を凊理する関数で、renderを呌び出す前に、デヌタを準備し、デヌタを曎新するためのサブスクリプションを登録する必芁がありたす。 ハンドラヌは、抂略的に次のようになりたす。

 function getTodos(page, model){ model.subscribe('todos', function(){ page.render(); }); } 

これはほが同じですが、先に進む前に3぀のク゚リすべおに察しお1぀のコントロヌラヌを䜜成し、タスクに察しお異なるフィルタヌのみを䜿甚するために、ダヌビヌモデルに぀いおいく぀かのこずを孊ぶ必芁がありたす。


前回のレッスンでは、いわゆる「パス」に぀いお話したした。 モデルの操䜜でそれらを䜿甚したす。 たずえば、デヌタをサブスクラむブする堎合model.subscribe 'path'、モデルのデヌタを受信および曞き蟌む堎合 model.get('') 、 model.set('', ) 。 パスの䟋


だからここに。 ご存じのずおり、パスの最初のセグメントはコレクションの名前です。 このダヌビヌ名は、ラテン文字、たたは文字$たたは_で始めるこずができたす。 $および_で始たるすべおのコレクションは特別であり、サヌバヌず同期しおいたせんこれらはモデルに察しおロヌカルであり、1぀のモデルのみがダヌビヌアプリケヌションで䜜成されたす。 $で始たるコレクションは、独自のニヌズのためにダヌビヌを予玄したした。 開発者は、アンダヌスコアで始たるコレクションを䜿甚したす。

少し実隓しおみたしょう。 ブラりザヌで開発者コン゜ヌルを開き、「 app.model.get()ず入力しお、出力を衚瀺したす。

「_」コレクションには特別なものが1぀あり_pageは、URLが_pageれるたびに_pageれ_pageこれにより、あらゆる皮類の䜜業デヌタを保存するのに非垞に䟿利です。 このレッスンでは、より倚くの䟋を芋るこずができたす。

フィルタヌに移りたしょう。 モデルのドキュメントを読むず、ダヌビヌにはリアクティブデヌタの凊理を容易にするさたざたなメカニズムがあるこずがわかりたす。 これは、たずえば、リアクティブ関数、デヌタで発生するさたざたなむベントのサブスクリプション、デヌタフィルタヌ、デヌタ゜ヌタヌなどです。

フィルタヌに぀いお説明したしょう。 たずえば、アクティブなタスクのみを衚瀺するフィルタヌを実装するにはどうすればよいですか

特定の名前でフィルタヌ関数を登録したす名前はバンドルでのシリアル化に必芁です。 ドキュメントには、それらを厳密にapp.on('model')に登録する必芁があるず曞かれおいたす

 app.on('model', function(model) { model.fn('completed', function(item) { return item.completed; }); }); 

さらにコントロヌラヌで、このフィルタヌを䜿甚しおtodosコレクションをフィルタヌ凊理したす。

 function getPage(page, model){ model.subscribe('todos', function() { var filter = model.filter('todos', 'completed') filter.ref('_page.todos'); page.render(); }); } 

ここで、行filter.ref('_page.todos');は非垞に重芁ですfilter.ref('_page.todos'); 、その䞭で、フィルタリングされた「todos」が_page.todosパスで利甚可胜になりたす。 すべおをたずめるず、コントロヌラヌを䜿甚したフィルタヌコヌドをここに提案したす。

 app.on('model', function(model) { model.fn('all', function(item) { return true; }); model.fn('completed', function(item) { return item.completed;}); model.fn('active', function(item) { return !item.completed;}); }); app.get('/', getPage('all')); app.get('/active', getPage('active')); app.get('/completed', getPage('completed')); function getPage(filter){ return function(page, model){ model.subscribe('todos', function() { model.filter('todos', filter).ref('_page.todos'); page.render(); }); } } 

お気づきかもしれたせんが、すべおを統䞀するために、停のフィルタヌを「すべお」䜜成する必芁がありたしたが、これはテむクの欠劂にずっお倧きな料金ではないず思いたす。

さお、少し気が散りたした。 アプリケヌションを埩掻させたしょう。

タスクの远加ずリスト


レむアりトのデヌタ入力の入力は次のようになりたす。

  <form id="todo-form"> <input id="new-todo" placeholder="What needs to be done?" autofocus> </form> 

ダヌビヌの叀兞的なパタヌン倚くの最新のフレヌムワヌクず同様は、リアクティブバむンディングです。 入力で入力した倀を_pageパスに_pageたす。 enterクリックを凊理するために、フォヌムのsubmitむベントハンドラヌも登録しenter 。

  <form id="todo-form" on-submit="addTodo(_page.newTodo)"> <input id="new-todo" placeholder="What needs to be done?" autofocus value="{{_page.newTodo}}"> </form> 

on-submitはなく、自然にon-click 、 on-keyup 、 on-focus蚘述on-clickこずができon-click -぀たり、これはダヌビヌでむベントを凊理する暙準的な方法です。 app.protoハンドラヌを配眮しapp.proto ダヌビヌコンポヌネントに぀いお説明するずきに、各コンポヌネントがそのハンドラヌをそれ自䜓に栌玍するこずがわかりたすが、今のずころこれを行いたす。

 app.proto.addTodo = function(newTodo){ if (!newTodo) return; this.model.add('todos', { text: newTodo, completed: false }); this.model.set('_page.newTodo', ''); }; 

テキストが空かどうかを確認し、タスクをコレクションに远加しお、 inputクリアしinput 。 ハンドラヌにパラメヌタヌが1぀しかないこずに気づいたかもしれたせん。䜕らかの理由でむベントオブゞェクトたたはhtml芁玠自䜓ぞの参照が必芁な堎合は、次のようにhtmlに明瀺的に蚘述する必芁がありたす。 on-submit="addTodo(_page.newTodo, $event, $element)" 、 $eventおよび$elementはダヌビヌ自䜓によっお埋められる特別なパラメヌタヌです。

フィルタリングされたタスクリストの出力ul芁玠を線集したす。

  <ul id="todo-list"> {{each _page.todos as #todo, #index}} <li class="{{if #todo.completed}}completed{{/}}"> <div class="view"> <input class="toggle" type="checkbox" checked="{{#todo.completed}}"> <label>{{#todo.text}}</label> <button class="destroy"> </button> </div> <form> <input class="edit"> </form> </li> {{/each}} </ul> 

だから䜕をした

アむテムを削陀する


それは基本的に行われたす

 <button class="destroy" on-click="delTodo(#todo.id)"> </button> 

 app.proto.delTodo = function(todoId){ this.model.del('todos.' + todoId); }; 

そしお、さらに短くするこずもできたした。

 <button class="destroy" on-click="model.del('todos.' + #todo.id)"> </button> 


すべおの「完了」タスクの削陀も同様です「完了完了」ボタンは右䞋にありたす。

  <button id="clear-completed" on-click="clearCompleted()"> Clear completed (0) </button> 

 app.proto.clearCompleted = function(){ var todos = this.model.get('todos'); for (var id in todos) { if (todos[id].completed) this.model.del('todos.'+id); } } 

芁玠を線集する


ダブルクリックするず、タスクは線集モヌドになりたす。 レむアりトから刀断するず、このモヌドに切り替えるずきは、察応するli芁玠にeditingクラスを远加する必芁がありたす。 たた、途䞭で、ダブルクリックしお発生する遞択を取り陀き、必芁な入力に正しくフォヌカスを合わせる必芁がありたす。

次のようにそれを行うこずを提案したすパス-_page.editを䜿甚しお、線集したタスクに関する情報を保存したす。 そこで、線集したタスクのIDずテキストを保存したす。

テキストを個別に保存するのはなぜですか、すでにタスク自䜓に保存されおいたすか
それはすべお目暙に䟝存したす。 テキストをタスクから盎接入力に接続した堎合、ナヌザヌはデヌタベヌス内の芁玠を盎接線集したす。 ぀たり、その線集ボタンを抌すたびには、ブラりザヌで他のナヌザヌに即座に衚瀺されたす。 さらに、耇数のナヌザヌが同時にテキストを線集し、すべおの倉曎を衚瀺できたすが、これは必芁なものではありたせん。 通垞のシナリオは、最終的に線集されたデヌタのデヌタベヌスにコミットするか、コミットを拒吊するこずです。぀たり、ナヌザヌがenterキヌを抌した堎合にのみ、すべおのナヌザヌに察しおすべおを曎新する必芁がありenter 。

そのため、次のすべおを実装したす。

  <ul id="todo-list"> {{each _page.todos as #todo}} <li class="{{if #todo.completed}}completed{{/}} {{if _page.edit.id === #todo.id}}editing{{/}}"> <div class="view"> <input class="toggle" type="checkbox" checked="{{#todo.completed}}"> <label on-dblclick="editTodo(#todo)">{{#todo.text}}</label> <button class="destroy" on-click="delTodo(#todo.id)"> </button> </div> <form on-submit="doneEditing(_page.edit)"> <input id="{{#todo.id}}" class="edit" value="{{_page.edit.text}}" on-keyup="cancelEditing($event)"> </form> </li> {{/each}} </ul> 

 app.proto.editTodo = function(todo){ this.model.set('_page.edit', { id: todo.id, text: todo.text }); window.getSelection().removeAllRanges(); document.getElementById(todo.id).focus() } app.proto.doneEditing = function(todo){ this.model.set('todos.'+todo.id+'.text', todo.text); this.model.set('_page.edit', { id: undefined, text: '' }); } app.proto.cancelEditing = function(e){ // 27 = ESQ-key if (e.keyCode == 27) { this.model.set('_page.edit.id', undefined); } } 

ダブルクリックするず、 editTodo関数がトリガヌされ 、その䞭に_path.editを入力し、䞍芁な遞択を削陀し、必芁な入力にフォヌカスを切り替えたすここでは、入力id = todo.idを指定しお少し浮気したした。

線集が終了したら、enterたたはesqを抌したす。 したがっお、 doneEditing 、 cancelEditingの2぀のハンドラヌのいずれかが起動したす。 コヌドを孊ぶ-新しいこずは䜕もない。

アクティブおよび完了したタスクの数-リアクティブ機胜


したがっお、最埌に行うこずは、アクティブなタスクず完了したタスクの数をフッタヌに出力するこずです。 これは、リアクティブ関数ずは䜕かを説明する正圓な理由です。

プロゞェクトのアヌキテクチャに関する小さな発蚀
私が遞択したアプリケヌション実装オプションが唯䞀のものではないこずに泚意しおください。 これを考えるず、特定のプロゞェクトがすぐにlive-queryを䜿甚しお頭に浮かびたす-これは、デヌタベヌスに察しおmongo-requestを行うこずができるもう1぀の玠晎らしいダヌビヌメカニズムであり、その結果は事埌的に曎新されたす。 もちろん、ク゚リでは、さたざたな遞択、゜ヌト、数量制限 $limit 、 $skip 、 $orderby を䜿甚できたす。 コレクション内の芁玠の数を任意の遞択を含めお返すク゚リを䜜成するこずもできたす-これはたさに私たちの堎合です。 次の投皿のいずれかで「ラむブ」ク゚リを調査したすが、実際のアプリケヌションでもよく䜿甚されるリアクティブ関数による実装を瀺すこずが適切であるず考えたした。

したがっお、リアクティブ関数は、デヌタが倉曎されるたびにトリガヌされる関数です。 ぀たり、この特定のリアクティブ関数がこれらの特定のデヌタの倉化を監芖するこずを瀺す必芁がありたす。 このデヌタは、パラメヌタヌずしおこの関数に入力されたす。 次に、圌女は䜕かを蚈算し、結果を返したす。 その結果は、特定の「パス」に関連付けられおいたす...

さお、これはすべお抜象的であり、したがっお読みにくいです。 䟋を䜿甚したしょう。 アクティブなタスクず完了したタスクを含むTodoのコレクションがありたす。 コレクションの倉曎時に、どこかたずえば、 _page.countersパスに沿っおでアクティブなタスクず完了したタスクのカりンタヌにアクセスできるず_page.countersです。 次のようなもの

 _page.counters = { active: 2, completed: 3 } 

次に、このデヌタをフッタヌに簡単に衚瀺できたす。

これらのカりンタヌを取埗する1぀のオプションは、リアクティブ関数を䜿甚するこずです。 それらはフィルタヌず同じ方法で登録されたす

 app.on('model', function(model) { model.fn('all', function(item) { return true; }); model.fn('completed', function(item) { return item.completed;}); model.fn('active', function(item) { return !item.completed;}); model.fn('counters', function(todos){ var counters = { active: 0, completed: 0 }; for (var id in todos) { if(todos[id].completed) counters.completed++; else counters.active++; } return counters; }) }); 

これがcounters機胜の登録方法ですが、それだけではありたせん。 適切なタむミングで起動し、パスに結び付ける必芁がありたす。 これは、 model.start関数を䜿甚しおコントロヌラヌで実行されたす。
  model.subscribe('todos', function () { model.filter('todos', filter).ref('_page.todos'); model.start('_page.counters', 'todos', 'counters'); page.render(); }); 


これで、テンプレヌトでカりンタヌを䜿甚できるようになりたした。 フッタヌを完成させおいたす

 <footer:> <footer id="footer"> <span id="todo-count"><strong>{{_page.counters.active}} </strong> <span>items left</span> </span> <ul id="filters"> <li><a href="/" class="{{if $render.url==='/' }}selected{{/}}">All</a></li> <li><a href="/active" class="{{if $render.url==='/active' }}selected{{/}}">Active</a></li> <li><a href="/completed" class="{{if $render.url==='/completed'}}selected{{/}}">Completed</a></li> </ul> <button id="clear-completed" on-click="clearCompleted()" class="{{if _page.counters.completed==0}}hidden{{/}}"> Clear completed ({{_page.counters.completed}}) </button> </footer> 

完了したタスクがない堎合は、必芁なカりンタヌを衚瀺するず同時に、「クリア完了」ボタンを非衚瀺にしたした。 たた、ブラりザヌコン゜ヌルでapp.model.get()調査䞭に取埗した情報を䜿甚しお、 selectedクラスをアクティブなリンクに远加したした。 はい、予玄枈みの$renderコレクションには、さたざたな有甚な情報、特にレンダリングにurlれるurlおいたす。 もう䞀床コン゜ヌルを芋おください。

たずめ


䜕が起こったのかを詊し、いく぀かのタブを開いお、すべおが同期されおいるこずを確認したす。



コヌドを比范したい堎合のgithubプロゞェクト。

PS
次のderbyjsの蚘事を芋逃したくない堎合は、プロファむルの曎新を賌読しおください zag2art 。 私は自分でやっおいたす-ハブでは、特定の非垞に興味深いハブをトラッカヌに远加する方法がないため、芋逃すこずはありたせん。

derbyjs — github

Source: https://habr.com/ru/post/J221703/


All Articles