ngClass not changing on Route Event - html

i have a bottomNavigation that tells in which route we are clicking by changing theyr style.
Here is my template of the bottom-navigation template
class="menu-icon"
[ngClass]="{ 'active-item': buttonActivated.value == '/my-goal'}"
[ngClass]="{ 'active-item': buttonActivated.value == '/my-goal/goal-detail'}"
>
Here is my TS file
buttonActivated: BehaviorSubject<string> = new BehaviorSubject<string>('/')
constructor(private router: Router) {
this.router.events.subscribe(() => {
this.buttonActivated.next(this.router.url);
} }
So the logic is: everytime the route changes or have an event, the buttonActivated.value will change.
And depending on that value the ngClass would change to active-item.
When i click to /my-goal route the ngClass works perfectly, but when i click in his child /my-goal/goal-detail/id and when i come back in the same route, the ngClass is not working as a active-item.
I checked the value of buttonActivated.value and is changing correctly when i click back on the parent route, but the ngClass is still not changing.
Thank you in advance

You can use routerLinkActive directive.
RouterLinkActive directive add the .active class by tracking either the linked route of element is currently active or not, and then allow to specify CSS as per needed.

Related

Is it possible to use [maxSelectedLabels] property in an ngif condition?

I'm using Prime NG Multiselect component and I want to show selectedItemsLabel="{0} Selected" when there are more than 3 selected checkboxes, but if all of the checkboxes are selected, then selectedItemsLabel="All" should be shown in the placeholder.
I'm new to angular and I been following documentation of this MultiSelect component, yet this doesn't show the options to able to implement multiple conditions of properties, and I was wondering if it's even possible.
Example of how It might be
<ng-template pTemplate="filter" let-value let-filter="filterCallback">
<p-multiSelect
[ngModel]="value"
[options]="routeOptions"
placeholder="Any"
(onChange)="filter($event.value)"
optionLabel="name"
selectedItemsLabel="{0} selected"
[maxSelectedLabels]="3"
>
<ng-template let-option pTemplate="item">
<div>
<span class="p-ml-1">{{ option.name }}</span>
</div>
<div *ngIf="[maxSelectedLabels="routeOptions.length - 1"] Then selectedItemsLabel="All"></div>
</ng-template>
</p-multiSelect>
</ng-template>
Yes, you can. First give the component a ref with # like this:
<p-multiSelect
#myMultiSelect
[ngModel]="value"
[options]="routeOptions"
placeholder="Any"
(onChange)="filter($event.value)"
optionLabel="name"
selectedItemsLabel="{0} selected"
[maxSelectedLabels]="3"
>
.......
Then you have access to it:
<div *ngIf="myMultiSelect.maxSelectedLabels === routeOptions.length - 1">Im visible</div>
If the option of maxSelectedLables is the length - 1 of routeOptions then the div is visible. That is how ngIf works
BUT
Thats not what you want. You wanna set the selectedItemsLabel property. And you have it not understand correctly. You set the maxSelectedLables to 3 as example AND set the selectedItemsLabel directly, too! The text of the selectedItemsLabel will be only shown if needed (controlled by the component).
<h5>Basic</h5>
<p-multiSelect #meins [options]="cities" [(ngModel)]="selectedCities" defaultLabel="Select a City" optionLabel="name"
[maxSelectedLabels]="3" selectedItemsLabel="{0} items selected">
</p-multiSelect>
Look here the Stackblitz!
The documentation of ng-prime will helps, too and say:
selectedItemsLabel: Label to display after exceeding max selected labels e.g. ({0} items selected), defaults "ellipsis" keyword to indicate a text-overflow.
UPDATE 18.02.2023
You wanna show "ALL" only if all items selected. So add the onChange event and bind the selectedItemsLabel. Why binding? It has some problems with a condition in it. So we make it inside the code.
HTML
<p-multiSelect [options]="cities" [(ngModel)]="selectedCities" defaultLabel="Select a City" optionLabel="name"
[maxSelectedLabels]="2" [selectedItemsLabel]="bindTest" (onChange)="onChange()">
</p-multiSelect>
Inside the code do the follow with onChange:
Code
onChange() {
if (this.selectedCities.length === this.cities.length) {
this.bindTest = "ALL";
this.changeRef.detectChanges();
}
else {
this.bindTest = "{0} items selected";
this.changeRef.detectChanges();
}
}
Now it works how you wish. One important thing: We use changeRef.detectChanges(); Without this the components selected text will not changing directly. Import it in the components constructor:
constructor(
private countryService: CountryService,
private primengConfig: PrimeNGConfig,
private changeRef: ChangeDetectorRef
) {
.....
I made a Stackblitz of the problem: https://stackblitz.com/edit/primeng-tablefilter-demo-ipt7y1?file=src%2Fapp%2Fapp.component.html,src%2Fapp%2Fapp.component.ts
(Expand the page to the left to view the column filter in the stackblitz)
If you notice, the clear button doesn't clear the selected textbox anymore. After some testing it seems the [(ngModel)] breaks it, I think it got to do something with two-way binding? It is not shown in the stackblitz, but if you include
(onChange)="filter($event.value)"
the clear button still clears the filter from the table, but not in the selected textbox.
I found out that there is this property
[showClear]="true"
That adds an X at the end of the textbox that clears it out. Sadly, the styling/positioning is not what I need.
What could be the ways to fix the clear button ? Add a ts function to clear out the selected values? If so, how to bind it to the clear button because it is generated from
<p-columnFilter
display="menu"
menu property and I had no luck to find the way to try add/change functionality to that button.

Is there any way to add href link for mat-chips component as property or attribute

I am trying to add a new icon and possibly a href on the mat-chip component but I want it as properties and not to hardcode into that.
So the idea is to have it as a component so I can call it from another component and give data.
For example links, and icons are on the left side and right sides.
Icons on left should be an edit icon and the right will be default remove.
But at the hovering, both of them need to be highlighted.
So let's say if from another component we define the left icon then it will be shown and if not then will be not shown.
Or can it be done within a directive too?
I have till now created like this but I think I need more to do on that.
See stackblitz.
Maybe instead of ngIf is any other way to use it.
But I want for example that in the child component to send the index so then I can use it into href.
But in the child, I cannot use the index so I can iterate through the components and use the href for each of them and I want to use this component for different data.
see components in stack blitz.
users and group components.
1 of them has a variable name and the other one has a username.
https://stackblitz.com/edit/angular-3vp4xw-a9jeot?file=src/app/chips-autocomplete-example.ts
What about somethin like that :
You create a component like 'mat-chip-link':
<mat-chip *ngFor="let fruit of fruits" (removed)="remove(fruit)">
<ng-content select="[.beforeLink]"></ng-content>
<a
[href]="link"
class="mat-standard-chip"
target="_blank"
style="margin: 0; padding: 0"
>My link</a
>
<ng-content select="[.afterLink]"></ng-content>
</mat-chip>
(I am not sure about the ng-content selector, check the doc here for more info. https://angular.io/guide/content-projection).
Which has an input like #Input link: string;
Then from the parent you can call this component like that
<mat-chip-link *ngFor="let fruit of fruits" (removed)="remove(fruit)" [link]="test">
<mat-icon matChipRemove class="beforeLink" *ngIf="editable">cancel</mat-icon>
<mat-icon matChipRemove class="afterLink" *ngIf="removable">cancel</mat-icon>
</mat-chip>
It might be done easier, for example (Typescript):
Template of custom component mat-chip-link
<mat-chip-link (click)="this.clickEventHandler($event)"/>
<mat-chip>
<ng-content>
</ng-content>
</mat-chip>
</mat-chip-link>
Component
private _href: string | null = null;
#Input('href') public set href(value: string | null) {
this._href = value;
}
public clickEventHandler(e: MouseEvent) {
if(this._href !== null) {
window.open(this._href, '_blank');
} else {
console.log('No href specifief');
}
}
Usage
<mat-chip-link [href]="https://www.stackoverflow.com">
Jump to StackOverflow 🙌🏼
</mat-chip-link>

Angular material checkbox automatically un-checks itself

I have a list that I display as checkboxes using angular-material (Angular 7). Below I will add code snippet for .html and .ts files.
Whenever I click on a checkbox it is checked but then immediately un-checked. I entered in debug mode and see that when I click on a checkbox, my isSelected() method gets called 4 times by Angular. When I click on it, it immediately goes to checked state. Then it is still checked the second time that Angular calls it. On the third time, it becomes un-checked (meanwhile isSelected() is still true). I cannot figure out what I did wrong. What I tried is:
Switch from isSelected() method to a class property (added the isSelected boolean field on myListItem objects)
Added bidirectional binding on top of the previous idea
Switch from checked to ngModel
Nothing helped. What else to try, I don't know. Please help me out.
html snippet:
class MyListItem {
id: number
name: string
}
// omitted annotations
export class MyComponent implements OnInit, OnDestroy {
myList: MyListItem[] = [] // omitted initialization
isSelected(myListItem: MyListItem): boolean {
return this.myList.includes(myListItem)
}
toggle(myListItem: MyListItem): void {
// omitted the code, I debugged it and it works correctly:
// it adds/removes the item to/from the list
}
}
<mat-list>
<mat-list-item *ngFor="let myListItem of myList">
<mat-checkbox flex="100" (click)="toggle(myListItem)"
[checked]="isSelected(myListItem)">
{{ myListItem.name }}
</mat-checkbox>
</mat-list-item>
</mat-list>
Use change event not click:
<mat-checkbox flex="100" (change)="toggle(myListItem)"
[checked]="isSelected(myListItem)">
{{ myListItem.name }}
</mat-checkbox>
I am not sure if this will work but you can add an Event parameter to the toggle function.
toggle(myListItem: MyListItem, event: any) { event.preventDefault() }
Then in your html:
(click)="toggle(myListItem, $event)"
Again, Not sure if this will work, but I have found that sometimes these click events will happen automatically, unless the prevent default() function is called

Disable showing links of <a> element on hover

When I hover over on any website's a element, I get a link in left bottom corner. For example, when I move cursor on Stackoverflow's logo I get Stackoverflow's URL in corner:
Is it possible to disable this URL in the corner using css / html? I am using Angular 5 in project so if there is an Angular feature that does, please let me know. Thanks for answers.
The preview is rendered by the browser and you can't control it. The only solution would be to use another tag with a similar style and functionality, for example:
<span class="link" onclick="window.open('http://website.com','_blank');">Website</span>
You can use button with attribute routerLink, it will not display the URL on hover. It could be written as:
<button [routerLink]="['/register']">Sign Up</button>
Since it's about angular, you can just do this instead:
<button (click)="routeToOtherPage()">Link</button>
with
routeToOtherPage() {
this.router.navigate(["/other-page"]);
}
You can also write your own directive to inline this, something along the lines of this:
#Directive({
selector: "[clickRouterLink]"
})
export class ClickRouterLinkDirective {
#Input()
private clickRouterLink: any | any[];
#HostListener("click")
public handleLinkClicked() {
// Crude check for whether an array has been provided.
// You might want to make this better (or even compare with the implementation of routerLink).
const route = this.clickRouterLink && typeof this.clickRouterLink.length === "number"
? this.clickRouterLink
: [this.clickRouterLink];
this.router.navigate(route);
}
constructor(private router: Router) {}
}
And then
<button clickRouterLink="events">Link</button>
<button [clickRouterLink]="['events', event.id]">Link</button>

