Write if-else condition angular 4 for flexGrow - html

in my app I am using fexbox concept to show the companies name and headcompany of a material. I used ngx-datatable here. I set columnmode to flex. Now I set flexgrow for each column to 1. I want to wrote an if-else condition in case of flex-grow for this two column. for example if selectedCompany is pressed the second column grow will be 2, else it will be 1.
<ngx-datatable *ngIf="companies && companies.length" class="material" [scrollbarH]="true" [rows]="companies" [externalPaging]="true" [externalSorting]="true" [count]="companyPage.totalElements" [offset]="companyPage.number" [limit]="companyPage.size"
[columnMode]="'flex'" [footerHeight]="'auto'" [headerHeight]="'auto'" [rowHeight]="'auto'" (page)='onCompanyPageEvent($event)' (sort)='onCompanySortEvent($event)' (activate)="onActivateCompany($event)">
<ngx-datatable-column [prop]="'name'" [flexGrow]="1" >
<ng-template ngx-datatable-header-template >
<span class="ellipsis" title="{{value}}">
{{'A921Name' | translate}}
</span>
</ng-template>
</ngx-datatable-column>
// I triesd to write if-else here
<ngx-datatable-column [prop]="'parentName'" *ngIf="selectedCompany" [flexGrow]="2" [sortable]="false" >
<ng-template ngx-datatable-header-template>
<span class="ellipsis" title="{{value}}">
{{'A922ParentCompany' | translate}}
</span>
</ng-template>
</ngx-datatable-column>
</ngx-datatable>

When you wrap an attribute with square brackets, you make it dynamic. It means that it will look for properties on the respective typescript class. So for static values like the one you have here [footerHeight]="'auto'", you don't need to wrap footerHeight with square bracket as you are setting it to a string 'auto'. You can just do footerHeight="auto". However, if there is a property in your typescript class that holds the string value for footerHeight, for example:
export class RelatedPage {
footerHeightValue: string;
constructor(){
this.footerHeightValue = 'auto';
}
}
then you can do the dynamic binding and set your footerHeight to the respective property as in [footerHeight]="footerHeightValue";
So, when you want to make your attribute's value change on certain conditional statement, you want to make the attribute dynamic as in [attribute]. In your case, if your flexGrow property has to change based on whether the title was tapped or not, you could bind the tap event to a boolean value.
<span (click)="titleClicked()">{{companyName}}</span>
and in your typescript class,
export class RelatedPage {
titleSelected: boolean;
titleClicked() {
this.titleSelected = true;
}
}
Now, you can bind the titleSelected property with your flexBox attribute as in
[flexBox]="titleSelected ? 2 : 1"
this evaluates as
if (titleSelected === true) {
flexBox = 2;
} else {
flexBox = 1;
}

Related

Angular: is it possible to bundle attribute bindings in a template, similar as ng-templates for HTML-elements?

Is it possible to combine multiple attribute-bindings within the HTML of an Angular component to prevent duplicated code?
For example, we currently have a component which has a button and an anchor like this:
<ng-template #button>
<button [class]="classes"
(click)="onClick($event)"
[attr.disabled]="disabled">
<ng-template [ngTemplateOutlet]="buttonContent"></ng-template>
</button>
</ng-template>
<ng-template #anchor>
<a [class]="classes"
(click)="onClick($event)"
[attr.disabled]="disabled"
[href]="href">
<ng-template [ngTemplateOutlet]="buttonContent"></ng-template>
</a>
</ng-template>
<ng-template #anchorWithoutHref>
<a [class]="classes"
(click)="onClick($event)"
[attr.disabled]="disabled">
<ng-template [ngTemplateOutlet]="buttonContent"></ng-template>
</a>
</ng-template>
As you can see, we have [class]="classes" (click)="onClick($event)" [attr.disabled]="disabled" on all three variants. Is it possible to create some kind of template or anything that's re-usable, so these attribute-bindings are only done on one place?
(We use Angular version 15.1.3.)
EDIT: classes; disabled; and onClick(...) are values/getters in my button.component.ts (and disabled is an #Input as well):
get classes(): string {
let classesBuilder = new ClassesBuilder();
...
return classesBuilder.toString();
}
private _disabled: boolean = false;
#Input()
set disabled(value: BooleanInput) {
this._disabled = coerceBooleanProperty(value);
}
get disabled(): boolean {
return this._disabled;
}
onClick($event: MouseEvent) {
if (this.href && (!this.useButtonClick || $event.ctrlKey || $event.shiftKey)) {
return; // Default behaviour
}
$event.preventDefault();
$event.stopPropagation();
if (!this.disabled) {
this.buttonClick.emit();
}
}
This disabled is used in a few places within if-checks (like the onClick above, but also some other functions).
So the Directive approach suggested by #WouterSpaak does sound promising, and would indeed work if I could have moved the entire functionality/values to that directive. But if I want to pass these values above from my button.component.ts to the Directive, I think I still need duplicated attributes to pass that data to the Directive, making it redundant in my case..
Create a directive:
#Directive({
selector: 'sharedAttrs'
})
export class SharedAttrsDirective {
#HostBinding('class')
protected readonly clz = classes;
#HostBinding('attr.disabled')
protected isDisabled() {
return someBoolean;
}
#HostListener('click', ['$event'])
protected handleClick(event: MouseEvent) {
doStuffWith(event);
}
}
Then in your template, you can do something like:
<button sharedAttrs></button>
<a sharedAttrs href="www.stackoverflow.com"></a>
<some-other-element-or-component sharedAttrs></some-other-element-or-component>

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/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

Displaying hidden Div element at specific index using Angular Material table

I have a mat-table which has the following functionality: -
(1) Add / Remove rows
(2) Add data into a row using various controls (combo-box, text box, etc).
One of the controls (Addition Information) is a text box that when a ? is entered displays a hidden 'div' element that will eventually be used to hold a list of data.
The issue I have is that if I add say 3 rows and enter a ? into the 3rd row the hidden 'div' display on all 3 rows.
I need a way to somehow index each row added to the table and only display the 'div' element of that row.
Unfortunately my knowledge of HTML is limited and I am fairly new to Angular as well.
I have created a StackBlitz solution demoing my issue. demo
HERE'S A WORKING STACKBLITZ I created an array expandedCols : boolean[] = []; that keeps track of the state (expanded or not) of your div, when you add a row, I also add one to that array with default value false, when you put ? I just change the value at index i to true.
<ng-container matColumnDef="additionalCode" class="parent" >
<th mat-header-cell *matHeaderCellDef>Additional Code</th>
<td mat-cell *matCellDef="let element; let i = index" class="parent" >
<mat-form-field class="type">
<input matInput (keyup)="toggleLookup($event, i)" autocomplete="off" (keydown.ArrowDown)="onDown()" placeholder="Enter ?">
</mat-form-field>
<div *ngIf="expandedCols[i] == true" class="child">Yah it expanded
<button (click)="expanded = false">Close</button>
</div>
</td>
</ng-container>
TS:
addRow() {
this.doAddRow();
this.expanded = false;
this.expandedCols.push(false);
}
toggleLookup(event: any, i): void {
if (event.target.value !== '?') {
return;
}
event.target.value = '';
this.expanded = true;
this.expandedCols[i] = true;
}
You should also pay attention to removing rows, do splice, you get the idea.
The proper way to solve your issue is to use row local state - so put your attribute inside column collection (new column is hidden). This way you will have an opportunity to manage expanded property of specific row. Here is your updated stackblitz.