Postfix a comma if angularJS expression is not null - angularjs

I need to display an address on the View. Something like
123 Street, 12 Apartment, 1 Floor, Richardson, Texas, 75081
User may skip entering the value of Floor for instance, and in that case I'll have to remove the comma visible after floor. Need something like -
{{streetAddress + ", "}}{{apartment + ", "}}{{floor + ", "}}{{city + ","}}{{state + ","}}{{zipCode}}
As in comma should be displayed only that expression has some value and is not null.
In the above statement if Floor is empty, it would display - Street, null, Apartment...
I am trying to avoid tedious writing like
<span ng-show="streetAddress">{{streetAddress}}, </span>
<span ng-show="floor">{{floor}}, </span>
and so on..

If the case of needing the comma is just isolated to this address the controller you should be enough. However, if you could think of a case where you would want to share this functionality throughout your app I would suggest a filter to handle adding the comma.
angular.module('yourModule', []).filter('commaFilter', function() {
return function(input) {
return input ? input + 'j' : '';
};
});
used like
{{ streetAddress | commaFilter }}

Do it in the controller:
var fullAddress = [],
components = [fullAddress,apartment,floor,city,state,zipCode];
// ES5
components.forEach(function(item) {
if (item) {
fullAddress.push(item);
}
});
// pre-ES5
for (i=0;i<components.length;i++) {
if (components[i]) {
fullAddress.push(components[i]);
}
}
$scope.fullAddress = fullAddress.join(",");
And then your template is easily:
<span>{{fullAddress}}</span>

Related

