The toggle change does not detect me - html

I have a toggle that activates or deactivates a tooltip in a table. At the moment, it is activated when starting the application but I want it to be deactivated when starting the application and it is only activated when the toggle is set to true. I have the code as I show it below. I use the Nebular library for angular v9
ngOnChanges(changes: SimpleChanges) {
if (
changes.showTooltip &&
changes.showTooltip.currentValue !== changes.showTooltip.previousValue
) {
this.cdr.detectChanges();
}
}
toggleTooltip() {
this.showTooltipFlag = !this.showTooltipFlag;
console.log(this.showTooltipFlag);
if (this.showTooltipFlag) {
this._render.removeClass(this._document.body, "hide-tooltip");
} else {
this._render.addClass(this._document.body, "hide-tooltip");
}
}
<condor-table
*ngIf="!isLoading"
[tableTemplates]="templates | async"
[columnsDefinition]="tableDef"
(rowInfo)="rowClicked($event)"
[dataCondor]="messages$ | async"
[showTooltip]="true"
></condor-table>
<div class="search__toggle">
<span>{{
(showTooltipFlag ? "hide_tooltip" : "show_tooltip") | translate
}}</span>
<nb-toggle
[checked]="showTooltipFlag"
(checkedChange)="toggleTooltip()"
></nb-toggle>
</div>

There are two ways of doing it:
you can change in your ngOnInIt:
this.showTooltipFlag=false
or else you can also try this way,
while declaring showTooltipFlag:
showTooltipFlag: boolean=false

Related

Keep expanded row state after reloading data source in material accordion

I have an accordion with nested expansion panels and I want to keep the expanded/not expanded status of each row after data is reloaded.
I found on the documentation of material accordion in expanded input that can be used for expansion panels, but I don't see a solution to keep the state of each row in order to pass it to the expanded input. https://material.angular.io/components/expansion/api
<mat-expansion-panel *ngFor="let region of (groupedData | keyvalue)">
<mat-expansion-panel *ngFor="let country of (region.value | keyvalue)"
togglePosition='before'>
</mat-expansion-panel>
</mat-expansion-panel>
What if you keep track of the expanded regions and countries?
expandedRegions: { [key: string]: boolean } = {};
expandedCountries: { [key: string]: boolean } = {};
Add some event handlers for the opened and closed outputs of the mat-expansion-panel component and update the maps accordingly:
<mat-expansion-panel *ngFor="let region of (groupedData | keyvalue: regionSortFn)"
(opened)="handleRegionPanelStateChange(region.key, true)"
(closed)="handleRegionPanelStateChange(region.key, false)"
[expanded]="expandedRegions[region.key]">
<!-- ... -->
<mat-expansion-panel *ngFor="let country of (region.value | keyvalue: countrySortFn)"
togglePosition='before'
(opened)="handleCountryPanelStateChanged(country.key, true)"
(closed)="handleCountryPanelStateChanged(country.key, false)"
[expanded]="expandedCountries[country.key]">
<!-- ... -->
</mat-expansion-panel>
</mat-expansion-panel>
The handlers are nothing more than this:
handleRegionPanelStateChange(key: string, isOpen: boolean) {
this.expandedRegions = { ...this.expandedRegions, [key]: isOpen };
}
handleCountryPanelStateChanged(key: string, isOpen: boolean) {
this.expandedCountries = { ...this.expandedCountries, [key]: isOpen };
}
Whenever you reload the data, the panels that were previously expanded should keep their state. If you refresh the page, this will of course be lost, if you need to persist across page refreshes, look into session storage or local storage and put them there.

How to hide a component if there is no result found in Angular

