how can we get exact key match of object array in angularjs - angularjs

I am trying to get the exact key match of object array in angularjs instead of that I am getting all matching results. How can i achieve that?
here is the controller code:-
var myApp = angular.module('myApp', []);
myApp.controller('ToddlerCtrl', function ($scope,$filter) {
$scope.toddlers = [
{
"name": "Toddler One",
"tid": "85",
},
{
"name": "Toddler Two",
"tid": "485",
},
{
"name": "Toddler Three",
"tid": "4485",
} ,
{
"name": "Toddler Four",
"tid": "8845",
}
];
var found = $filter('filter')($scope.toddlers, 85 );
console.log(found); // I want the exact match i.e. ("Toddler one" with "85" only) but I am getting 3 results.
});
here is the Plnkr

The solution is quite easy: Add a true as a third param to indicate that the search needs be exact. Note that 85 won´t match anything though, you need to put '85':
var found = $filter('filter')($scope.toddlers, '85' , true);
For more info check the comparator parameter in docs: Angular Filter

Related

Iterating over json response with duplicate keys in AngularJS [duplicate]

This question already has answers here:
How to get the JSON with duplicate keys completely in javascript
(5 answers)
Closed 4 years ago.
I have been trying to fetch a section of the response json data from an API call in my angular application.
I have tried various combinations but I am only able to fetch the last record and not both the records.
Here is my html
<div ng-controller="MyCtrl">
<li ng-repeat="x in textvalues">
{{ x.event.description }}
</li>
</div>
and the controller code
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $http) {
$http.get('http://www.vizgr.org/historical-events/search.php?', {params: {format: 'json', query:'India', limit:'2'}}).then(function(response) {
$scope.textvalues = response.data;
console.log($scope.textvalues);
});
}
The response from the API call is as follows:
{
"result": {
"count": "2",
"event": {
"date": "-293",
"description": "When an invasion of",
"lang": "en",
"category1": "By place",
"category2": "Persia",
"granularity": "year"
},
"event": {
"date": "-250",
"description": "The Mauryan s",
"lang": "en",
"category1": "By place",
"category2": "India",
"granularity": "year"
}
}
}
And I am trying to print the event description in loop on the UI
Here is my fiddle - http://jsfiddle.net/nirajupadhyay/Jd2Hw/105/
I have tried various combination of the response data but unable to get to both the descriptions.
Kindly help.
I feel this API is somehow massively wrong. A JSON object can never have 2 identical keys with different values in them. If you check the network tab, then you will see that the response has only 1 event key in the object its value is the last value that is returned by the object. So although it might show 2 events in stringified version but it will never hold 2 values to same key in a JSON object.
Read Does JSON syntax allow duplicate keys in an object?
Instead of passing format: json to API, do not pass any param for format. It will give the result in xml format. Then convert this xml format to json format either by some library or the code as shown below.
var myApp = angular.module('myApp', []);
function MyCtrl($scope, $http) {
$http.get('http://www.vizgr.org/historical-events/search.php?', {
params: {
/* format: 'json', */
query: 'India',
limit: '2'
}
}).then(function(response) {
console.log(response.data);
var dom = parseXml(response.data);
var json = xml2json(dom)
console.log(json)
$scope.events = json.result.event;
});
function parseXml(xml) {
var dom = null;
if (window.DOMParser) {
try {
dom = (new DOMParser()).parseFromString(xml, "text/xml");
}
catch (e) { dom = null; }
}
else if (window.ActiveXObject) {
try {
dom = new ActiveXObject('Microsoft.XMLDOM');
dom.async = false;
if (!dom.loadXML(xml)) // parse error ..
window.alert(dom.parseError.reason + dom.parseError.srcText);
}
catch (e) { dom = null; }
}
else
alert("cannot parse xml string!");
return dom;
}
function xml2json(xml) {
try {
var obj = {};
if (xml.children.length > 0) {
for (var i = 0; i < xml.children.length; i++) {
var item = xml.children.item(i);
var nodeName = item.nodeName;
if (typeof (obj[nodeName]) == "undefined") {
obj[nodeName] = xml2json(item);
} else {
if (typeof (obj[nodeName].push) == "undefined") {
var old = obj[nodeName];
obj[nodeName] = [];
obj[nodeName].push(old);
}
obj[nodeName].push(xml2json(item));
}
}
} else {
obj = xml.textContent;
}
return obj;
} catch (e) {
console.log(e.message);
}
}
}
Check js fiddle here.
As Nandita Arora Sharma answered the API response is wrong.
RFC-7159, standard for JSON published by (IETF), states:
" 4. Objects
... single colon comes after each name, separating the name
from the value. A single comma separates a value from a following
name. The names within an object SHOULD be unique."
API Test with Postman. Which renders the JSON and as mentioned and there is only one event. This is possibly the reason why angular can only repeat 1 time
within the raw data has all event object with duplicated keys.
You need to pass $http as a parameter to your controller , you can see the particular error says "$http is not defined" in controller
function MyCtrl($scope,$http) {
WORKING FIDDLE
To answer your actual question, your service returns an object not an array . iT returns an object of object. so you cannot use ng-repeat in this case. it returns on
{"result":{"count":"3","event":{"date":"-250","description":"The
Mauryan ''Lion Capital of Asoka'', is erected as part of a pillar at
Sarnath, Uttar Pradesh in India (approximate date). It is now
preserved at the Sarnath Museum in
Sarnath.","lang":"en","category1":"By
place","category2":"India","granularity":"year"}}}
inorder to access event you can just use
{{textvalues.result.event.description}}
DEMO
The object structure that you are referring to can never exist
{
"result": {
"count": "2",
"event": {
"date": "-293",
"description": "When an invasion of",
"lang": "en",
"category1": "By place",
"category2": "Persia",
"granularity": "year"
},
"event": {
"date": "-250",
"description": "The Mauryan s",
"lang": "en",
"category1": "By place",
"category2": "India",
"granularity": "year"
}
}
}
If any key is repeated in an object, it overrides the previous set value in the JSON object key value pair. What the API should return should be something like given below, then only you can get all event objects.
{
"result": {
"count": "3",
"event": [{
"date": "-250",
"description": "The Mauryan ''Lion Capital of Asoka'', is erected as part of a pillar at Sarnath, Uttar Pradesh in India (approximate date). It is now preserved at the Sarnath Museum in Sarnath.",
"lang": "en",
"category1": "By place",
"category2": "India",
"granularity": "year"
},
{
"date": "-250",
"description": "The Mauryan ''Lion Capital of Asoka'', is erected as part of a pillar at Sarnath, Uttar Pradesh in India (approximate date). It is now preserved at the Sarnath Museum in Sarnath.",
"lang": "en",
"category1": "By place",
"category2": "India",
"granularity": "year"
},
{
"date": "-250",
"description": "The Mauryan ''Lion Capital of Asoka'', is erected as part of a pillar at Sarnath, Uttar Pradesh in India (approximate date). It is now preserved at the Sarnath Museum in Sarnath.",
"lang": "en",
"category1": "By place",
"category2": "India",
"granularity": "year"
}]
}
}

$loaded is not working properly when the server data is changed

I am new to firebase and angularjs. For my sales application I would like to use both. So, in my app I am using AngularJS v1.5.8 + Firebase v3.3.0 + AngularFire 2.0.2. I have sales and users objects in firebase db, and has a business logic that one user can sell multiple products, but one product can have only one owner (user).
Here is the users and sales objects in database:
{
"sales" : {
"-KQlb5N6A9rclc5qcWGD" : {
"price" : 8,
"quantity" : {
"count" : 12,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Patlicanli Borek",
"user" : "-KQ52OJd-lwoDIWzfYFT"
},
"-KQlcScsq8cidk7Drs04" : {
"price" : 12,
"quantity" : {
"count" : 10,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Deneme",
"user" : "-KQ5-mZBt6MhYy401gGM"
},
"-KQzXHwOv2rC73scjV46" : {
"price" : 12,
"quantity" : {
"count" : 11,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Pacanga",
"user" : "-KQ5-mZBt6MhYy401gGM"
},
"-KSCBgpArtnKunUuEuVr" : {
"price" : 15,
"quantity" : {
"count" : 15,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Iskembe",
"user" : "-KQ52OJd-lwoDIWzfYFT"
}
},
"users" : {
"-KQ5-mZBt6MhYy401gGM" : {
"address" : "Halkali kucukcekmece",
"email" : "burak.kahraman#gmail.com",
"name" : "Burak Hero",
"nick" : "Burak'in Mutfagi"
},
"-KQ52OJd-lwoDIWzfYFT" : {
"address" : "Izmir kaynaklar",
"email" : "ayse#gmail.com",
"name" : "Ayse Kahraman",
"nick" : "Ayse'nin Mutfagi"
}
}
}
What I want to do is when my app is opened, it will show all sales together with corresponding user details. (just like main page of letgo application) Which means I should implement a simple join between sales and users objects. As far as I searched throughout internet and api docs, there is no way to implement this kind of join in a single call to firebase. (Pl correct me if I am wrong) So I used below method with using $loaded function inside of my SalesService to implement join.
angular.
module('core.sales')
.service('SalesService', function ($firebaseArray, $firebaseObject, UsersService) {
this.getAllSalesJoin = function () {
var sales;
var refSales = firebase.database().ref('sales');
sales = $firebaseObject(refSales);
sales.$loaded()
.then(function () {
angular.forEach(sales, function (sale) {
var saleUser = UsersService.getUserDetail(sale.user);
saleUser.$loaded()
.then(function () {
sale.user = saleUser;
});
});
});
return sales;
};
});
As you see I am fetching all sales, after it finishes, looping for each sale to get and set related user detail by calling another UsersService shown below
angular.
module('core.users')
.service('UsersService', function ($firebaseArray,$firebaseObject) {
this.getUserDetail = function (userId) {
var user;
var refUser = firebase.database().ref('users/'+userId);
user = $firebaseObject(refUser);
return user;
};
});
So far so good, when I call SalesService.getAllSalesJoin function within my Controller and print the JSON object using <pre>{{$ctrl.allSales | json}}</pre>, everything works as I wanted, below is the Controller code and printed JSON object in the template.
angular.
module('saleList').
component('saleList', {
templateUrl: 'MCTs/sale-list/sale-list-template.html',
controller: ['SalesService','UsersService', function SaleListController(SalesService,UsersService,$scope) {
this.allSales = SalesService.getAllSalesJoin();
}]
});
Template shows the merged objects
{
"$id": "sales",
"$priority": null,
"-KQlb5N6A9rclc5qcWGD": {
"price": 8,
"quantity": {
"count": 12,
"type": "porsiyon"
},
"status": "sale",
"title": "Patlicanli Borek",
"user": {
"$id": "-KQ52OJd-lwoDIWzfYFT",
"$priority": null,
"address": "Izmir kaynaklar",
"email": "ayse#gmail.com",
"name": "Ayse Kahraman",
"nick": "Ayse'nin Mutfagi"
}
},
"-KQlcScsq8cidk7Drs04": {
"price": 12,
"quantity": {
"count": 10,
"type": "porsiyon"
},
"status": "sale",
"title": "Deneme",
"user": {
"$id": "-KQ5-mZBt6MhYy401gGM",
"$priority": null,
"address": "Halkali kucukcekmece",
"email": "burak.kahraman#gmail.com",
"name": "Burak Hero",
"nick": "Burak'in Mutfagi"
}
},
.....
But the problem is, when server data is changed (new sale is entered or old one is deleted), angular automatically understands the change but it applies the change to the view without implementing or calling my joined function, it simply prints only the sales object not the merged one with users. Below is the showing object after server data is changed.
{
"$id": "sales",
"$priority": null,
"-KQlb5N6A9rclc5qcWGD": {
"price": 8,
"quantity": {
"count": 12,
"type": "porsiyon"
},
"status": "sale",
"title": "Patlicanli Borek",
"user": "-KQ52OJd-lwoDIWzfYFT"
},
"-KQlcScsq8cidk7Drs04": {
"price": 12,
"quantity": {
"count": 10,
"type": "porsiyon"
},
"status": "sale",
"title": "Deneme",
"user": "-KQ5-mZBt6MhYy401gGM"
},
....
I am confused why it behaves like that? Is my way to implement join using $loaded wrong? Or should I use another method to implement this kind of join? I am looking forward to see your priceless suggestions and ideas.
$loaded() only fires when the initial data has loaded. From the reference documentation (emphasis mine):
Returns a promise which is resolved when the initial object data has been downloaded from the database.
This is the main reason I often say: "if you're using $loaded(), you're doing it wrong".
You're right about needing to join data with multiple calls. In AngularFire you can extend $firebaseArray to perform such an operation. For a great example of how to do this, see this answer by Kato: Joining data between paths based on id using AngularFire
Thank for the guide #Frank. I read all your suggestions and found the solution. For contributing stackoverflow knowledge and to help others here is the complete solution for the problem.
I first created a new factory that extends $firebaseArray and override $$added and $$updated methods to perform join to Users object each time when the data is updated or added.
angular.
module('core.sales').factory("SalesFactory", function ($firebaseArray, Sales) {
return $firebaseArray.$extend({
$$added: function (snap) {
return new Sales(snap);
},
$$updated: function (snap) {
return this.$getRecord(snap.key).update(snap);
}
});
});
angular.
module('core.sales').factory("Sales", function ($firebaseArray, $firebaseObject) {
var refUsers = firebase.database().ref('users');
function Sales(snapshot) {
this.$id = snapshot.key;
this.update(snapshot);
}
Sales.prototype = {
update: function (snapshot) {
var oldTitle = angular.extend({}, this.title);
var oldPrice = angular.extend({}, this.price);
var oldQuantity = angular.extend({}, this.quantity);
this.userId = snapshot.val().user;
this.title = snapshot.val().title;
this.status = snapshot.val().status;
this.price = snapshot.val().price;
this.quantity = snapshot.val().quantity;
this.userObj = $firebaseObject(refUsers.child(this.userId));
if (oldTitle == this.title && oldPrice == this.price &&
oldQuantity.count == this.quantity.count && oldQuantity.type == this.quantity.type)
return false;
return true;
},
};
return Sales;
});
As you see, SalesFactory uses another factory called Sales. In that particular factory I retrieve all properties of Sales object and assign each of them to its corresponding property. And that is the case I am performing join to Users object by creating new property : this.userObj
One thing is missing that is just calling the new Factory instead of $firebaseArray
this.getAllSalesArray = function () {
var sales;
var refSales = firebase.database().ref('sales');
sales = SalesFactory(refSales);
return sales;
};
All in all, all Sales object joined with related User is printed to the view is,
[
{
"$id": "-KQlb5N6A9rclc5qcWGD",
"userId": "-KQ52OJd-lwoDIWzfYFT",
"title": "Patlicanli Borek",
"status": "sale",
"price": 12,
"quantity": {
"count": 11,
"type": "tabak"
},
"userObj": {
"$id": "-KQ52OJd-lwoDIWzfYFT",
"$priority": null,
"address": "İzmir kaynaklar",
"email": "ayse#gmail.com",
"name": "Ayşe Kahraman",
"nick": "Ayşe'nin Mutfağı"
}
},
{
"$id": "-KQlcScsq8cidk7Drs04",
"userId": "-KQ5-mZBt6MhYy401gGM",
"title": "Deneme",
"status": "sale",
"price": 12,
"quantity": {
"count": 10,
"type": "porsiyon"
},
"userObj": {
"$id": "-KQ5-mZBt6MhYy401gGM",
"$priority": null,
"address": "Halkalı küçükçekmece",
"email": "burak.kahraman#gmail.com",
"name": "Burak Hero",
"nick": "Burak'ın Mutfağı"
}
},
...
]

how to convert json data in angularJs

I am getting json data from service. this is my json data:
[
{
"id":"1",
"body":"sample text",
"read":"true",
"checked":"true"
},
{
"id":"2",
"body":"sample text",
"read":"true",
"checked":"false"
}
]
I want to read it as id=2,read=true , how to convert this json string format to actual datatype.
You need to loop through your array and process the boolean values that are currently represented as text. In the example below, I've used JSON.parse to process the boolean text and then saved the result back to the read variable. Save applies to id but I've gone for parseInt in this case.
.controller('MyCtrl', function($scope) {
//example mock http call
function getHttpData() {
return [{
"id": "1",
"body": "sample text",
"read": "false",
"checked": "true"
}, {
"id": "2",
"body": "sample text",
"read": "true",
"checked": "false"
}];
}
function getData() {
var data = getHttpData();
//process array as required
data.forEach(function(value) {
value.id = parseInt(value.id);
value.read = JSON.parse(value.read);
});
return data;
}
$scope.data = getData();
});
Fiddle here:
https://jsfiddle.net/tmakin/cvzc4mks/5/
You want to pass your JSON string to the JSON.parse() function. That will return a JavaScript object that you can use normally.

Why is my $watch showing an array that appears to be empty?

I'm working on creating an AngularJS directive in order to use D3 to render a visualization, but I'm running into problems when it comes to setting a $watch. The majority of my stuff looks extremely similar to the AngularJS tutorial. My resources are configured in a file called resources.json, which I'm sure is returning correctly.
Here's the relevant code of what I have so far:
app.js
var resourceApp = angular.module("resourceApp", [
'ngRoute',
'resourceControllers',
'resourceDirectives',
'resourceServices'
]);
/* ... routing config ... */
controllers.js
var resourceControllers = angular.module('resourceControllers', []);
resourceControllers.controller("OverviewCtrl", [ '$scope', 'Resource',
function($scope, Resource) {
$scope.resources = Resource.query();
}
]);
/* ... other controllers ... */
directives.js
var resourceDirectives = angular.module('resourceDirectives', []);
resourceDirectives.directive("resourceVisualization", function() {
return {
restrict: 'E',
scope: {
resources: '='
},
link: function(scope, el, attrs) {
// svg setup is here
scope.$watch("resources", function(nRes, oRes) {
if (nRes) {
// this logs an array of Resource objects
//(once expanded in Firebug)
console.log(nRes);
var cats = nRes.map(function(r) { return r.category; });
// this logs an empty array
console.log(cats);
}
});
}
};
});
overview.html
<ul>
<li ng-repeat="resource in resources">
{{resource.name}}
</li>
</ul>
<resource-visualization resources="resources"></resource-visualization>
resources.json (which is what services.js is configured to pull from)
[
{
"id": 1,
"category": "test",
"type": "sample",
"name": "Test1"
},
{
"id": 2,
"category": "test",
"type": "sample4",
"name": "Test2"
},
{
"id": 3,
"category": "fake",
"type": "sample1",
"name": "Test3"
},
{
"id": 4,
"category": "new",
"type": "sample2",
"name": "Test4"
}]
Now, I know that the REST call is working, because the <ul> is populated. However, in the directive, the logging statements are returning empty arrays. I'm aware of the the async-ness of $resource, but the object that is logged first in the $watch contains $resolved: true.
Am I missing something?
It's all fine. Your call to Resource.query() returns immediately an empty array. If the ajax call returns the real data, your array will be filled with the arrived data. So the first assignment to $scope.resources fires your $watch function with an empty array. You my solve your problem if you are using the $watchCollection function. See http://docs.angularjs.org/api/ng.$rootScope.Scope for further information.

AngularJs - Filtering out duplicates in select control

I have a select list of axleTypes, each having a Type property of either a Front Axle or a Rear Axle. I can not seem to filter out duplicate words of 'Front' and 'Rear'.
Update:
Html:
<select ng-model="axleType.Type" ng-options="type for type in uniqueTypes">
Controller:
$scope.axleTypes = API.GetAxleTypes();
$scope.fixedAxleTypes = [
{ "$id": "1", "Description": "I beam, telescopic type shock absorbers", "Type": "Front", "Id": 1 },
{ "$id": "2", "Description": "Full-floating banjo housing", "Type": "Rear", "Id": 2 },
{ "$id": "3", "Description": "Something Else", "Type": "Rear", "Id": 2 },
{ "$id": "4", "Description": "I beam, telescopic type shock absorbers", "Type": "Front", "Id": 4 }
];
// This Works
$scope.uniqueTypes = _.uniq(_.pluck($scope.fixedAxleTypes, 'Type'));
// This does not
//$scope.uniqueTypes = _.uniq(_.pluck($scope.axleTypes, 'Type'));
// This does not
//$scope.uniqueTypes = _.uniq(_.pluck(API.GetAxleTypes(), 'Type'));
I am thoroughly confused. And yes, the API works, I copy pasted above data from
Chrome > Network>Response window
Seeing the error that you added in your edit, I'm sure that the reason is what I described in my comment, which is the use of the (axleType.Type) expression for the filter in a context where that expression cannot be evaluated. Since you're not using the expression in the filter implementation, you could just omit it altogether, I believe.
What i guess is API.GetAxleTypes(); should be doing some asynch tasks like calling $http.
if that is the case then $scope.axleTypes wont be of the array type you are looking for.
The the GetAxleTypes can look something similar to this.
Service definition:
{
uniqueaxleTypes:[],
GetAxleTypes = function($http,..){
var promise = $http({
//config
})
promise.then(function(response){
this.uniqueaxleTypes = _.uniq(_.pluck(response.data, 'Type'));
})
}
}
Then if you bind a scope variable to this uniqueaxleTypes it will always reflect the unique values.
Since $scope.fixedAxleTypes is a hard coded value, its working fine.

Resources