Reputation: 27087
I am calling my Firebase Database JSON in AngularJS and trying to filter through the code. I keep seeing Array errors like:
Error: [filter:notarray] http://errors.angularjs.org/1.4.9/filter/notarray?
My HTML:
<form>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-search"></i></div>
<input type="text" class="form-control" placeholder="Search" ng-model="searchTranslation">
</div>
</div>
</form>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>ID</th>
<th>English</th>
<th>Arabic</th>
<th>Status</th>
<th>Settings</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="i in translations | filter:searchTranslation track by $index">
<td>{{$index}}</td>
<td>{{i.transNameEn}}</td>
<td class="textarabic">{{i.transNameArabic}}</td>
<td class="{{i.transStatus}}">{{i.transStatus}}</td>
<td>
<a ng-href="{{url}}pages-edit-translation/{{i.transId}}"
class="btn caps btn-warning">
EDIT
</a>
</td>
</tr>
</tbody>
</table>
My JS code
// @pages-home-translation
cmsApp.controller('pages-home-translation', function ($scope, $http) {
$scope.sortType = 'English'; // set the default sort type
$scope.sortReverse = false; // set the default sort order
$scope.searchTranslation = ''; // set the default search/filter term
$http.get(firebase_url+'cms/translations.json'+randstatus).success(function(data) {
$scope.translations=data; // or data.data
});
});
My JSON:
{
"00hym5km2tf08s38fr-ivjgnsw6": {
"notes": "",
"transCreation": "11/15/2016, 4:14:07 PM",
"transId": "00hym5km2tf08s38fr-ivjgnsw6",
"transModified": "11/15/2016, 4:14:07 PM",
"transNameArabic": "استخدم كعنوان الدفع الافتراضي",
"transNameEn": "Use as my default billing address",
"transStatus": "FIXED"
},
"08zq3t9411zaketnpnwmi-ivjhzz5q": {
"notes": "",
"transCreation": "11/15/2016, 4:51:35 PM",
"transId": "08zq3t9411zaketnpnwmi-ivjhzz5q",
"transModified": "11/15/2016, 4:51:35 PM",
"transNameArabic": "احذية كرة القدم",
"transNameEn": "Football Shoes",
"transStatus": "FIXED"
},
"0aoycw0b0c9v8ov6xbt9-ivjhwnv6": {
"notes": "",
"transCreation": "11/15/2016, 4:49:00 PM",
"transId": "0aoycw0b0c9v8ov6xbt9-ivjhwnv6",
"transModified": "11/15/2016, 4:49:00 PM",
"transNameArabic": "جينز واسع",
"transNameEn": "Flared Jeans",
"transStatus": "FIXED"
}
}
Note: If I remove
<tr ng-repeat="i in translations | filter:searchTranslation track by $index">
and do this instead it works:
<tr ng-repeat="i in translations">
But clearly I wish to filter the results using the search form. Using data.data
shows no errors in the console.
I have tried to do setTimeOut
on the http.get
and also delays so that it loads the JSON first but it is still not working.
Thanks
Upvotes: 2
Views: 706
Reputation: 27192
Some changes needed as per your code. First of all $scope.translations
should be an array but as per your JSON it's not an array. ng-repeat
directive only accepts an array to repeat the data
.
Your $scope.translations
should be like that as per your HTML
Structure :
$scope.translations = [
{
"notes": "",
"transCreation": "11/15/2016, 4:14:07 PM",
"transId": "00hym5km2tf08s38fr-ivjgnsw6",
"transModified": "11/15/2016, 4:14:07 PM",
"transNameArabic": "استخدم كعنوان الدفع الافتراضي",
"transNameEn": "Use as my default billing address",
"transStatus": "FIXED"
},
{
"notes": "",
"transCreation": "11/15/2016, 4:51:35 PM",
"transId": "08zq3t9411zaketnpnwmi-ivjhzz5q",
"transModified": "11/15/2016, 4:51:35 PM",
"transNameArabic": "احذية كرة القدم",
"transNameEn": "Football Shoes",
"transStatus": "FIXED"
},
{
"notes": "",
"transCreation": "11/15/2016, 4:49:00 PM",
"transId": "0aoycw0b0c9v8ov6xbt9-ivjhwnv6",
"transModified": "11/15/2016, 4:49:00 PM",
"transNameArabic": "جينز واسع",
"transNameEn": "Flared Jeans",
"transStatus": "FIXED"
}
];
Working demo :
var cmsApp = angular.module('myApp',[]);
cmsApp.controller('pages-home-translation', function ($scope, $http) {
$scope.sortType = 'English'; // set the default sort type
$scope.sortReverse = false; // set the default sort order
$scope.searchTranslation = ''; // set the default
$scope.translations = [
{
"notes": "",
"transCreation": "11/15/2016, 4:14:07 PM",
"transId": "00hym5km2tf08s38fr-ivjgnsw6",
"transModified": "11/15/2016, 4:14:07 PM",
"transNameArabic": "استخدم كعنوان الدفع الافتراضي",
"transNameEn": "Use as my default billing address",
"transStatus": "FIXED"
},
{
"notes": "",
"transCreation": "11/15/2016, 4:51:35 PM",
"transId": "08zq3t9411zaketnpnwmi-ivjhzz5q",
"transModified": "11/15/2016, 4:51:35 PM",
"transNameArabic": "احذية كرة القدم",
"transNameEn": "Football Shoes",
"transStatus": "FIXED"
},
{
"notes": "",
"transCreation": "11/15/2016, 4:49:00 PM",
"transId": "0aoycw0b0c9v8ov6xbt9-ivjhwnv6",
"transModified": "11/15/2016, 4:49:00 PM",
"transNameArabic": "جينز واسع",
"transNameEn": "Flared Jeans",
"transStatus": "FIXED"
}
];
});
table,th,td {
border:1px solid black;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="pages-home-translation">
<form>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-search"></i></div>
<input type="text" class="form-control" placeholder="Search" ng-model="searchTranslation">
</div>
</div>
</form>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>ID</th>
<th>English</th>
<th>Arabic</th>
<th>Status</th>
<th>Settings</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="i in translations | filter:searchTranslation">
<td>{{$index}}</td>
<td>{{i.transNameEn}}</td>
<td class="textarabic">{{i.transNameArabic}}</td>
<td class="{{i.transStatus}}">{{i.transStatus}}</td>
<td>
<a ng-href="{{url}}pages-edit-translation/{{i.transId}}"
class="btn caps btn-warning">
EDIT
</a>
</td>
</tr>
</tbody>
</table>
</div>
Upvotes: 0
Reputation: 9800
The problem is that the filter
doesn't filter objects by default, even though ng-repeat
can iterate over an object properties. Therefore there is two way out for this issue: convert the object to array before set it to your model, or you can create a custom filter (which is not much reusable) to filter over an object properties.
To create a custom filter you can do:
Html
<ANY ng-repeat="item in items | filterObject:myModel">
JS
angular.filter('filterObject', function () {
return function (obj, myModel) {
// filter logic, bla bla bla
};
});
To convert the object into an array, you can do it on controller side (or create a helper function, service, etc.) or you can create a custom filter to do so. For example:
Html
<ANY ng-repeat="item in items | asArray | filter:myModel">
JS
.filter('asArray', function() {
return function(obj /*, addKey*/ ) {
// in case of undefined just return the same object to pass through
if (!obj) return obj;
// return an object maped as array of key as an item
return Object.keys(obj).map(function(key) {
return obj[key];
});
};
});
Using a filter to conver array into an object, you can reuse the $filter
logic without having to implement a filter solution again. Therefore, I consider this better than usign a custom filter for the task.
A little less expensive approach would be converting it to an array before sending it to your model. However, you may want to keep the original property names (ids or whatever that is).
$http.get(firebase_url+'cms/translations.json'+randstatus).success(function(data) {
var myData = Object.keys(data).map(function(key) {
return obj[key];
});
$scope.translations = myData;
});
The following example implements this solution, using the second approach (there is 1 second delay simulating a server response or something to fill the translations
model):
var cmsApp = angular.module('cmsApp', []);
cmsApp.filter('asArray', function() {
return function(obj /*, addKey*/ ) {
// in case of undefined just return the same object to pass through
if (!obj) return obj;
// return an object maped as array of key as an item
return Object.keys(obj).map(function(key) {
return obj[key];
});
};
});
// @pages-home-translation
cmsApp.controller('pages-home-translation', function($scope, $http, $timeout) {
$scope.sortType = 'English'; // set the default sort type
$scope.sortReverse = false; // set the default sort order
$scope.searchTranslation = ''; // set the default search/filter term
var data = {
"00hym5km2tf08s38fr-ivjgnsw6": {
"notes": "",
"transCreation": "11/15/2016, 4:14:07 PM",
"transId": "00hym5km2tf08s38fr-ivjgnsw6",
"transModified": "11/15/2016, 4:14:07 PM",
"transNameArabic": "استخدم كعنوان الدفع الافتراضي",
"transNameEn": "Use as my default billing address",
"transStatus": "FIXED"
},
"08zq3t9411zaketnpnwmi-ivjhzz5q": {
"notes": "",
"transCreation": "11/15/2016, 4:51:35 PM",
"transId": "08zq3t9411zaketnpnwmi-ivjhzz5q",
"transModified": "11/15/2016, 4:51:35 PM",
"transNameArabic": "احذية كرة القدم",
"transNameEn": "Football Shoes",
"transStatus": "FIXED"
},
"0aoycw0b0c9v8ov6xbt9-ivjhwnv6": {
"notes": "",
"transCreation": "11/15/2016, 4:49:00 PM",
"transId": "0aoycw0b0c9v8ov6xbt9-ivjhwnv6",
"transModified": "11/15/2016, 4:49:00 PM",
"transNameArabic": "جينز واسع",
"transNameEn": "Flared Jeans",
"transStatus": "FIXED"
}
};
$timeout(function() {
$scope.translations = data; // or data.data
}, 1500);
});
angular.element(document).ready(function() {
angular.bootstrap(document, ['cmsApp']);
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.js"></script>
<div ng-controller="pages-home-translation">
<form>
<div class="form-group">
<div class="input-group">
<div class="input-group-addon"><i class="fa fa-search"></i>
</div>
<input type="text" class="form-control" placeholder="Search" ng-model="searchTranslation">
</div>
</div>
</form>
<table class="table table-bordered table-striped" border="1" cellpadding="4" style="border-collapse: collapse;">
<thead>
<tr>
<th>ID</th>
<th>English</th>
<th>Arabic</th>
<th>Status</th>
<th>Settings</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="i in translations | asArray | filter:searchTranslation track by $index">
<td>{{$index}}</td>
<td>{{i.transNameEn}}</td>
<td class="textarabic">{{i.transNameArabic}}</td>
<td class="{{i.transStatus}}">{{i.transStatus}}</td>
<td>
<a ng-href="{{url}}pages-edit-translation/{{i.transId}}" class="btn caps btn-warning">EDIT</a>
</td>
</tr>
</tbody>
</table>
</div>
Upvotes: 3