How can I change component without changing URL in Angular?

I would like to change component without changing URL. Let's assume that I have a component register. When I open my website I have url www.myweb.com. Then I would like to register by clicking sign up. I would like to display my component register without changing URL. Should I use ngIf or something else? Can you show me example how it should be done?
UPDATE I am sorry, but it seems to me that I was misunderstood. I tried
this solution:
login.component.ts:
showSignUp: boolean = false;
login.component.html:
<button (click)="showSignUp = true">Sign Up</button>
<register *ngIf="showSignUp"></register>
However when I clicking the button Log in I get this:
before:
after clicking:
After clicking the button Log in I would like to get a new website but with the same URL like this:
UPDATE
What do you think about solution shown below? In html file I will be checking whether variable authenticated is equal true. If so then I will display home component.
login() {
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password)
.subscribe(
data => {
this.authenticated = true;
// this.router.navigate([this.returnUrl]);
},
error => {
this.authenticated = false;
this.alertService.error(error);
this.loading = false;
});
}
UPDATE
Unfortunately it doesn't work. Any ideas how can I use it with this button?
<button [disabled]="loading" class="btn btn-primary">Log in</button>
You can use *ngIf and show the component in condition!
examle
In your sign up component, set a variable and change its value on click of sign up button. And display your register component on click of the login by pitting the condition in display
// sign up component
showRegister = false;
in your sign up component html
<register *ngIf="showRegister"></register>
Yes, this is a perfect use case for ngIf. Try not to over engineer it.
ngIf is the way to go on this kind of thing.
Just put in your component code something like
showSignUp: boolean = false;
then in template:
<button (click)="showSignUp = true">Sign Up</button>
<register *ngIf="showSignUp"></register>
And since you seem new to Angular, I'll mention that in order to use ngIf in template, your module needs to import the CommonModule like
import { CommonModule } from '#angular/common';
imports: [
CommonModule,
]