Order by Date AngularJS (Ionic - angularjs

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'">

Related

Formating MYSQL datetime in Angular doesn't work in Safari browser

I want to format MYSQL datetime (eg. 2017-02-07 22:58:22), so I've found, that it's necessary to convert it to ISO format with this filter first:
angular.module('datePipe',[]).filter('dateToISO', function() {
return function(input) {
input = new Date(input).toISOString();
return input;
};
});
This works in chrome and also in firefox, but not in safari :( There it throws error:
Error: Invalid Date toISOString#[native code]
How can I fix this error? Thank you :)
I found the solution:
html
<div class="date-left">{{ newie.created_at | dateParser | date:'d. M. yyyy' }}</div>
and here's dateParser
angular.module('datePipe',[]).filter('dateParser', function() {
return function(input) {
var year = input.substr(0,4);
var month = input.substr(5,2);
var day = input.substr(8,2);
return year + "-" + month + "-" + day;
};
})
I just wanted to display date only :)

Ionic collection-repeat with date dividers

I got a very large list of about 200 items with text and images. ng-repeat is way to slow to render this smoothly. It tried it with this solution. Works nice. But not with collection-repeat.
My web-service return this:
There are events with specific dates. The events should be grouped by date. So in order to use collection repeat, how is it possible to insert dividers, if you cant use angular.filter groupBy?
I can offer you a partial solution which would only work if the dataset is ordered by the displayed field in the divider.
First of all we need to create a fake element in the array so that we can discriminate the divider amongst the other element.
Let's say we have a collection of posts fetched from a webservice:
.controller('mainController', function($scope, dataService) {
$scope.posts = [];
var divider = '';
});
the private field divider will be in use when we load the posts.
And we will have the loadMore method to load extra data when we scroll the list:
$scope.loadMore = function(argument) {
page++;
dataService.GetPosts(page, pageSize)
.then(function(result) {
if (result.data.length > 0) {
angular.forEach(result.data, function(value, key) {
value.divider = false;
if (value.postId !== divider)
{
divider = value.postId;
$scope.posts.push({divider: true, dividerText: value.postId});
}
$scope.posts.push(value);
});
}
else {
$scope.theEnd = true;
}
})
.finally(function() {
$scope.$broadcast("scroll.infiniteScrollComplete");
});
};
When we fetch the data from the web api (and the promise is resolved) we loop through the collection and check if the field is different from the divider. If this is a new divider we store the info and add a new element to the collection:
angular.forEach(result.data, function(value, key) {
value.divider = false;
if (value.postId !== divider)
{
divider = value.postId;
$scope.posts.push({divider: true, dividerText: value.postId});
}
$scope.posts.push(value);
});
As you can see I've added an element:
$scope.posts.push({divider: true, dividerText: value.postId});
I've used a dividerText field which will be displayed later on.
Now we need to create our own directive divider-collection-repeat which should be attached to a collection repeat:
<ion-item collection-repeat="post in posts" item-height="75" divider-collection-repeat>
I guess you're using infinite-scroll, so here is the whole HTML:
<ion-content ng-controller="mainController">
<ion-list>
<ion-item collection-repeat="post in posts" item-height="75" divider-collection-repeat>
{{post.name}}
</ion-item>
</ion-list>
<ion-infinite-scroll ng-if="!theEnd" on-infinite="loadMore()" distance="50%"></ion-infinite-scroll>
</ion-content>
this is the directive:
.directive('dividerCollectionRepeat', function($parse) {
return {
priority: 1001,
compile: compile
};
function compile (element, attr) {
var height = attr.itemHeight || '75';
var itemExpr = attr.collectionRepeat.split(' ').shift();
attr.$set('itemHeight', itemExpr + '.divider ? 40 : (' + height + ')');
attr.$set('ng-class', itemExpr + '.divider ? "item-divider" : ""');
var children = element.children().attr('ng-hide', itemExpr + '.divider');
element.prepend(
'<div ng-show="' + itemExpr + '.divider" class="my-divider" ' +
'ng-bind="' + itemExpr + '.dividerText" style="height:100%;">' +
'</div>'
);
return function postLink(scope, element, attr) {
scope.$watch(itemExpr + '.divider', function(divider) {
element.toggleClass('item-divider', !!divider);
});
};
}
});
The directive prepends an element (html) to the list using the expression you've defined in your collection-repeat.
In my sample I've use collection-repeat="post in posts" so this line:
var itemExpr = attr.collectionRepeat.split(' ').shift();
fetches the item's name; in my case it is going to be post.
We use the height as well cause we might need to have a different height for the divider.
This bit here is the place where all the magic happens:
element.prepend(
'<div ng-show="' + itemExpr + '.divider" class="my-divider" ' +
'ng-bind="' + itemExpr + '.dividerText" style="height:100%;">' +
'</div>'
);
It uses an ng-show for the field 'post.divider' (ng-show="' + itemExpr + '.divider") and binds the our text field ng-bind="' + itemExpr + '.dividerText"
I've also added a custom class my-divider just in case we need to change the layout of our divider a bit.
The final result is here or in this plunker.
As you might have noticed I haven't used a date field as I already had a sample where, sadly, I didn't have any dates.
I guess it should be very easy to adapt to your situation.
The directive is based on a sample I have found on github.
You will find the code for the directive here.

