Ajax/jsonを使用して「select」フィルターを含むng-tableにデータを入力するにはどうすればよいですか?
問題を示すプランク:http://plnkr.co/Zn09LV
私はAngualrJSとng-table拡張機能を理解しようとしていますが、JavaScriptで定義された静的データを使用している場合は、フィルターなどが機能するいくつかの素敵なテーブルを取得できますが、実際のデータをにロードしようとすると私が引っ掛かったテーブル。
Ng-tableの本体は正しく入力されており、テキストフィルターのみを使用している限り、すべてが機能しているように見えます。
<td data-title="'Name'" filter="{ 'Name': 'text' }" sortable="'Name'">
{{user.Name}}
</td>
正常に動作します。
ただし、これを更新して選択フィルターを使用すると、次のようになります。
<td data-title="'Name'" filter="{ 'Name': 'select' }" sortable="'Name'" filter-data="Names($column)">
{{user.Name}}
</td>
サーバーからデータが返される前にNames変数が常に評価されるという、同期の問題が発生しました。 (おそらく、サーバーへの要求が送信される前にNames varibaleが評価されます。)これは、フィルターの空のリストを取得することを意味します。
サーバーからデータが返されると、選択フィルターを更新する方法が見つからないようです。フィルタリストを作成するコードを最初に再実行しても効果がないようです。更新された変数が読み取られないように、ng-tableをトリガーしてフィルタを再チェックする方法がわかりません。また、非同期呼び出しが完了するまで変数の評価を延期する方法がわかりません。
私のJavaScriptには、ng-table GitHubページのサンプルajaxコードをほぼ使用し、selectフィルターのサンプルコードを追加しました。
$scope.tableParams = new ngTableParams({
page: 1, // show first page
count: 10, // count per page
sorting: {
name: 'asc' // initial sorting
}
}, {
total: 0, // length of data
getData: function($defer, params) {
// ajax request to api
Api.get(params.url(), function(data) {
$timeout(function() {
// update table params
var orderedData = params.sorting ?
$filter('orderBy')(data.result, params.orderBy()) :
data.result;
orderedData = params.filter ?
$filter('filter')(orderedData, params.filter()) :
orderedData;
$scope.users = orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count());
params.total(orderedData.length); // set total for recalc pagination
$defer.resolve($scope.users);
}, 500);
});
}
});
var inArray = Array.prototype.indexOf ?
function (val, arr) {
return arr.indexOf(val)
} :
function (val, arr) {
var i = arr.length;
while (i--) {
if (arr[i] === val) return i;
}
return -1
};
$scope.names = function(column) {
var def = $q.defer(),
arr = [],
names = [];
angular.forEach(data, function(item){
if (inArray(item.name, arr) === -1) {
arr.Push(item.name);
names.Push({
'id': item.name,
'title': item.name
});
}
});
def.resolve(names);
return def;
};
追加の$ q.defer()を追加し、最初のデータをラップしてから$ scope.names関数をラップすることを数回試みましたが、promiseとdeferについての私の理解は、何かを機能させるのに十分なほど強力ではありません。
GitHubには、これがng-tableのバグであることを示唆するメモがいくつかありますが、それが当てはまるのか、それとも何か気の毒なことをしているだけなのかはわかりません。
https://github.com/esvit/ng-table/issues/186
続行する方法に関するポインタは非常に高く評価されています
-ケイン-
同様の問題がありましたが、少し複雑な問題がありました。フィルタのリストを動的に更新できるようにしたかったのですが、とにかく$ scope変数に含める必要があるため、完全に実行可能と思われました。基本的に、$scope.filterOptions = [];
があれば、filter-data="filterOptions"
を設定でき、そのリストへの更新は自動的に反映されると思っていました。私は間違っていた。
しかし、私はかなり良いと思う解決策を見つけました。まず、ngTable選択フィルターテンプレートをオーバーライドする必要があります(これを行う方法がわからない場合は、$templateCache
を使用する必要があり、オーバーライドする必要のあるキーは'ng-table/filters/select.html'
です)。
通常のテンプレートでは、このng-options="data.id as data.title for data in $column.data"
のようなものがありますが、これに関する問題は、$column.data
が$scope.filterOptions
を更新しても変更されない固定値であるということです。
私の解決策は、オプションのリスト全体を渡すのではなく、$ scope keyのみをfilter-dataとして渡すことです。したがって、filter-data="filterOptions"
の代わりに、filter-data="'filterOptions'"
を渡してから、テンプレートにng-options="data.id as data.title for data in {{$column.data}}"
のような小さな変更を加えます。
明らかに、これは選択フィルターの動作方法に対する重要な変更です。私の場合、テーブルが1つしかない非常に小さなアプリの場合でしたが、このような変更によって他の選択が失敗するのではないかと心配するかもしれません。その場合は、「select」をオーバーライドするだけでなく、このソリューションをカスタムフィルターに組み込むことをお勧めします。
カスタムフィルター でそれを達成することができます:
Ngtableの標準選択フィルターの コード は次のように述べています。
_<select ng-options="data.id as data.title for data in column.data"
ng-model="params.filter()[name]"
ng-show="filter == 'select'"
class="filter filter-select form-control" name="{{column.filterName}}">
</select>
_
このデータを呼び出すと、次のように渡されます。filter-data="names($column)"
そしてngtableがデータの取得を処理します。これが外部リソースで機能しない理由がわかりません。あなたが指摘したように、それは$ columnとpromiseと関係があるに違いありません。
これを回避するために、コードで簡単な回避策を実行しました。次のような独自の選択フィルターテンプレートを作成します。
_<select id="filterTest" class="form-control"
ng-model="tableParams.filter()['test']"
ng-options="e.id as e.title for e in externaldata">
</select>
_
この外部データをコントローラーにフェッチします。
_$scope.externaldata = Api.query(); // Your custom api call
_
完全に機能しますが、データにid
があるので、name
関数は必要ありません。
このソリューションは最適ではないことを理解しています。誰かがこの「回避策」以上にここに書き込み、私たちを啓発するかどうかを見てみましょう。 esvit も時々ここにあります;)
これは私のために働きます:
HTML:
<td data-title="'Doc type'" filter="{ 'doc_types': 'select' }" filter-data="docTypes()" sortable="'doc_types'">
{{task.doc_type}}
</td>
AngularJS:
$scope.docTypes = function ($scope)
{
var def = $q.defer();
//var docType = [
// {'id':'4', 'title':'Whatever 1'},
// {'id':'9', 'title':'Whatever 2'},
// {'id':'11', 'title':'Whatever 3'}
//];
// Or get data from API.
// Format should be same as above.
var docType = $http.get('http://whatever.dev', {
params: { param1: data1 }
});
//Or with Restangular
var docType = Restangular.all('/api/doctype').getList();
def.resolve(docType);
return def;
};
@Andiónが述べたように、 カスタムフィルター で達成できます。
Promises( Angularの$ qサービス )、興味深い Promisesに関するAndyの記事
$ scope.namesメソッドを修正し、非同期データを返し、遅延オブジェクトを次のように解決する$ httpサービスを追加できます。
$scope.names = function(column) {
var def = $q.defer();
/* http service is based on $q service */
$http({
url: siteurl + "app/application/fetchSomeList",
method: "POST",
}).success(function(data) {
var arr = [],
names = [];
angular.forEach(data, function(item) {
if (inArray(item.name, arr) === -1) {
arr.Push(item.name);
names.Push({
'id': item.name,
'title': item.name
});
}
});
/* whenever the data is available it resolves the object*/
def.resolve(names);
});
return def;
};
同様の問題が発生しましたが、フィルター値を取得するために追加のAJAX呼び出しを行いたくありませんでした。
OPのコードの問題は、$ scope.dataが設定される前にfilter-data関数が実行されることです。これを回避するために、Angular $ watchを使用して$ scope.dataの変更をリッスンしました。$ scope.dataが有効になると、filter-dataが正しく入力されます。
$scope.names2 = function () {
var def = $q.defer(),
arr = [],
names = [];
$scope.data = "";
$scope.$watch('data', function () {
angular.forEach($scope.data, function (item) {
if (inArray(item.name, arr) === -1) {
arr.Push(item.name);
names.Push({
'id': item.name,
'title': item.name
});
}
});
});
def.resolve(names);
return def;
};
変更でフォークされた元のプランク: http://plnkr.co/edit/SJXvpPQR2ZiYaSYavbQA
こちらもご覧くださいSO $ watchに関する質問: AngularJSで$ scope。$ watchと$ scope。$ applyを使用するにはどうすればよいですか?
Diabloが述べたように$ q.defer()で問題を解決しました
ただし、isコードは実際には非常に単純で単純です。
hTMLの場合:
<td ... filter-data="countries">
コントローラー内:
$scope.countries = $q.defer();
$http.get("/getCountries").then(function(resp){
$scope.countries.resolve(resp.data.data);
})
「まず、ngTable選択フィルターテンプレートをオーバーライドする必要があります(これを行う方法がわからない場合は、$ templateCacheを使用する必要があり、オーバーライドする必要のあるキーは 'ng-table/filters/select.html'です) 。」
Ng-tableのスクリプトの下にオーバーライドされたスクリプトを追加しましたが、すべてうまくいきました...
<script id="ng-table/filters/select.html" type="text/ng-template">
<select ng-options="data.id as data.title for data in {{$column.data}}" ng-table-select-filter-ds="$column" ng-disabled="$filterRow.disabled" ng-model="params.filter()[name]" class="filter filter-select form-control" name="{{name}}"> <option style="display:none" value=""></option> </select>
</script>
私がしたことは、値を含むselectタグを配置し、ng-modelにフィルターの値を返すようにすることです。
プレーンテキストを翻訳する必要があったので、これは役に立ちました。
<td data-title="'Status'| translate" ng-bind = "("cc_assignment_active"== '0') ? ('Inactive' | translate) : ('Active'| translate)"
filter="{ cc_assignment_active: 'select3'}" >
</td>
<script id="ng-table/filters/select3.html" type="text/ng-template">
<select class="filter filter-select form-control" ng-model="params.filter()[name]" name="{{name}}">
<option active value="" translate>---All---</option>
<option value="1" translate>Active</option>
<option value="0" translate>Inactive</option>
</select>