Angular - How to add a counter Variable from template to html property? - html

I have this HTML:
<ng-container *ngFor="let something of things; let i = index">
<span data-e2e="{{"abc" + i}}" class="checkmark"></span>
</ng-container>
I want to have this result rendered in the DOM:
<span data-e2e="abc0" class="checkmark"></span>
<span data-e2e="abc1" class="checkmark"></span>
...
But this {{"abc" + i}} wont work.
"abc" should be a string.
How can I do this, without saving the property (counter: i) to the components controller

You should change,
<ng-container *ngFor="let something of things; let i = index">
<span data-e2e="{{"abc" + i}}" class="checkmark"></span>
</ng-container>
To:
<ng-container *ngFor="let something of things; let i = index">
<span [attr.data-e2e]="'abc'+i" class="checkmark"> {{something}} </span>
</ng-container>
Here you should use attr as prefix and need to assign the value to custom attribute using property binding like, [attr.data-e2e]="'abc'+i"..
Working Stackblitz..

You should change:
<span data="normalString{{i}}" class="checkmark"></span>
normalString should be outside {{}}
also data attribute is not correct, read more how to properly add custom data attribute:
https://www.w3schools.com/tags/att_global_data.asp

Related

How to Set default value in a loop of formControls in HTML?

I have an array of input elements with formControl Directive. I have set the value of each element with value attribute, but it showing blank. If I set the value in ts file, the same value will be set in each and every element. So, how to differentiate the elements if I have to set value in ts?
Here is my code:
HTML code:
<ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>Amount
<button mat-icon-button (click)="onEditClick(datasource.data)">
<mat-icon style="cursor: pointer;" class="ml-2 mr-2" >edit</mat-icon>
</button>
</th>
<td mat-cell *matCellDef="let row; let i=index;">
<ng-container *ngIf="isActual">{{row.amount || "-- --"}}</ng-container>
<mat-form-field *ngIf="isEditMonth" class="example-full-width month-textbox" appearance="outline">
<input #elRef matInput type="text" [formControl]="amountField" (change)="onAmountChanged($event,row)" [value]="row.amount">
</mat-form-field>
</td>
</ng-container>
TS Code:
amountField = new FormControl();
this.form.controls['amountField'].setValue("Value you want to be selected");
You can set the value by using this.
The default value of the select element can be set by using the selected attribute on the required option. This is a boolean attribute meaning if the selected attribute is present the element will be displayed by default on the dropdown list.

How to access dynamically set inputs name property in an Angular form in the template

