Catching ng-repeat duplicate error - angularjs

I have this template:
<li ng-repeat="item in results.items track by item.id"><ul result-list-line></ul></li>
Sometimes I get Duplicates in a repeater are not allowed. Basically a back-end problem, but I need to handle it in the FE.
Now, I know it's been discussed before: I can loop over the data and catch duplicate myself or I can bypass it by using track by $index. I don't want to do either – I want to catch the Angular error and handle it (display an error message, basically). How do I do that?
Here's my version to Stepan Kasyanenko's answer below:
.factory('$exceptionHandler', [ '$injector', function ( $injector ) {
return function (exception, cause) {
// Preventing circular reference when using $rootScope
var $rootScope = $injector.get('$rootScope');
var msg = exception.message;
// Still have the usual console error - extend instead of replace
var $log = $injector.get('$log');
$log.error.apply($log, arguments);
//catching ngRepeat duplicates
if ( msg.search("ngRepeat:dupes") > -1 ) {
msg = "Duplicate entries were sent from the server"
}
// Not always you get a cause string, but if so you might want to add it to the message
if ( cause ) {
msg += ". Cause: "+cause;
}
// No matter what I did, I couldn't inject any factory that uses $rootScope, so I used custom event instead
$rootScope.$emit("angularError", msg);
};
}])

