Conditions and loops withing AngularJS expressions? - javascript

I have one set of JSON data consisting of the following two objects:
{
"date":"20160118",
"entity":"01",
"security":{
"securityID":"191457",
"identifiers":[
{
"value":"345342532",
"type":"fii"
},
{
"value":"32453452",
"type":"isin"
},
{
"value":"48125D886",
"type":"cusip"
}
]
}
}
And:
{
"date":"342534543",
"entity":"01",
"security":{
"securityID":"3425435",
"identifiers":[
{
"value":"32543543",
"type":"fii"
}
]
}
}
I am creating a table using only AngularJS expressions and HTML.
I'm running into trouble when accessing security.identifiers[#].value. Depending on how many identifiers a security has - they may be in different array positions - meaning an "fii" could end up in the same column as a "cusip" for the previous row. Is there a way within my angular expression I can search through an array and find a string? In this case identifiers.type will be the key to knowing which column it is placed in. I've tried looping through in my javascript and providing an index to my expression, but I seem to be running into closure issues - I'm hooping there is a simpler way.
So far I have this for my identifiers columns - I'm aware they are wrong but hopefully will give you an idea of what I'm trying to do.
<td>{{data.security.identifiers.type === "fii" ? data.security.identifiers.value : ""}}</td>
<td>{{data.security.identifiers.type === "isin" ? data.security.identifiers.value : ""}}</td>
<td>{{data.security.identifiers.type === "cusip" ? data.security.identifiers.value : ""}}</td>

I can understand what do you want to do, the below code could work like you expect.
<td>{{data.security.identifiers.filter(function(v){return v.type == 'fii'})[0].value}}</td>
And to improve you can create a function to do the filter (probably in the Controller or Service):
$scope.identifierFilter = function(type) {
var filtered = this.data.security.identifiers.filter(function(v) {
return v.type == type;
});
return filtered && filtered[0] ? filtered[0].value : '';
};
and in the html
<td>{{identifierFilter('fii')}}</td>
I'm look at your example, I think the above code would work.But If you can change your JSON structor it will be more better and easy.
"identifiers":{
fii : 'dfdfdf',
isin : '32453452',
cusip: '48125D886'
}
html can simple like
<td>{{data.security.identifiers.fii}}</td>
And if you want to do this transform, I can give you further answer.
with could help
I update your example, sorry about some syntax error about above code.
please check: https://plnkr.co/edit/NgvJKrfOGmKHfOap08xU?p=preview
This is reconstructor data before template use.It's better if the data structor can change, choice which you like.
https://plnkr.co/edit/7eHAvSaBcOiTajOomNEs?p=preview

Probably, your best option is to use a directive, or filter depending on your requirements. A directive will make your mark-up clear and you will encapsulate the functionality you need. For example.
<table border="1" style="width:100%">
<tr>
<th>Entity</th>
<th>SecurityID</th>
<th>Security1</th>
<th>Security2</th>
<th>Security3</th>
<th>date</th>
</tr>
<tr ng-repeat="data in JSONData">
<td>{{data.entity}}</td>
<td>{{data.security.securityID}}</td>
<td><identifier identifiers="data.security.identifiers" render-type="fii"></identifier></td>
<td><identifier identifiers="data.security.identifiers" render-type="isin"></identifier></td>
<td><identifier identifiers="data.security.identifiers" render-type="cusip"></identifier></td>
<td>{{data.date}}</td>
</tr>
</table>
The plunker:
https://plnkr.co/edit/6VJuQob1jfDzsR8XPKKM?p=preview
Hope this helps.

Related

Iterate over an array of objects with object having multiple properties