I wrapped an ngx-datatable component in a form tag so I can validate inputs in the table cells. Due to the nature of how the table is populated I set the inputs name properties dynamically
<form #tableForm="ngForm">
<ngx-datatable
[rows]="_rows">
<ng-container *ngFor="let column of rowDeffinition; let columnIndex=index">
<ngx-datatable-column [prop]="column.key" [name]="column.label">
<ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value" let-row="row">
<input
class="cell-input"
(blur)="updateCellValue($event, column.key, rowIndex)"
type="text"
[ngModel]="value"
[name]="rowIndex + '-' + column.key"
/>
...
</ng-template>
</ngx-datatable-column>
</ng-container>
</ngx-datatable>
</form>
Normally, the name property would creates a local variable in the template and you can access the inputs control properties via the variable name.
<input type="text" name="name" [(ngModel)]="name" required minlength="4" />
<div *ngIf="name.invalid && name.touched">
I wonder how can I do this dynamically in the same manner I set the inputs name. So far I was able to access the input controls via the form reference but this becomes quite wordy
<span *ngIf="!tableForm.controls[rowIndex + '-' + column.key]?.valid &&
!tableForm.controls[rowIndex + '-' + column.key]?.pristine"
class="[ c-validation-message ]">
required
</span>
You can wrap your input in new component and you can access these generated components via #ViewChildren(...) in parent components .ts file:
#ViewChildren(NgxDatatableInput) datatableInputs: QueryList<NgxDatatableInput>;
I recommend to create method in parent component, which retrieves concrete datatableInput from datatableInputs by name as parameter. After that you can use this method in generated, new ValidationSpanComponent:
<ValidationSpan [control]="getDatatableInput(dynamicName)">
</ValidationSpan>
Template of ValidationSpanComponent:
<span *ngIf="!control.valid &&
!control.pristine"
class="[ c-validation-message ]">
required
</span>
Inspired by this answer I came up with following solution
<form #tableForm="ngForm">
<ngx-datatable
[rows]="_rows">
<ng-container *ngFor="let column of rowDeffinition; let columnIndex=index">
<ngx-datatable-column [prop]="column.key" [name]="column.label">
<ng-template ngx-datatable-cell-template let-rowIndex="rowIndex" let-value="value" let-row="row">
<!-- Helper template allowing to define few variables for more readable property binding-->
<ng-template #cellContent [ngTemplateOutlet]="cellContent"
let-cellName="cellName" let-isValidInput="isValidInput" let-isPristineInput="isPristineInput"
let-isRequiredInput="isRequiredInput"
[ngTemplateOutletContext]="{
cellName: rowIndex + '-' + column.key,
isValidInput: tableForm.controls[rowIndex + '-' + column.key]?.valid,
isPristineInput: tableForm.controls[rowIndex + '-' + column.key]?.pristine,
isRequiredInput: column.input?.required
}">
<input
class="cell-input"
(blur)="updateCellValue($event, column.key, rowIndex)"
type="text"
[ngModel]="value"
[name]="cellname"
/>
...
</ng-template>
</ng-template>
</ngx-datatable-column>
</ng-container>
</ngx-datatable>
</form>
This improves the general readability vastly, since my table has a very complex logic, and i track the state of the cell in a structure like:
interface EditTableRowStatus {
editing: { [coolumnId: string]: boolean},
changed: { [coolumnId: string]: boolean},
addedRow: boolean;
}
let rowsStates = EditTableRowStatus[]
which led to super complex property binding in the template
<input
class="cell-input"
*ngIf="column.input?.type === INPUT_TYPES.TEXT && (rowsStates[rowIndex]?.editing?.[column.key] || rowsStates[rowIndex]?.addedRow)"
[autofocus]="!rowsStates[rowIndex]?.addedRow || columnIndex === 0 "
(blur)="updateCellValue($event, column.key, rowIndex)"
type="text"
[ngModel]="value"
[ngClass]="{'has-changes': rowsStates[rowIndex]?.changed?.[column.key]}"
[name]="rowIndex + '-' + column.key"
[required]="column.input?.required"
/>
now becoming much more readable
<input
class="cell-input"
*ngIf="inputType === INPUT_TYPES.TEXT && (isEditing || isAddedRow)"
[autofocus]="!isAddedRow || columnIndex === 0 "
(blur)="updateCellValue($event, column.key, rowIndex)"
type="text"
[ngModel]="value"
[ngClass]="{'has-changes': isChanged}"
[name]="cellName"
[required]="isRequiredInput"
/>

Don't render if array is empty - Angular *ngFor

I'm building up a Frontend with JSON Data.
I have to go trough a lot of arrays to check if a value exists inside. If the Value doesn't exist, then my complete HTML shouldn't render.
<ng-container *ngIf="document161['_source']['161_Events']">
<div class="row">
<span class="col-md-auto meta-object text-muted"><span>Begräbnistag:</span></span>
<span class="col">
<span *ngFor="let event of document161['_source']['161_Events']">
<span *ngFor="let sub of event.peventsub">
<ng-container *ngIf="sub.content === 'Begräbnis'">
<span *ngFor="let geburtsort of event.pstdatetypesub">
{{geburtsort.content}}
</span>
</ng-container>
</span>
</span>
</span>
</div>
<mat-divider class="my-2"></mat-divider>
</ng-container>
What i have already tried is:
<ng-template #thenBlock>
<ng-container *ngIf="document161['_source']['161_Events']">
<div class="row">
<span class="col-md-auto meta-object text-muted"><span>Begräbnistag:</span></span>
<span class="col">
<span *ngFor="let event of document161['_source']['161_Events']">
<span *ngFor="let sub of event.peventsub">
<ng-container *ngIf="sub.content === 'Begräbnis'">
<span *ngFor="let geburtsort of event.pstdatetypesub">
<ng-container ngIf="*ngIf="geburtsort.content; then thenBlock else elseBlock">
{{geburtsort.content}}
</ng-container>
</span>
</ng-container>
</span>
</span>
</span>
</div>
<mat-divider class="my-2"></mat-divider>
</ng-container>
</ng-template>
<ng-template #elseBlock>
</ng-template>
But as you can see there are multiple problems. Maybe Ii can't reach the ngIf condition if the value doesn't exist. And the then else logic doesn't work inside a ng-container template.
What should be the best solution?
try this
*ngIf="document161?._source?.161_Events?.length > 0"
If array having values it will show that element else will hide same.