In my Home page, I have a search bar and imported three components. my search bar have the ability to search through them but just wondering how can I hide a particular component if a result in not found in that component and only show a component that have a result.
The problem I have right now is, if search result is only found in Application group component then, the attachment and training component is showing me blank (pls check uploaded image below). I just want to hide the components that don't have the result while user is filtering/searching and just show it back the component when a user cancel the search.
I would be really appreciated if I can get help or suggestion on this.
<!-- attachments -->
<div>
<app-attachment [attachments]="entity.attachments"></app-attachment>
</div>
<!-- appgroups -->
<div *ngFor="let entityGroup of entity.entityGroups">
<app-application-group [entityGroup]="entityGroup" [entity]="entity"></app-application-group>
</div>
<!-- Training and Support -->
<div>
<app-training [entity]="entity"></app-training>
</div>
</div>
ngOnInit(): void {
this.searchText$ = this.searchService.searchText
.asObservable()
.pipe(debounceTime(750), distinctUntilChanged())
.subscribe((value) => {
this.filterValue = value;
this.loadApplication(this.entityType, this.entityId);
});
this.collapse = false;
this.expanded = true;
this.route.url.subscribe((_value) => {
this.entityType = BaseEntity.stringToType(_value[0].path);
this.entityId = Number(_value[1].path);
this.loadApplication(this.entityType, this.entityId);
this.populateMeetups(this.entityId);
});
}
loadApplication(entityType: EntityType, entityId: number): void {
this.color = BaseEntity.color(this.entityType);
if (this.entityType && this.entityId) {
// this.filterValue = null;
this.childrenActive = null;
this.pageSize = 999;
this.childrenActive = true; // We want to bring only active children for things that have tables.
}
this.entityService
.getApplicationDetails(
entityId,
entityType,
this.pageSize,
this.childrenActive,
this.filterValue,
)
.subscribe((entity) => {
this.entity = entity;
this.ancestor = this.entity.channels.get(0);
this.entityGroup = this.entity.entityGroups.filter(
(r) => r.entityType === EntityType.Application,
);
this.entity.attachments = this.entity.attachments.filter((app) => {
return app.name.toLowerCase().includes(this.filterValue.toLowerCase());
});
});
}
click here to view my screenshot
Use *ngIf to remove stuff from the DOM you don't want to show. For example:
<ng-container *ngIf="entity.attachments?.length">
<div>
<app-attachment [attachments]="entity.attachments"></app-attachment>
</div>
</ng-container>
Or hide it with css:
<div [ngClass]="entity.attachments?.length ? 'show' : 'hide'">
<app-attachment [attachments]="entity.attachments"></app-attachment>
</div>
and the css:
.hide {
visibility: hidden;
}
You may want to consider placing the *ngIf inside the child component instead of the parent.
Try to use ngIf by checking the length of the search result instead of creating a new variable. Also use else block to display no result found as shown below
Show result here
No result found .

Angular/Primeng - Implement p-autocomplete dropdown with no button

I have a "p-autocomplete" element with multiple entries and dropdown enabled, and am wondering if I can do so without displaying the button, but instead show the dropdown menu when users click on the autocomplete field.
Is this possible with this type of object?
I am using Primeng version 4.3.0, if it makes a difference.
HTML:
<p-autoComplete id="input-country" [multiple]="true" [(ngModel)]="selectedCountries" [dropdown]="true"
[suggestions]="filteredCountries" (completeMethod)="filterCountryMultipe($event)">
<ng-template let-selectedCountries pTemplate="item">
....
</ng-template>
</p-autoComplete>
===================
UPDATE=====================
Thank you to olivier-depriester for your answer below. In addition to the onFocus method, I also removed the dropdown property and handled everything with the autocomplete, simply amending the filter method to accept an empty query.
Current HTML:
<p-autoComplete id="input-country" [multiple]="true" [(ngModel)]="selectedCountries" (onFocus)="onFocus()"
[suggestions]="filteredCountries" (completeMethod)="filterCountryMultiple($event)" >
<ng-template let-selectedCountries pTemplate="item">
....
</ng-template>
</p-autoComplete>
Current Typescript:
onFocus(){
this.filterCountryMultiple({query:''});
this.autoComplete.show();
}
filterCountryMultiple(event) {
let query = event.query;
this.filteredCountriesMultiple = this.filterCountry(query);
}
filterCountry(query):any[] {
....
for(let i = 0; i < countries.length; i++) {
let country = countries[i];
if(...query is empty...){
filtered.push(country.code);
}
else if(...match found...) {
filtered.push(country.code);
}
}
return filtered;
}
With primeng 6.5 to 7.0, I used the show function, bound on the focus event.
On typescript side :
export class MyComponent {
#ViewChild(AutoComplete) private autoComplete: AutoComplete;
onFocus() {
this.autoComplete.show();
}
}
And on HTML side :
<p-autoComplete id="input-country" [multiple]="true" [(ngModel)]="selectedCountries" [dropdown]="true"
[suggestions]="filteredCountries"
(completeMethod)="filterCountry($event)"
(onFocus)="onFocus($event)">
<ng-template let-selectedCountries pTemplate="item">
<div class="ui-helper-clearfix" style="border-bottom:1px solid #D5D5D5">
.......
</ng-template>
</p-autoComplete>
But in primeng current version (v8), this method is no more documented. So I don't know if it still exists