I'm trying to iterate over the following:
state = {
products : [
{
x : "sd",
y : "fdg"
}, {
x : "sdx",
y : "fdgx"
}
]
}
I need to iterate over the above products array and inside object to create:
<tr><td>sd</td><td>fdg</td></tr>
I tried using :
{
this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
})
}
but get multiple errors and prod being undefined.
It's possible that logic elsewhere in your component is mutating the state, which in turn may be the root cause of the error thrown during rendering.
Be sure to check that the products array is consistently defined in your components state, and that the items in that array are defined.
One solution here might be to take a more defensive approach to rendering your table row elements, by doing the following:
{
Array.isArray(this.state.products) && this.state.products
.filter(product => !!product)
.map(product => {
return <tr><td>{product.x}</td><td>{product.y}</td></tr>;
})
}
Here, the rendering logic asserts that this.state.products is the expected array type via Array.isArray(). Addtionally, the logic ensures any prop being rendered is defined by first filtering out any undefined prop items via this line:
filter(product => !!product)
Hope that helps!
The problem is that the return statement is an HTML code which is causing the problem whereas you can encode the code into a string and the DOM will treat it as HTML code
this.state.products.map(function(prod){ return "<tr><td>"+prod.x+"</td><td>"+prod.y +"</td> </tr>" }).
you need to add that in one variable return as below:
const prod = this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
});
Use the variable inside render lifecycle as below.
{prod}
Here is the working code attached in jsFiddle
Hope this helps!

Use Lodash to find the indexOf a JSON array inside of an [] array

I have an array that looks something like this.
Users : {
0 : { BidderBadge: "somestuff", Bidders: 6, }
1 : { BidderBadge: "somemorestuff", Bidders: 7,}
}
I want to search the array using lodash to find a value inside of each of the user objects.
Specifically, I want to use values from another similar array of objects to find the value.
var bidArray = [];
_.each(this.vue.AllUsers, function(user) {
_.each(this.vue.Bids, function(bid) {
if(user.BidderBadge == bid.Badge) {
bidArray.push(user);
}
});
});
This is what I have and it works, but I want to do it using only one loop instead of two. I want to use something like _.indexOf. Is that possible?
If you want to avoid nesting, you just have to modify Azamantes' solution a bit
var bidders = this.vue.Bids.reduce(function(acc, bid) {
return acc[bid.BidderBadge] = true;
}, {});
var bidArray = this.vue.AllBidders.filter(function(bidder) {
return !!bidders[bidder.Badge];
});
It is difficult to give an accurate answer with an example that doesn't coincide with the input that your provide.
Anyway, supposing your data structures were more or less like this ones, you could solve the problem with lodash _.intersectionWith.
Intersect both arrays using a comparator that checks the correct object properties. Also, take into account that users must go first in the intersection due to the fact that you're interested in its values.
function comparator(user, bid) {
return user.BidderBadge === bid.Badge;
}
console.log(_.intersectionWith(users, bids, comparator));
Here's the fiddle.

Knockoutjs css binding not working

(Using KnockoutJs 2.0.0)
I have a list of phone numbers in my viewmodel. Each phone number has a type (home, work, mobile, etc). I want to display an icon (based on a fontawesome class) next to each phone number.
If I hardcode the icons in the css binding, everything works:
<tbody data-binding="foreach: phoneList">
<tr>
<td><span data-bind="css: {'icon-home' : TypeId() == 1, 'icon-building': TypeId() == 2, ... , 'icon-phone': TypeId() >= 7></span></td>
...
</tbody>
I wanted to replace the hardcoded list with a call to a function. I initially tried adding the function to the parent but had no success, so then I tried adding the function directly to the phone object itself both as a function and as a ko.computed() -- but neither of these work for me.
I've dummied up some code here that demonstrates the problem. If you inspect the span element of the table items, you'll see that it almost appears as if the data-biding is treating the returned string as an array of characters and setting the class based on indexes rather than treating the returned string as a class.
I'm sure this is something completely obvious, but I've been beating my head to no avail.
A computed observable should work just fine. The problem is what what you're returning from that computed observable. You need to return the definition of classes in the same format as the hard-coded version:
me.getClass = ko.computed(function() {
return me.typeId() == 1 ? { 'mobile': true } : { 'business': true };
});
See the updated version here: http://plnkr.co/edit/qDjgMlZpXHjn5ixY3OCt
Or, you could define a custom binding to clean up the computed function a bit, though it should be noted that in this case all classes will be replaced by the output of the binding. This is probably not necessary in Knockout 3.0.0, as alluded to in the comments and other answers.
Binding:
ko.bindingHandlers.setClass = {
update: function(element, valueAccessor, allBindings) {
var value = ko.utils.unwrapObservable(valueAccessor());
element.className = value;
}
};
Observable:
me.setClass = ko.computed(function() {
return me.typeId() == 1 ? "mobile" : "business";
});
HTML:
<td data-bind="setClass: setClass, text: typeId"></td>
A version using a custom binding is here: http://plnkr.co/edit/ryaA4mIf7oh5Biu8bKj0?p=info
Fix
Example
I updated your version of KO to 3.0.
Next, I changed your ko.computed binding for getClass from:
me.getClass = ko.computed(function() { return me.typeId == 1 ? "mobile" : "business"; });
to:
me.getClass = ko.computed(function() { return this.typeId() == 1 ? "mobile" : "business"; }, me);
Note
There may be a way to do this with KO 2.0, but I couldn't find documentation for previous versions. I imagine the issue is related to syntax if the feature exists.
An alternate way to do this is use an attr data-bind, instead of using a custom binding handler to set the class on the element.
So, you would still need to use a computed to set the observable:
me.setClass = ko.computed(function() {
return me.typeId() === 1 ? "mobile" : "business";
});
Then use an attr binding to set the class on the html element:
<td data-bind="attr: { class: setClass }, text: typeId"></td>

