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

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.

Related

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. Getting wrong data from ngb-panel

I use ngb-accordion in my app. I am trying to get data from every panel but when the first panel is opened click from the second panel returns me wrong data.
Result
I think the problem is the event which raises when input file changes.
Stackblitz Link
I will be glad if someone give me a hint for solving this problem.
There are few things to note in your code.
Your *ngFor is at ngb-accordion which is creating a new accordion for every loop, instead of creating multiple panel within one accordion.
Fix: <ngb-panel *ngFor="let data of datalist; let i = index">
You are using the same label for all three panels, because of which your first panel is opening every time, regardless of which panel you are clicking.
Fix: <label [for]="'image-input-' + i"> and <input ... [id]="'image-input-' + i"
The modal that opens after image selection has no knowledge of which panel it's getting triggered from. So, you have to use your (change)="onFileChange($event, data)" event/function to keep track of selected panel/corresponding data.
Then you can pass that selection from your modal to your processFile(...)
Fix:
export class AppComponent {
...
selectedData: Data;
...
...
onFileChange(event: any, data): void {
...
this.selectedData = data;
}
}
html:
...
<input ... (change)="onFileChange($event, data)>
...
...
<button
...
(click)="processFile(imageInput, selectedData)"
> Done
</button>
Stackblitz Demo

Scoping Issue in Table when trying to access a specific row in a dilaog box (Vue.js, Element-ui)

I have created a table using el-table (Element-ui) in Vue.js. I want to access a specific row in the table when clicked on a button in that row, but the catch here is that after clicking on the button, a dialog box should open up and then access that specific row. When I try to access the row outside of the dialog box using scope.row, it works perfectly fine but it does not work properly when accessed inside teh dialog box, instead it runs in a loop till the end of the table.
Please find the code below:
<el-table-column prop="count"
label="Total">
<template slot-scope="scope">
<!-- {{fetchData(scope.row)}} When scope.row is accessed here, it works perfectly-->
<el-button type="text" #click="dialogVisible = true">{{scope.row.count}}</el-button>
<el-dialog
:visible.sync="dialogVisible"
:before-close="handleClose">
<!--I want to access the speicfic row clicked here, but it ends up looping through the table and doesnt send that specific row only. -->
{{fetchData(scope.row)}}
</el-dialog>
</template>
</el-table-column>
Can someone please suggest some solution to this issue in the above code? I am stuck on this for while. Would appreciate it.
Thank you.
This is a table... So fetchData will be called for each row as your code sits now.
But if you attach fetchData on the button instead, it will work. But then you would have to add a variable to the mix, or use a computed property. Anyways, I don't like calling functions in template, handle that logic in script or using computed properties. So here's what I'd do:
data() {
return {
chosenRow: null
}
},
methods: {
fetchData(row) {
this.chosenRow = row;
}
}
Template:
<template slot-scope="scope">
<el-button type="text" #click="fetchData(scope.row); dialogVisible = true">
{{ scope.row.follower_count }}
</el-button>
<el-dialog :visible.sync="dialogVisible">
{{ chosenRow }}
</el-dialog>
</template>
or just assign the row in template...
#click="chosenRow = scope.row; dialogVisible = true"

How to disable item from different component in Angular

So I have Tags component and I imported that component in my Chart-details component. In my Chart Details component I have a checkbox that will disable all the input box, drop down box, buttons that are located inside chart-details page but some reason my imported tags component is not disabling when I click the checkbox. Any suggestion or help on how to fix this so that a user cannot add or remove tags when the checkbox is clicked will be really appreciated.
Chart Details Component. HTML
//Check box to disable all the input, drop down, buttons
<mat-checkbox *ngIf="chart && workspace" color="primary" [disabled]="this.workspace.type === WorkspaceType.user"
[(ngModel)]="chart.isPublished" (ngModelChange)="publishChange($event)">Published</mat-checkbox>
//Example Button
<button color="primary" mat-flat-button (click)="saveClick()" [disabled]="this.chart.isPublished">Save</button>
// Imported Tags Component
<mc-tags [_normalTags]="chart.tags" [removable]="true" [selectable]="true"
(added)="tagAdded($event)" (removed)="tagRemoved($event)" [disabled]="this.chart.isPublished" >
</mc-tags>
I have added [disabled]="this.chart.isPublished" but I got an error saying "Can't bind to 'disabled' since it isn't a known property of 'mc-tags'. ". Also I tried (disabled) but still not working and user can still add or remove tags even though checkbox is checked.
Tags Component.HTML
<mat-chip-list #chipList [disabled]="true">
<mat-chip *ngFor="let chip of normalTags" [selectable]="selectable"
[removable]="removable"
(removed)="removeTags(chip)">
{{chip.tag}}
</mat-chip>
<input matInput #input [(ngModel)]="tagIn" [formControl]="tagCtrl2"
[matAutocomplete]="auto" />
</mat-chip-list>
Right now I have to do [disabled]="true" on mat-chip-list in Tags component.html so that user can't add or remove it. I don't want to hard code this and want to control this using Chart Detail Component Checkbox.
The disable matchip and input look like this (PIC)
It's not gonna run but I've attached the whole code for these two component over here https://stackblitz.com/edit/angular-ivy-wwfcai . thanks
Have you declared "#Input() disabled" in your child component class?
In Child.component.ts
#Input() disabled: any;
ngOnInit() {
if(disabled){
//set properties to true/false accordingly
}
}
In Parent.component.html
[disabled]="this.workspace.type === WorkspaceType.user"

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