Unwanted component method execution during a mouse click event

I'm currently working on a component that displays a list of items using material grid list and material cards, where an item will be displayed only if it is exists in a given datasource. So far I am getting the result I need, but upon further inspection, I tried to log the method that I am calling to check if the item exists into the console and that's where I discovered that anytime I click on the page during testing/debugging, the method gets executed. I am just worried if this will somehow affect the performance of the app.
I haven't specifically tried anything yet as I am still unaware how this is happening (I am a beginner to angular, please bear with me)
HTML
<mat-grid-list cols="4" rowHeight=".85:1">
<div *ngFor="let item of items">
<mat-grid-tile *ngIf="item.isActive">
<mat-card class="mat-elevation-z10 item-card">
<mat-card-header>
<mat-card-title>{{item.title}}</mat-card-title>
<mat-card-subtitle>{{item.subtitle}}</mat-card-subtitle>
</mat-card-header>
<img mat-card-image src="{{item.icon}}" alt="{{item.name}}">
<mat-card-content>{{item.description}}</mat-card-content>
<mat-divider [inset]="true"></mat-divider>
<mat-card-actions>
<button mat-button
[disabled]="!isAccessible(item.name)">Action1</button>
</mat-card-actions>
</mat-card>
</mat-grid-tile>
</div>
</mat-grid-list>
COMPONENT
export class ItemComponent implements OnInit {
items: any;
dataSource: ItemDataSource; //items from the back end server
constructor(private store: Store<AppState>) { }
ngOnInit() {
this.items = fromConfig.ITEMS;
this.dataSource = new ItemDataSource(this.store);
this.dataSource.load();
}
isAccessible(itemName: string) {
return this.dataSource.isAccessible(itemName);
}
}
DATASOURCE
export class ItemDataSource implements DataSource<Item> {
itemSubject = new BehaviorSubject<Item[]>([]);
constructor(private store: Store<AppState>) { }
isAccessible(itemName: string): boolean {
let exists = false;
for (const itemSubject of this.itemSubject.value) {
console.log('Parameter Item Name: ' + itemName + '; Subject Item Name: ' + itemSubject.name);
if (itemSubject.name === itemName ) {
exists = true;
break;
}
}
return exists;
}
connect(collectionViewer: CollectionViewer): Observable<Item[]> {
return this.itemSubject.asObservable();
}
disconnect(collectionViewer: CollectionViewer): void {
this.itemSubject.complete();
}
}
Expected result would be that the method will be executed only once during initialization or after refresh.
You are using square brackets bind the disable property of the button. This binds the function with that button state. So, the function is called every time the page is being rendered. To use the function only once (as you intended), remove the braces.
<button mat-button disabled="!isAccessible(item.name)">Action1</button>
This will call the function only once when the page is rendered intially.

ngClass active button function Angular

How can I change the style of each button once I click on it?
I have several buttons and I need to achieve something similar to an active state, but in reality what you execute is a function.
I tried the following but I do not achieve an active state [ngClass]=" { 'btn-primary':categoria.nombre }
component.ts
ngOnInit() {
this.eventos = this.fs.getEventosAll();
this.categorias = this.fs.getCategorias();
}
filtrarEventos(categoria) {
this.eventos = this.fs.filterEventos(categoria);
}
component.html
<button class="btn btn-sm" *ngFor="let categoria of categorias | async" (click)="filtrarEventos(categoria.nombre)" [ngClass]=" { 'btn-primary':categoria.nombre } ">
{{ categoria.nombre }}
</button>
You can first, add a property, let's say, categoriaActiva to the component holding the buttons, add
this.categoriaActiva = categoria
to the filtrarEventos callback, and finally add
[class.btn-primary]="categoria.nombre === categoriaActiva"
to the button.
Here is the stackblitz of what you are trying to do
I called one method which sets the value of the clicked index
Template
<div *ngFor="let categoria of categorias; let i = index"
(click)="changeState(i)"
[ngClass]="clickedIndex === i ? 'primary' : 'secondary'">
{{categoria.nombre}}
</div>
Component
changeState(index) {
this.clickedIndex = index;
}