Substring a JSON object value in an ng-repeat

I was wondering what would be the best way to accomplish getting a substring of a value from a JSON object that is being spit out in an ng-repeat. Currently I have:
<tr data-ng-repeat=" item in records | orderBy : '-score' | limitTo : 10 " ng-click="moreInfo(item)">
<td>{{$index+1}}</td>
<td>{{item.name}}</td>
<td>{{item.score}}</td>
</tr>
And what is displayed on the page is user-SmithJoe. I was wondering if it is possible in the html to do something like {{item.name.substring(5,item.name.length())}}. I am sure that doesnt work, but hopefully you understand what I am trying to accomplish from that. I ultimately just want SmithJoe to be the output.
This is the js:
data2.forEach(function(r) {
if (r && r.user && r.user.toLowerCase().indexOf($scope.searchText.toLowerCase()) !== -1) {
$scope.records.push(r);
}
});
It the for each part is just going through all of the returned json objects and adding them to records[] if they contain what I am looking for. So since I did it this way I didn't really see there any way to take a substring # that point?
Thanks.
Add a property to the object when you iterate:
data2.forEach(function(r) {
if (r && r.user && r.user.toLowerCase().indexOf($scope.searchText.toLowerCase()) !== -1) {
r.prettyName = r.name.replace("user-", "");
$scope.records.push(r);
}
});
And output that name:
<td>{{item.prettyName}}</td>
{{item.name.slice(5)}}
should works :)

AngularJS Custom Filter orderBy sortPredicate not working

The problem is that I have an array of recipe objects. Each recipe object has some comments on it. I want to sort the array in angularJS controller using the $filter service provided by angular.
$scope.recipes = $filter('orderBy')($scope.data, function(recipe) {
return recipe.comments.length;
});
But its not giving the required results. However, I'm able to achieve the desired results using the JS array sort functionality like this
$scope.data.sort(function(a, b) {
if (a.comments.length < b.comments.length) return 1;
if (b.comments.length < a.comments.length) return -1;
return 0;
});
The Plunkr for the same scenario is : http://plnkr.co/edit/L9Bt67xHRCJLBoWG8EZp?p=preview
Thanks in advance. Please Help!
It can be done a lot simpler using orderBy
http://plnkr.co/edit/B0fMi7FotgmG2tkCjySt?p=preview
<ul>
<li ng-repeat="r in recipes | orderBy:'-comments.length'">
{{r.title}} - {{r.comments.length}}
</li>
</ul>
Added this as another answer, since you want to manage it in your controller and you want the reverse, add true as the final arg in $filter
Documentation
$filter('orderBy')(array, expression, reverse)
Example
$scope.recipes = $filter('orderBy')($scope.data, function(recipe) {
return recipe.comments.length;
}, true);
I'm pretty sure you can also set the reverse to a var in scope if you wish.
$scope.recipes = $filter('orderBy')($scope.data, "comments.length", true)
The filter expects an expression, not a function.

Categories

Resources