Order by Date AngularJS (Ionic

I'm trying to show an accordion list ordered by date. This date is in JSON format, which is not friendly to the user. However, if I format it the way I want to, I can no longer use it to order the list.
.controller('WarningsCtrl', function ($scope, HttpService) {
HttpService.getWarnings()
.then(function (res) {
for (var i = 0; i < res.length; i++){
//converts json date (2017-01-25T16:10:45) in simple date (25-01-2017)
//inicioArray = res[i].inicio.split("-");
//inicioArray[2] = inicioArray[2].substring(0, inicioArray[2].indexOf("T"));
//res[i].inicio = inicioArray[2] + "-" + inicioArray[1] + "-" + inicioArray[0];
}
$scope.warnings = res;
}, function (err) {
console.log("Error. " + err);
});
console.log("Warnings: " + $scope.warnings);
})
<ion-item-accordion class="item item-text-wrap" title="{{warning.titulo}}" date="{{warning.inicio}}" icon-align="right" icon-close="ion-chevron-right" icon-open="ion-chevron-down" style="font-size:10px!important; color:red;" ng-repeat="warning in warnings | orderBy:'-inicio'" >
<div style="text-align:center" ng-if="warning.imagem != null"><img ng-src="http://www.predio24.com/{{warning.imagem}}" style="width:100%; height:auto" /></div><br />
<div ng-bind-html="warning.corpo | unsafe"></div>
</ion-item-accordion>
If I uncomment the JSON date conversion, the orderby will not work, because the order of numbers is not good for ordering. How can I keep the formated date, while ordering using the original date?
Have you tried using an Angular filter for the formatting of the date when supplying it to the Accordion directive? Your commented code above would probably be a filter function like below (assuming I've interpreted your function right):
.filter('formatDate', function() {
return function(rawDate) {
//converts json date (2017-01-25T16:10:45) in simple date (25-01-2017)
inicioArray = rawDate.split("-");
inicioArray[2] = inicioArray[2].substring(0, inicioArray[2].indexOf("T"));
return inicioArray[2] + "-" + inicioArray[1] + "-" + inicioArray[0];
}
});
Then in your accordion markup, date will be interpolated as:
date="{{warning.inicio | formatDate}}"
This should leave the date alone for other operations (like the orderBy) while supplying a formatted version to the directive for display...
I have not explored angularJS filters too much, so the solution to this problem was simpler than I thought.
I left the controller without any relevant code
.controller('WarningsCtrl', function ($scope, HttpService) {
HttpService.getWarnings()
.then(function (res) {
$scope.warnings = res;
}, function (err) {
console.log("Error. " + err);
});
console.log("Warnings: " + $scope.warnings);
})
And used a filter in my view:
date="{{warning.inicio | date: 'dd/MM/yyyy' }}"
So:
<ion-item-accordion class="item item-text-wrap" title="{{warning.titulo}}" date="{{warning.inicio | date: 'dd/MM/yyyy' }}" icon-align="right" icon-close="ion-chevron-right" icon-open="ion-chevron-down" style="font-size:10px!important; color:red;" ng-repeat="warning in warnings | orderBy:'-inicio'">

Can I repeat only in filter but keeping it to one element?

Hi I am wondering if I can use ng-repeat over an array but keeping it to 1 element but to show the repeated objects inside an attribute instead.
This is my current code:
<span ng-if='locations.length >= 2'>Several places</span>
<span ng-if='locations.length <= 1' ng-repeat="location in locations">{{location.city}}</span>
What I want to do now is if the locations.length is 2 or more elements I want this to show instead:
<span title='New York, Dallas, Los Angeles'>Several places</span>
I want my locations to repeat only inside the title attribute.
Now when I do like this:
<span ng-if='locations.length >= 2' ng-repeat="location in locations" title="{{location.city + ', '}}">Several pl..</span>
I get the following code which isnt wrong because that is what I've coded:
<span title="New York ,">Several places</span>
<span title="Dallas ,">Several places</span>
<span title="Los Angeles ,">Several places</span>
But it is not what I want my code to do, but I cannot really find how to do what I want to do. How can I achieve that my items in locations gets repeated only inside the title attribute?
Define a method in your controller that concatenates all city names as below
$scope.concatAllCityNames = function(locations) {
var allCityNames = "";
angular.forEach(locations, function(location ,index){
allCityNames = allCityNames + location.city;
if(index < locations.length-1) {
allCityNames = allCityNames + ", ";
}
});
return allCityNames;
}
call this method using AngularJS expression from title attribute as below
<span ng-if='locations.length >= 2' title="{{concatAllCityNames(locations)}}" >Several places</span>
Alternatively you can concatenate all city names in a $scope variable while retrieving locations and access it from title attribute as below
<span ng-if='locations.length >= 2' title="{{allCityNames}}" >Several places</span>
I think the best way to achieve what you want is to create a variable in the controller (since you didn't say anything about locations I made an assumption:
var locations = [
{city: 'New York'},
{city: 'Dallas'},
{city: 'Los Angeles'}
]
$scope.locationsTitle = locations.map(obj => obj.city).toString(); // 'New York,Dallas,Los Angeles'
Now you can use locationsTitle in the view to have what you asked :)
Just to give a variety of answers, you could also achieve what you're after with a directive. This example will make it flexible so that you can pass any collection of objects to the directive and specify which property on the objects to use for the title.
directive
.directive('elementWithTitle', function(){
return {
restrict: 'A',
scope: {
titleItems: '=',
titleItemProperty: '#'
},
link: function(scope, element, attrs) {
var title = '';
angular.forEach(scope.titleItems, function(item){
title += item[scope.titleItemProperty] + ', ';
});
// strip off last ,
title = title.substring(0, title.length - 2);
element.attr('title', title);
}
}
})
html
<span ng-if="locations.length >= 2" element-with-title title-items="locations" title-item-property="city">Several Places</span>

How to sort results after I filtered them with angular

I have a table and each row has a column with an amount of money. That amount can be in different currency. For now I have two different currencies, for example euros and dollars.
In order to sort that table by amount of money (low-to-high or reverse) I should first convert the amount in dollars for example and then sort the table.
So, I have an order function that works well reference : https://docs.angularjs.org/api/ng/filter/orderBy
I created a filter 'currency' that converts the amount from euros to dollars (i have this as default). The currency converter works good.
But, when I click the button for ordering, I see the results with the converted currency but the table is ordered with the numeric value of the first results.
ng-click="changeCurrencyToDollars(); order('bonus_amount');"
For example the initial data is :
10 US Dollar
9 Euros
and it is converted to :
US Dollar
11.14 US Dollar
Any ideas why the sorting is not working on the converted currency (filtered results) ?
Thanks
Controller:
$scope.convertedCurrency = false; //initial table data with mixed currencies
$scope.changeCurrencyToDollars = function (){
$scope.convertedCurrency = $scope.convertedCurrency ? false: true;
};
$scope.order = function(predicate){
$scope.predicate = predicate;
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.operators = orderBy($scope.operators, predicate, $scope.reverse);
};
app.filter('currency', [function() {
var defaultCurrency = 'Dollars';
return function(input, currencySymbol){
var out = "";
currencySymbol = currencySymbol || defaultCurrency;
switch (currencySymbol){
case 'Dollars':
out = input;
break;
case 'EUR':
out = 1.11 * input; // convert to dollars
currencySymbol = defaultCurrency;
break;
default:
out = input;
}
return out.toFixed(0) + ' ' + currencySymbol;
}
}]);
View:
Inside the ng-repeat:
<span class="highlight-word" ng-if="!convertedCurrency">{{operators.bonus_amount}} {{operators.bonus_currency}}</span>
<span class="highlight-word" ng-if="convertedCurrency">{{operators.bonus_amount | currency: operators.bonus_currency}}</span>
How I solved it:
I inserted a new property to every object with the converted value. That way every object had a "dollars" property. And for sorting I used the same $scope.order, since it was working but with the new property of the object. Not the best angular solution, but at least the "weight" was in the controller and not in the view.
you have not given your object but ,let's say that is it like this:
$scope.operator = [{
bonus_amount:'100',
bonus_currency:'Dollars'},
{
bonus_amount:'10',
bonus_currency:'Dollars'},
{
bonus_amount:'150',
bonus_currency:'Dollars'}];
Now let's add the currency filter(you put your custom filter to make the conversion) and the orderby filter, and show the data:
In your ng-repeat you add the 'orderBy' filter and then, when you show the data you add the 'currency' filter:
<li ng-repeat="operators in operator | orderBy:'+bonus_amount'">{{operators.bonus_amount | currency }}</li>
Hope helps, good luck.

how to pass an argument to angularjs custom filter

i want to be able to pass different price point to the filter but the way it defined right now it not working unless i hard code it in eg priceArray.push(value.price)
var priceSumUp = function ($log){
return function(arrayObj, nameValue){
var priceArray = [],
totalPrice = 0;
function sumUp(first, second){return first + second}
ng.forEach(arrayObj, function(value, key, obj){
priceArray.push(value.price);
});
totalPrice = priceArray.reduce(sumUp, 0);
//console.log(priceArray);
return totalPrice;
};
}
<div class="" ng-bind-html="vm.getisSelected | priceSumUp:price"></div>
I think what you are looking for is this...
First make sure you are passing the right argument. Either change it to
$scope.price = 'price'
// or
<div ng-bind="items | priceSumUp:'price'"></div>
Next you can use the array-like ECMA object properties to access the item...
return function(arrayObj, v){
var priceArray = arrayObj.map(function(obj){
return obj[v]
})
...
}
See my plunker here this would also allow for say...
<div ng-bind="items | priceSumUp:'tax'"></div>

AngularJS ng-repeat, comma separated with 'and' before the last item

I have a list of items ...
$scope.Users =[{
UserName: ''
}];
In my view I want to list them like this assuming I have only 4 items in my $scope:Users
Username1, Username2, Username3 and Username4
<span data-ng-repeat="user in Users">{{user.Username}}</span>{{$last ? '' : ', '}}
The above expression will basically add comma after each item and works fine.
My problem is how do I add an and keyword before the last item so it will be like:
Username1, Username2, Username3 and Username4
instead of:
Username1, Username2, Username3, Username4
$last is the truthy value.. so it holds either true or false and it doesn't hold the last element index..
I guess below expression should solve your problem
<p><span ng-repeat="user in Users">
{{user.Username}} {{$last ? '' : ($index==Users.length-2) ? ' and ' : ', '}}
</span></p>
Also make sure that you have the expression with $last within ng-repeat element and not outside of it
Please check the below working fiddle
http://jsfiddle.net/hrishi1183/Sek8F/2/
This could be one of the solutions
<span data-ng-repeat="user in Users">{{user.Username}}<font ng-show="!$last">,</font></span>
<span ng-repeat="user in Users">{{$first ? '' : $last ? ' and ' : ', '}}{{user.Username}}</span>
Instead of appending something prepend it. You can use $first to omit the first one. You can then use $last to add "and" instead of a comma.
Use a comma before and to promote clarity!
Bill Gates donates 6 million to Username1, Username2 and Username3.
vs
Bill Gates donates 6 million to Username1, Username2, and Username3.
Without the serial comma, the sentence does not clearly indicate that each of the users is to be given an equal share.
<span ng-repeat="user in Users">
{{user.Username}}{{$last ? '' : ($index==Username.length-2) ? ', and ' : ', '}}
</span>
Outputs:
Username1, Username2, Username3, and Username4
If you just need the text output, use a custom filter instead of ng-repeat:
<span>{{Users | humanizedUserList}}</span>
the filter code being something like this (using Lodash):
app.filter('humanizedUserList', function() {
return function(users) {
var last_users = _.last(users, 2);
return _.reduce(users, function(out, user, idx, users) {
out += user.UserName;
if(user === last_users[1]) { // last entry
return out;
}
else if(user === last_users[0]) { // second to last entry
return out + ', and ';
}
else {
return out + ', ';
}
});
};
}
You'd save yourself the hackish use of $last outside of the ng-repeat and ternary operators - and add reusability for other parts of your application.
I ran into this problem today, but with an extra challenge that the list items may need to be formatted within the sentence (for example, bold items or links). So just outputting a string from a filter wouldn't work. Initially I tried using ng-bind-html and outputting HTML from the filter, but that wasn't flexible enough.
I ended up making a directive that can be used within any ng-repeat to add the separator between list items. The tricky part is I only had access to $first, $last, and $index (not the original array or its length!).
My solution:
var app = angular.module('app', []);
app.directive('listSeparator', function() {
return {
replace: true,
template: '<span>{{ separator }}</span>',
link: function(scope, elem, attrs) {
scope.$watchGroup(["$index", "$first", "$last"], function() {
if (scope.$first)
scope.separator = "";
else if (scope.$last && scope.$index == 1)
scope.separator = " and ";
else if (scope.$last)
scope.separator = ", and ";
else
scope.separator = ",";
})
}
}
})
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.10/angular.min.js"></script>
<div ng-app="app">
Click a button to see the formatted list.
<button ng-click="myArray = ['one']">One item</button>
<button ng-click="myArray = ['one','two']">Two items</button>
<button ng-click="myArray = ['one','two','three']">Three items</button><br>
<span ng-repeat="item in myArray"><list-separator></list-separator>
<strong>{{item}}</strong></span>
</div>
Enjoy!

Resources