AngularJS Workshop-管理パネルの開発(パート2)

最初の部分では、AngularJS javascriptフレームワークの管理パネルの基本機能が実装されました-バックエンドからのデータの読み込み、エントリの追加/変更。 2番目の部分では、テーブルの並べ替えとページング、レコードの削除の実装を検討します。





仕分け



controllers.jsファイルのListCtrlコントローラーのテーブルヘッド配列を補完して、デフォルトのソート順を設定します。 0より大きい数値-昇順でソート、0より小さい数値-降順。 数値モジュールには、列ごとのソート順が表示されます。

... $scope.tablehead = [ {name:'title', title:"", sort:-2}, {name:'category', title:"", list:$scope.categories, sort:1}, {name:'answerer', title:" ", list:$scope.answerers}, {name:'author', title:""}, {name:'created', title:""}, {name:'answered', title:""}, {name:'shown', title:""} ]; ...
      
      





ヘッダーをクリックするとソートが機能するはずです。つまり、機能が追加されます。 AngularJSでは、これは非常に簡単です。 list.htmlテンプレートのテーブルタイトルを置き換えます。

 ... <thead> <tr ng-mousedown="$event.preventDefault()" onselectstart="return false"> <th ng-repeat="head in tablehead" ng-click="sortReorder(head.name,$event)" ng-class="{'sort-asc':head.sort>0,'sort-desc':head.sort<0}">{{head.title}}</th> </tr> </thead> <tbody> <tr ng-repeat="item in items | filterEx:tablehead:filter | orderByEx:tablehead:sortBy()"> ...
      
      





そして、コントローラーにソート機能を追加します。

 ... $scope.sortBy = function() { var order = []; angular.forEach($scope.tablehead, function(h){ if (h.sort>0) order[h.sort-1] = h.name; if (h.sort<0) order[Math.abs(h.sort)-1] = '-'+h.name; }); return order; }; $scope.sortReorder = function(col,e) { if (e.shiftKey) { var sortIndex = 0; angular.forEach($scope.tablehead, function(el) { if (Math.abs(el.sort)>sortIndex) sortIndex = Math.abs(el.sort); }); angular.forEach($scope.tablehead, function(el) { if (el.name==col) el.sort = el.sort?-el.sort:sortIndex+1; }); } else { angular.forEach($scope.tablehead, function(el) { if (el.name==col) el.sort = el.sort>0?-1:1; else el.sort = null; }); } }; ...
      
      





sortBy()関数は、orderByExフィルターで並べ替えるときに直接呼び出され、目的の順序で列名を返します。マイナス記号は逆の並べ替えを示します。 sortReorder()関数はソートを並べ替えます。Shiftキーを押したまま、新しい列を追加できます。選択した列を繰り返しクリックすると、この列のソート順が変更されます。

このタスクで、組み込みのorderByフィルターが並べ替える前に列のソースデータを取得し、Category列とResponse列を誤って並べ替えるという事実に再び遭遇しました。 AngularJSコードからorderByフィルターコードを抜き取り、変更を加えました。 変更は重要ではありません(必要なデータでコンパレーターを呼び出す4行が追加されています)。ここでは説明しません( フィルターコードはGitHubで確認できます)。



ページネーション