You can use $exceptionHandler.
Live example on jsfiddle.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.countries = [{
countryName: 'United States',
countryCode: 1
}, {
countryName: 'Canada',
countryCode: 2
}, {
countryName: 'Bahamas',
countryCode: 3
}, {
countryName: 'Chile',
countryCode: 4
}, {
countryName: 'Chile',
countryCode: 4
}];
})
.factory('$exceptionHandler', function() {
return function(exception, cause) {
alert(exception)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<label>Country:</label>
<div ng-repeat="country in countries track by country.countryCode">
{{country.countryName}}
</div>
</div>
</div>
UPDATED
$exceptionHandler handle also custom error.
angular.module('ExampleApp', [])
.controller('ExampleController', function($scope) {
$scope.countries = [{
countryName: 'United States',
countryCode: 1
}, {
countryName: 'Canada',
countryCode: 2
}, {
countryName: 'Bahamas',
countryCode: 3
}, {
countryName: 'Chile',
countryCode: 4
}, ];
$scope.raiseError = function() {
throw "my raiseError";
}
$scope.addBadElement = function() {
$scope.countries.push({
countryName: 'Chile',
countryCode: 4
});
}
})
.factory('$exceptionHandler', function() {
return function(exception, cause) {
alert(exception);
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="ExampleApp">
<div ng-controller="ExampleController">
<label>Country:</label>
<button ng-click="raiseError()">
raise Error
</button>
<button ng-click="addBadElement()">
Add Bad Country
</button>
<div ng-repeat="country in countries track by country.countryCode">
{{country.countryName}}
</div>
</div>
</div>

Related

Controlling ng-repeat iterations

HTML :
<div ng-repeat="data in $ctrl.list">
<div ng-init="$ctrl.applyAction(data)">
<h4>{{data.name}}</h4>
<ul ng-if="data.steps">
<li ng-repeat="step in data.steps">{{step.name}}</li>
</ul>
</div>
</div>
Controller :
$onInit() {
this.list = [{
name: "First Obj"
}, {
name: "Second Obj"
}, {
name: "Third Obj"
}, {
name: "Fourth Obj"
}];
}
applyAction(data) {
this.someHttpService.getResponse(data).then(function(success) {
data.reqForSecondServiceCall = success.data;
this.secondServiceCall(data);
}, function(error) {
// console.log(error);
});
}
secondServiceCall(data) {
this.someHttpService.getSecondServiceResponse(data).then(function(success) {
data.status = success.data;
}, function(error) {
// console.log(error);
});
}
Currently ng-repeat will be iterating through the list object irrespective of the service calls made on each object (asynchronous).
And the desired functionality is to render the current object only when the applyActions method is completed on previous object.
One solution is to queue the calls in an event queue and then invoke the events one by one when previous call is completed
angular.module('myApp',[]).controller('myCtrl', function($scope, $http, $timeout){
$scope.title = 'welcome';
$scope.finishedEvent = '';
$scope.eventQueue = [];
$scope.list = [{
name: "First Obj"
}, {
name: "Second Obj"
}, {
name: "Third Obj"
}, {
name: "Fourth Obj"
}];
$scope.applyAction = function(data, index) {
//declare the event
var event = function(){
var testApi = "https://jsonplaceholder.typicode.com/posts";
$http.get(testApi).then(function(response) {
data.steps = response.data.slice(0,2);
$scope.finishedEvent = data.name;
}, function(error) {
console.log(error);
});
};
if(index == 0){
event();
}else{
$scope.eventQueue.push(event);
}
};
$scope.$watch('finishedEvent', function(){
if($scope.eventQueue.length > 0){
$timeout(function(){
console.log($scope.finishedEvent + '- loaded')
var event = $scope.eventQueue[0];
$scope.eventQueue.splice(0, 1); //remove current event from queue
event();
}, 1000);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="myApp" ng-controller="myCtrl">
<h1>{{title}}</h1>
<div ng-repeat="data in list">
<div ng-init="applyAction(data, $index)">
<h4>{{data.name}}</h4>
<ul ng-if="data.steps">
<li ng-repeat="step in data.steps">{{step.title}}</li>
</ul>
</div>
</div>
</body>
NOTE 1: I used a dummie api just to have live data
NOTE 2: Remove the $timeout, only added it to make the example clear
Here's a plunker with the example

Accessing value in sub nested object in JSON with Angular

I'm trying access values in sub array objects of results:
results: [
{
list_name: "E-Book Fiction",
display_name: "E-Book Fiction",
bestsellers_date: "2016-09-03",
published_date: "2016-09-18",
rank: 1,
rank_last_week: 0,
weeks_on_list: 1,
asterisk: 0,
dagger: 0,
amazon_product_url: "http://www.amazon.com/Great-Reckoning-Novel-Inspector-Gamache/dp/1250022134?tag=thenewyorktim-20",
isbns: [],
book_details: [
{
title: "A GREAT RECKONING",
description: "An instructor at the police academy is found murdered, perhaps by one of the cadets favored by Armand Gamache, the retired homicide chief of the Sûreté du Québec.",
contributor: "by Louise Penny",
author: "Louise Penny",
contributor_note: "",
price: 0,
age_group: "",
publisher: "Minotaur",
primary_isbn13: "9781250022127",
primary_isbn10: "1250022126"
}
],
reviews: [
{
book_review_link: "",
first_chapter_link: "",
sunday_review_link: "",
article_chapter_link: ""
}
]
}
so that I can grab values like title from book_details, but currently only able to display the whole response with:
<article class="search-result row" ng-repeat="books in results">
<div class="col-xs-12 col-sm-12 col-md-3">
{{books}}
</div>
</article>
My controller is fairly simple, just grabbing the result:
myBooks.controller('bookController', function($scope, $http){
$http.get("data-url")
.success(function(response) {
console.log(response);
$scope.results = response;
});
});
A screenshot of the object in web console:
You can write it like this . Create a new books array in the scope by iterating over the results array.
app.controller("MyController", ["$scope","$http",
function($scope, $http) {
$scope.books;
$http.get('test.json').then(function (response){
console.log(response.data.results);
$scope.results = response.data.results;
$scope.results.map(function(obj, index) {
if(obj.book_details) {
if(!($scope.books)) {
$scope.books = [];
}
for(var i in obj.book_details) {
$scope.books.push(obj.book_details[i]);
}
}
});
console.log($scope.books);
});
}]);
<article class="search-result row" ng-repeat="book in ">
<div class="col-xs-12 col-sm-12 col-md-3">
{{book.title}}
</div>
</article>
Working link

AngularJS NG-Repeat Seems to Not Work on Array with Single Object

I have found many posts about how ng-repeat does not play well with objects, and expects the data to be an array, but my data is an array, it just happens to have a single object(list2). I get list1 fine, and everything works perfect. According to everything that I have found, list2 should work. Anyone know why it doesn't?
Data coming from my factory:
(function(){
var Factory = function(){
var model = {
list1: [
{
id: "1_1",
type: "header",
headerText: "1_1 Header",
secondaryHeaderText: "",
paragraphText: "1_1 Paragraph",
image: ""
},
{
id: "1_2",
type: "header",
headerText: "Header 1_2",
secondaryHeaderText: "",
paragraphText: "1_2 Paragraph",
image: ""
},
{
id: "1_3",
type: "header",
headerText: "1_3 Header1",
secondaryHeaderText: "1_3 Header2",
paragraphText: "",
image: ""
}
],
list2: [
{
id: "2_1",
type: "link",
headerText: "2_1 Header",
paragraphText: "2_1 Paragraph",
linkText: "2_1 Link Text",
image: ""
}
]
};
var factory = {};
factory.getList1 = function(){
return model.list1;
};
factory.getList2 = function(){
return model.list2;
};
return factory;
};
angular.module('designApp').factory('Factory', Factory);
}());
HMTL
<div>
<!--works perfectly fine -->
<div ng-repeat="item in list1" ng-include="'app/partial/list1.html'"></div>
</div>
<div>
<div ng-repeat="item in list2" ng-include="'app/partial/list2.html'"></div>
</div>
Controller
(function(){
var Controller = function($scope, Factory){
$scope.list1 = [];
$scope.list2 = [];
function init(){
$scope.list1 = Factory.getList1();
$scope.list2 = Factory.getList2();
}
init();
};
Controller.$inject = ['$scope', 'Factory'];
angular.module('designApp')
.controller('Controller', Controller);
}());
This is all that is in list2.html. Does not render any of the model data but renders the html tags, and throws no errors.
<h2>{{list2.headerText}}</h2>
<p>{{list2.paragraphText}}</p>
Thanks in advance for any help!
You have to replace
<h2>{{list2.headerText}}</h2>
<p>{{list2.paragraphText}}</p>
by
<h2>{{item.headerText}}</h2>
<p>{{item.paragraphText}}</p>
working plunkr:
https://plnkr.co/edit/FC5KPpOl7gsmfva63veq?p=preview

AngluarJS: using filter in controller

I have this controller:
angular.module('myApp', [])
.controller('testCtrl', ['$scope', '$filter',
function($scope, $filter) {
$scope.users = [{
name: "Hanna",
gender: "female"
}, {
name: "Martin",
gender: "male"
}, {
name: "Kim",
gender: "transgender"
}];
$scope.traditionalUsers = $filter('filter')($scope.users,femaleOrMale());
function femaleOrMale(user) {
return ['female','male'].indexOf(user.gender);
}
}
]);
I want to filter users that has a property that matches an array. This does not work. See plunk. How can I correctly call the filter-filter?
Remove the ()
You're calling the femaleOrMale function without any parameters when you write femaleOrMale() in
$scope.traditionalUsers = $filter('filter')($scope.users, femaleOrMale());
What you write to pass the function itself to the filter is
$scope.traditionalUsers = $filter('filter')($scope.users, femaleOrMale);
This will make the page not throw an error anymore, however it wont display male and female as traditional users, which i suspect is what you want. You need to return a boolean from the femaleOrMale filter, and indexOf returns the index of the match, or -1 if there is no match, so compare for -1.
return ['female','male'].indexOf(user.gender) != -1;
angular.module('myApp', [])
.controller('testCtrl', ['$scope', '$filter',
function($scope, $filter) {
$scope.users = [{
name: "Hanna",
gender: "female"
}, {
name: "Martin",
gender: "male"
}, {
name: "Kim",
gender: "transgender"
}];
$scope.traditionalUsers = $filter('filter')($scope.users, femaleOrMale);
function femaleOrMale(user) {
return ['female','male'].indexOf(user.gender) != -1;
}
}
]);
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
</head>
<body>
<div ng-app='myApp'>
<div ng-controller='testCtrl'>
<h3>Male and female users:</h3>
{{traditionalUsers}}
</div>
</div>
</body>
</html>

Multiple custom filters in angular.js

I have a multi check box application which requires me to have multiple filters. I have been unable to use multiple filters even if I try to hard code an array to filter on. Here is an example I have created to try to make this work.
Working Example
HTML MARKUP:
<body ng-app="app">
<div ng-controller="MainCtrl">
<div ng-repeat="item in data.sessions | IndustryFilter : data.sessions.industry ">
{{item.id}}
</div>
</div>
Javascript
var app = angular.module("app", [])
.controller("MainCtrl", function ($scope, MatchedFilterList) {
$scope.data = {"sessions": [{
"id": "a093000000Vhzg7AAB",
"industry": ["Automovtive"],
"sessionName": "Keynote",
},
{
"id": "a093000000zg7AAB",
"industry": ["Automovtive", "Retail"],
"sessionName": "Keynote2",
},
{
"id": "a093er000f00zg7AAB",
"industry": ["Automovtive", "Retail", "Consumer Goods"],
"sessionName": "Keynote3",
}
]};
}).filter("IndustryFilter", function (MatchedFilterList) {
return function () {
var filtered = [];
angular.forEach(MatchedFilterList.industry, function (item) {
filtered.push(item);
});
console.log("Filter: Filter " + filtered)
return filtered;
};
})
.factory("MatchedFilterList", function(){
var matchedFilterList = {};
matchedFilterList.industry = {
"Automotive": "Automotive",
"Retail" : "Retail"
};
return matchedFilterList;
});

Resources