angularjs ng-class with different conditions - javascript

i have a view which shows data and i want to add another class on the list items in my view.
<input type="text" class="filter" placeholder="search..." ng-model="search">
<ul>
<li ng-repeat="item in items | filter:search | orderBy:'date'">
{{ date }} {{ item.heading }}<button ng-click="deleteItem(item.ID)"><i class='icon-trash'></i></button>
</li>
</ul>
i have a variables called item.date and i want to compare it to another variables today. Here is the logic:
if (item.date - today <= 0)
apply class1
if else (item.date - today > 0 && item.date - today <= 3)
apply class2
and so on
how can i achieve this with angular?
can i put this logic directly into my view or do i need to define it in my controller?
thanks in advance

Since you have a bit heavy comparisons to make, I would suggest moving it inside a function rather than having it in HTML:
<li ng-class="getClass(item.date)"
ng-repeat="item in items | filter:search | orderBy:'date'">
JS:
$scope.getClass = function(date){
/* comparison logic here*/
/* returs class name as string */
}

I think you can use ng-class like this:
HTML
<li ng-class="{'class1': item.date - today <= 0, 'class2': (item.date - today > 0 && item.date - today <= 3)}"></li>
OR moving it inside a function like this:
HTML
<li ng-class="getClass(item.date)"></li>
JS
$scope.getClass = function(date){
return {'class1': item.date - today <= 0, 'class2': (item.date - today > 0 && item.date - today <= 3)};
}

Related

Angularjs: change object property of specific index of nested ng-repeat

I've nested ng-repeat like this:
<div ng-repeat="(dayOfWeek, time) in uiHoursOfOperation track by $index" ng-init="daysOfWeekIndex=$index">
<div class="" ng-repeat="openCloseSet in time.uiDayMs track by $index">
<md-input-container>
<md-select ng-model="openCloseSet[0]" ng-init="openTime = openCloseSet[0]">
<md-option ng-repeat="uiTime in time.avaiableTime" ng-value="uiTime.msValue">
{{::uiTime.display}}
</md-option>
</md-select>
</md-input-container>
<md-input-container ng-if="$index === 0">
<md-button ng-click="addSplitHours(time.uiDayMs ,$index +1, time)">
Add
</md-button>
</md-input-container>
</div>
</div>
On clicking the Add button. It creates a new block of time.uiDayMs.
Here is the add function:
$scope.addSplitHours = addSplitHours;
function addSplitHours(index, time) {
var newOpen = time.uiDayMs[time.uiDayMs.length - 1][1] + 900000;
time.uiDayMs.push([newOpen, newOpen + 900000 * 4]);
var closingtimeStarts = closingTime + 900000;
var closingtimeEnds = avaiableClosingTimes[avaiableClosingTimes.length - 1].msValue;
for (
var msValue = closingtimeStarts;
msValue <= closingtimeEnds;
msValue += 900000
) {
time.avaiableTime.push({
msValue: msValue,
display: moment(msValue)
.utc()
.format("h:mm A")
});
}
}
You can see I'm taking the next index number but unable to use it.
So my main question is how can I make the time.avaiableTime.push to only that specific index? Currently, It changes on each time.avaiableTime but I wanted to push on that specific index only.

Angular Js filter does not work with javascript addition operator