重要な機能は、大きなテーブルをページに分割することです。 これを行うために、ListCtrlコントローラー(ファイル/js/controllers.js)を補足します。

 ... $scope.paginator = { count: 5, // -    page: 1, pages: 1, setPages: function(itemsCount){ this.pages = Math.ceil(itemsCount/this.count); } }; $scope.items = Items.query(function(data){ $scope.paginator.setPages($scope.items.length); //    var i = 0; angular.forEach(data, function(v,k) { data[k]._id = i++; }); }); $scope.$watch('items',function() { $scope.paginator.setPages($scope.items.length); }); $scope.$watch('paginator.page',function() { if ($scope.paginator.page<1) $scope.paginator.page = 1; if ($scope.paginator.page>$scope.paginator.pages) $scope.paginator.page = $scope.paginator.pages; angular.forEach($scope.items, function(v,k) { $scope.items[k].selected = false; }); }); ...
      
      





$ watch()関数はこのコードで興味深いものです-指定された式が変更されると呼び出されます( $ watch関数についての詳細 )。 関数内でthis変数を試してみるのは面白かったです...

paginator.setPages()メソッドは、アイテムをロードするコールバックと関数$ watch( 'items')で2回呼び出されることに注意してください。 実際には、$ scope.items = Items.query()は$ -watchのトリガーが割り当てられたpromise-objectを返しますが、promise-objectに内部的な変更があるため、それにデータをロードするともう存在しません。

list.htmlテンプレートに数行を追加します。 テーブルシリーズイテレータを補足します。

 ... <tr ng-repeat="item in items | filterEx:tablehead:filter | orderByEx:tablehead:sortBy() | showPage:paginator"> ...
      
      





表の後にページコントロールボタンを追加します。

 ... <div id="table-tools"> <div class="pull-left">  {{Math.min(paginator.count,items.length)}}   {{items.length}} </div> <div class="controls input-append pull-right"> <input type="button" ng-click="paginator.page=1" class="btn btn-small" value="<<"> <input type="button" ng-click="paginator.page=paginator.page-1 || 1" class="btn btn-small" value="<"> <input ng-model="paginator.page" class="paginator-page"><span class="add-on"> {{paginator.pages}} .</span> <input type="button" ng-click="paginator.page=Math.min(paginator.page+1,paginator.pages)" class="btn btn-small" value=">"> <input type="button" ng-click="paginator.page=paginator.pages" class="btn btn-small" value=">>"> </div> <div class="clear"></div> </div> ...
      
      





ボタンハンドラーには、ページ範囲を制限し、$ watch()ハンドラー関数の呼び出し回数を減らす条件式が含まれています。

さて、filters.jsファイルのshowPageフィルターコード:

 ... .filter('showPage', function() { return function(list, paginator) { if (paginator.page<1) paginator.page = 1; if (paginator.count<1) paginator.count = 1; if (paginator.pages && paginator.page>paginator.pages) paginator.page = paginator.pages; return list.slice(paginator.count*(paginator.page-1), paginator.count*paginator.page); }; });
      
      





コードは非常に明白です。説明する意味がわかりません。



行の強調表示と削除



少し残った。 行を選択および削除するコードを記述します。 list.htmlテンプレート(最後)で、クリックハンドラーを追加し、クラスを追加してエントリを視覚的に強調表示することにより、行反復子を変更します。

 ... <tr ng-repeat="item in items | filterEx:tablehead:filter | orderByEx:tablehead:sortBy() | showPage:paginator" ng-click="selectItem($event)" ng-class="item.selected && 'selected'"> ...
      
      





テーブルの前にいくつかのボタンを追加します(div.toolsセクション):

 ... <button ng-click="deleteItem(1)" class="btn btn-danger" ng-show="selected.length==1">  </button> <button ng-click="deleteItem()" class="btn btn-danger" ng-show="selected.length>1">   ({{selected.length}})</button> ...
      
      





1つ以上のエントリが選択されている場合、これらのボタンが表示されます(ng-show == true)。



そして、ListCtrlコントローラーにいくつかの関数を追加します。

 ... $scope.selected = []; $scope.deleteItem = function(one) { if (one) { var _id = $scope.selected[0]; Items['delete']({id:$scope.items[_id].id}, function() { $scope.items.splice(_id,1); $scope.selected = []; }); } else { var ids = []; angular.forEach($scope.selected, function(_id) { ids.push($scope.items[_id].id); }); Items['delete']({ids:ids}, function(){ angular.forEach($scope.selected, function(_id) { $scope.items.splice(_id,1); }); $scope.selected = []; }); } }; $scope.selectItem = function(e) { if ((e.target||e.srcElement).tagName!='TD') return; var state = this.item.selected = !this.item.selected, _id = this.item._id; if (state) $scope.selected.push(_id); else angular.forEach($scope.selected, function(v,k) { if (v==_id) { $scope.selected.splice(k,1); return false; } }); }; ...
      
      





selectItem()関数は、要素の選択されたプロパティを設定し、その番号を特別な$ scope.selected配列に追加します。 ところで、要素番号は_idプロパティにあり、バックエンドから要素を受け取るときに入力します; AngularJS自体は追加しません。 deleteItem()関数は、$ scope.selected配列にリストされている要素をそれぞれ削除します。 $リソースオブジェクトに組み込まれたdelete()メソッドを使用します。 (これは、Items.delete()ではなく、Items ['delete']()という式を使用して呼び出されます。なぜなら、私のIDEは、deleteが組み込みのJavascript演算子であると信じており、いエラーを表示するからです...オブジェクトの場合Items.delete === Items ['delete'])



その他



本文に含まれていなかったそれらの瞬間でストーリーを補足します。

1. {{中括弧}}内のテンプレートでは、コンストラクターの$ scopeオブジェクトのプロパティとして宣言されている変数(および関数)が宣言されています。

2.テンプレート内のグローバルオブジェクトは使用できません。それらにアクセスするには、たとえば次のように、個別のプロパティに明示的に割り当てる必要があります。$ scope.Math = Math; そして、次のようにテンプレートで使用します:{{Math.min(a、b)}}。

3.別のコントローラーのスコープにアクセスする方法はあまり明確ではありません。 確かにあなたはできますが、私はまだ方法を見つけていません...

4.グラフィックライブラリなど、コントローラーに配置されていないコードからコントローラーのスコープにアクセスする方法はあまり明確ではありません。 ただし、すべてのコードをコントローラーに含める必要があるため、これを行う必要はありません...

5. $ scope。$ Watch()関数は、スプライス/プッシュ関数などによって配列プロパティが処理されると機能しません。



結果



動作するデモはこちらから入手できます: http : //lexxpavlov.com/ng-admin/v2/ (読み取り専用)

ソースはGitHubで表示できます: https : //github.com/lexxpavlov/angular-admin/



All Articles