Postfix a comma if angularJS expression is not null

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>

To convert currency from US to UK in AngularJS

I tried this code to display but I need AngularJS to automatically convert currency:
<div ng-controller="ctrl">
default currency symbol ($): {{0.00 | currency}}
custom currency symbol (£): {{0.00 | currency:"£"}}
</div>
<script src="index.js"></script>
<script src="uk-locale.js"></script>
As #Andrey said, you should build your own custom filter to handle the currency conversion.
Here's a simple demo of how I would build such a thing:
angular.module('myModule').filter('currency', function() {
var defaultCurrency = '$';
return function(input, currencySymbol) {
var out = "";
currencySymbol = currencySymbol || defaultCurrency;
switch(currencySymbol) {
case '£':
out = 0.609273137 * input; // google
break;
default:
out = input;
}
return out + ' ' + currencySymbol;
}
});
check the online demo
AngularJs currencyFilter just formats output. If you want actually convert currency, you need to make custom filter, for example.
Here is possible example:
angular.module('myFilter', []).filter('currencyConverter', [function() {
function convert(inputValue, currecyId) {
// Your conversion code goes here
}
return function(inputValue, currencyId) {
return convert(inputValue, currencyId);
}
});

How do I call an Angular.js filter with multiple arguments?

As from the documentation, we can call a filter such as date like this:
{{ myDateInScope | date: 'yyyy-MM-dd' }}
Here date is a filter that takes one argument.
What is the syntax to call filters with more parameters both from templates and from JavaScript code?
In templates, you can separate filter arguments by colons.
{{ yourExpression | yourFilter: arg1:arg2:... }}
From Javascript, you call it as
$filter('yourFilter')(yourExpression, arg1, arg2, ...)
There is actually an example hidden in the orderBy filter docs.
Example:
Let's say you make a filter that can replace things with regular expressions:
myApp.filter("regexReplace", function() { // register new filter
return function(input, searchRegex, replaceRegex) { // filter arguments
return input.replace(RegExp(searchRegex), replaceRegex); // implementation
};
});
Invocation in a template to censor out all digits:
<p>{{ myText | regexReplace: '[0-9]':'X' }}</p>
i mentioned in the below where i have mentioned the custom filter also , how to call these filter which is having two parameters
countryApp.filter('reverse', function() {
return function(input, uppercase) {
var out = '';
for (var i = 0; i < input.length; i++) {
out = input.charAt(i) + out;
}
if (uppercase) {
out = out.toUpperCase();
}
return out;
}
});
and from the html using the template we can call that filter like below
<h1>{{inputString| reverse:true }}</h1>
here if you see , the first parameter is inputString and second parameter is true which is combined with "reverse' using the : symbol
If you want to call your filter inside ng-options the code will be as follows:
ng-options="productSize as ( productSize | sizeWithPrice: product ) for productSize in productSizes track by productSize.id"
where the filter is sizeWithPriceFilter and it has two parameters product and productSize
like this:
var items = $filter('filter')(array, {Column1:false,Column2:'Pending'});
If you need two or more dealings with the filter, is possible to chain them:
{{ value | decimalRound: 2 | currencySimbol: 'U$' }}
// 11.1111 becomes U$ 11.11
In this code, jsondata is our array and in function return we are checking the 'version' present in the jsondata.
var as = $filter('filter')(jsondata, function (n,jsondata){
return n.filter.version==='V.0.3'
});
console.log("name is " + as[0].name+as[0]);

Resources