I was playing with Angular Js when I came across following issue
<li ng-repeat="x in capitals | orderBy:'capital'">
{{ x.country|uppercase}} : {{ x.capital|uppercase}}
</li>
works but
<li ng-repeat="x in capitals | orderBy:'capital'">
{{ x.country|uppercase+ ":" + x.capital|uppercase}}
</li>
dose't work
Is there any work around?
That wont work as you cant use addition operator inside expression. you can write your custom filter and use that. check the below fiddle.
jsfiddle.net/7nzot7jh/
Found the Answer
This is my custom filter
app.filter('alterCap',function()
{
return function(x)
{
var i, c, txt = "";
for (i = 0; i < x.length; i++) {
c = x[i];
if (i % 2 == 0) {
c = c.toUpperCase();
}
txt += c;
}
return txt;
};
});
And in my controller I have passed
app.controller("myCtrl",function($scope,$filter){
$scope.capitals = [
{country:'India',capital:'Delhi'},
{country:'Sri Lanka',capital:'Colombo'},
{country:'Afganistan',capital:'Kabul'},
{country:'Bhutan',capital:'Norway'},
{country:'Nepal',capital:'Thimphu'},
{country:'Japan',capital:'Tokyo'},
{country:'China',capital:'Beijing'},
{country:'Russia',capital:'Moscow'},
{country:'USA',capital:'Washington, D.C.'}
];
$scope.applyAlterCap = function(movie){
return $filter('alterCap')(movie);
};
});
Now I can use Filter in my expression as
<p>Using Custom Filter with addition operator inside expression</p>
<ul>
<li ng-repeat="x in capitals | orderBy:'capital'">
{{ applyAlterCap(x.country) + ":" + x.capital }}
</li>
</ul>
{{ x.country + ":" + x.capital}}

Angular2: *ngFor, AsyncPipe, and index

In a previous question here, I am trying to show a date only when it has changed between 2 messages.
The difference is that I am now connected to a realtime database (Firebase), so I subscribe to the data source and pass it through the async pipe before going through index and *ngIf:
<div *ngFor='let message of (chat | async) ; let i = index'>
<button *ngIf="i == 0 || (message.timestamp | date: 'ddMMMMyyyy') != ((chat | async)[i-1].timestamp | date: 'ddMMMMyyyy')">
{{ message.timestamp | date:'ddMMM' }}
</button>
...
<!--More html elements below (omitted)-->
...
</div>
This works well when I first load the view, however, if I push a new entry into chat, I get the following error:
TypeError: Cannot read property '0' of null
Maybe I am not too sure how the async pipe works, but when I try returning {{ (chat | async).length }}, it works as intended. Any suggestions on a workaround/proper practice?
In case someone else finds this since it's the first result with the key words, I was able to get the index of a *ngFor with the async pipe like this (assuming messages is an Observable of an iterable):
<div *ngFor="let message of (messages | async); let i = index;">
Still not sure how the AsyncPipe can be manipulated here, but I did mange to find a workaround that involves not using the pipe. I will await a better answer hopefully before marking this closed.
I subscribe to the data source in my class, and manipulate the array with a for loop before displaying it.
The (template) code in my question has now become:
<div *ngFor='let message of messages; let i = index'>
<button *ngIf="message.isNewDay">
{{ message.timestamp | date:'ddMMM' }}
</button>
...
<!--More html elements below (omitted)-->
...
</div>
And in the controller:
private chatid: string //chat id passed from previous screen
private chat: FirebaseListObservable<any>;
private messages: any = [];
constructor(private firebase: FirebaseService,
private _datepipe: DatePipe) {
}
ionViewLoaded() {
this.chat = this.firebase.database.list('/chat-messages/' + this.chatid);
this.chat.subscribe((data) => {
this.messages = data;
for (let i: number = 0; i < this.messages.length; i++) {
if (i === 0)
this.messages[i].isNewDay = true;
else {
let date1 = this._datepipe.transform(this.messages[i].timestamp, 'ddMMMMyyyy');
let date2 = this._datepipe.transform(this.messages[i-1].timestamp, 'ddMMMMyyyy');
if (date1 !== date2)
this.messages[i].isNewDay = true;
else
this.messages[i].isNewDay = false;
}
}
});
}
Note that I am currently using DatePipe in the class code (as well as in the template), so it is necessary to use providers: [DatePipe] along with pipes: [DatePipe].
If you want to use *ngFor, AsyncPipe, and index together - you should use this construction:
<ul>
<li *ngFor="let person of obsPersons | async as persons; index as i">
{{i + 1}} out of {{persons.length}}: {{person.personId}}
</li>
</ul>
Tested and works on Angular 7.
As mentioned above by #Primal Zed let company of (filteredCompaniesAliases | async); let i = index" should do the trick, syntax sample with plain html table:
<table>
<tr *ngFor="let company of (filteredCompaniesAliases | async); let i = index"">
<td>
{{selectedDiscountableCharge.name}}
</td>
<td>
${{selectedDiscountableCharge.amount}}
</td>
<td>
${{selectedDiscountableCharge.amount - ( (selectedDiscountableChargePercentanges / 100) * selectedDiscountableCharge.amount ) }}
</td>
</tr>
</table>
in your component:
companyAliasFilterCtrl: FormControl;
filteredCompaniesAliases: Observable<any[]>;
...
this.filteredCompaniesAliases = this.companyAliasFilterCtrl.valueChanges
.pipe(
startWith(''),
map(company => company ? this.filterOperatorsAliases(company) : this.companies.slice())
);
I use a function for showing (or not) the date. So to show the date (in this example) only when it changes:
<ion-list no-lines>
<ion-item-sliding *ngFor="let trip of tripsExt$ | async; let i = index">
<ion-item>
<ion-row (click)="change(trip)">
<ion-col col-6>
{{showOnChange(trip.date)}}
</ion-col>
<ion-col col-6>
{{trip.end_address_name}}
</ion-col>
</ion-row>
</ion-item>
<ion-item-options side="left">
<button ion-button color="primary" (click)="delete(trip)">
Delete
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
With showOnChange:
showOnChange(currentDate) {
if (currentDate != this.previousDate) {
this.previousDate = currentDate
return currentDate
}
return ''
}