toggle a checkbox attribute on a array of items in a carousel in Angular 2/Typescript

I have this carousel. Each item in the carousel has a checkbox above it. I want to be able to click on item and the checkbox gets a checked attribute. The code does this. The only problem I'm having is it toggles all check boxes in the carousel. I just want it to toggle the one clicked.
<tshq-carousel [dataSource]="brands" [selections]="selectedBrands" (select)="onItemsSelected($event)" [changeKey]="changeKey" [pageSize]="5" [circular]="false"
[enableSelection]="true" mode="single">
<ng-template let-brand>
<ng-container [ngSwitch]="brand.icon&&brand.icon.length>0">
<ng-container *ngSwitchCase="true" >
<label class="apply carousel-checkbox">
<input type="checkbox" id="brandCheck" [checked]="isChecked" value="no" name = "brand">
</label>
<img [src]="formatLogo(brand.icon)" style="max-width:150px" (click)="isChecked = !isChecked" />
</ng-container>
</ng-container>
</ng-template>
</tshq-carousel>
You use isChecked variable to binding to all input checkbox of carousel, so when you change value (check or uncheck) 1 input checkbox, it also set to all remain ones.
I suggest you add 1 property to model brand and use it to binding to input checkbox or image.
Ex:
<tshq-carousel [dataSource]="brands" [selections]="selectedBrands" (select)="onItemsSelected($event)" [changeKey]="changeKey" [pageSize]="5" [circular]="false"
[enableSelection]="true" mode="single">
<ng-template let-brand>
<ng-container [ngSwitch]="brand.icon && brand.icon.length>0">
<ng-container *ngSwitchCase="true" >
<label class="apply carousel-checkbox">
<input type="checkbox" id="brandCheck" [checked]="brand.isChecked" value="no" name="brand">
</label>
<img [src]="formatLogo(brand.icon)" style="max-width:150px" (click)="brand.isChecked = !brand.isChecked" />
</ng-container>
</ng-container>
</ng-template>
</tshq-carousel>

How to get first element of md-radio-button checked within *ngFor (angular2)

Please I have this code snippet:
<md-radio-group>
<span *ngFor="let size of item.sizes; let i = index;">
<md-radio-button [value]="size.id" [attr.checked]="i === 0 ? '' : null">
<span class="inline inset text-capitalize" >{{size.name}} </span>
</md-radio-button>
</span>
</md-radio-group>
I want that first md-radio-button will be checked by default, that's why I've added [attr.checked]="i === 0 ? '' : null" following this question but nothing happened. Any ideas how to achieve that please ?
the md-radio-button being a custom component, you cannot check it with an attribute like you would do with an <input type="radio">.
But maybe you could set a default value on your ngModel or formControl...?
<md-radio-group [(ngModel)]="foo">
<span *ngFor="let size of item.sizes; let i = index;">
<md-radio-button [value]="size.id" [attr.checked]="i === 0 ? '' : null">
<span class="inline inset text-capitalize" >{{size.name}} </span>
</md-radio-button>
</span>
</md-radio-group>
ngOnInit(){
this.foo=item.sizes[0].id;
}