AngularJS custom orderBy from the same variable

In my view I am doing this:
<div ng-repeat="item in items">
{{item.some_key}}
</div>
some_key variable has only 3 type of string output: "unsafe", "elevated" and "trace". I want to order output of items in this order: first all items which have unsafe, then "elevated" and at the end "trace" ... I can't do this in a simple orderBy filter ... How to achieve this?
Thanks for a help in advance!
Make a custom orderBy
<div ng-repeat="item in items | orderBy: statusEvent">
{{item.some_key}}
</div>
$scope.statusEvent = function(item) {
if (item.some_key == "unsafe") return 0
else if (item.some_key == "elevated") return 1
else if (item.some_key == "trace") return 2
else return 3
}
Use a custom filter
var orders = {
unsafe:0,
elevated:1,
trace:2
}
$scope.someKeyOrder = function(item){
return orders[item.some_key];
}
and use it
ng-repeat="item in items | orderBy: someKeyOrder"
Demo at http://plnkr.co/edit/4Tjsc8?p=preview

transfer logic into controller

please help to fix the script.
http://jsfiddle.net/rrnJc/
this is a dynamic list of news. you can scroll through.
the problem that the logic is in the template:
<li ng-repeat="item in news" ng-show="$index >= currentPosition && $index <= (currentPosition + qntVisibleRecords)">
this is not right.
I would like to chtby visible part of the list of news was in the controller (in the function $scope.newsVisible). at the same conclusion in the template design and deliver this:
<li ng-repeat="item in newsVisible">
<span class="date">{{item.date}}</span>
<span class="title">{{item.title}} - {{$index}}</span>
</li>
Just slice your array of news when controller is initialized and each time you call the changeCurrent function and iterate through newsVisible. Possible code could be :
$scope.changeCurrent = function(value){
$scope.currentPosition = $scope.currentPosition + value;
if($scope.currentPosition < 0 ){
$scope.currentPosition = 0;
}
var end = Math.min($scope.currentPosition + $scope.qntVisibleRecords, $scope.news.length)
//ensure that you won t slice at an index greater than news.length
$scope.newsVisible = $scope.news.slice($scope.currentPosition, end);
}
BTW you can call this function when the controller initialized in order to init your array of newsVisible.
$scope.changeCurrent($scope.currentPosition);
You can check it here http://jsfiddle.net/rrnJc/1/

Categories