Angular 5 [object HTMLDivElement] - html

When I click the button, I'm sending the 'copyRow' element to the 'copyItem' method. I'm equalizer the 'copyItem' element with the 'item' variable in the 'Typescript' file.
This 'item in the html file' variable when I want to show '[object htmldivelement]' I'm getting as output.
create.component.html
<div class="questions">
<div class="form-group copy-row" #copyRow>
<label>Question</label>
<input type="text" class="form-control" placeholder="Question title">
</div>
{{ item }}
</div>
<button type="button" class="btn btn-primary" (click)="copyItem(copyRow)">Add</button>
create.component.ts
item;
constructor() { }
ngOnInit() {
}
copyItem(row) {
this.item = row;
}
EDIT
My aim is to do a survey project.
When I click on the 'Add' button, the same '#copyRow' element will show in the {{ item }} section. However, I get an output like the second link.
1: http://prntscr.com/j1ncp1
2: http://prntscr.com/j1nd19

I'm not sure what you want to achieve with this but this is the explanation of what is happening in your code.
#copyRow is a reference to the HTML element & in this case it is a div element. So when you're passing the reference using copyItem function, you are actually passing an HTML element.
Putting these things together, the copyItem method gets following signature -
public item: HTMLElement;
public copyItem(row: HTMLElement): void {
this.item = row;
//this is how you get inner HTML
console.log(row.innerHTML);
//this is to get inner text
console.log(row.innerText);
}
This is the reason why you are getting [object HTMLDivElement] in the template for item binding (you are trying to display an object).
You can simply use {{item.innerHTML}} or {{item.innerText}} to display the inner content of selected HTML element.
Let me know if I'm missing anything.
EDIT - Alternative Way (Binding in Template)
If you are not doing additional stuff in the component, the binding can be as simple as assigning the HTML element reference directly to the item property in template itself -
<div class="questions">
<div class="form-group copy-row" #copyRow>
<label>Question</label>
<input type="text" class="form-control" placeholder="Question title">
</div>
{{ item?.innerHtml }}
{{ item?.innerText }}
</div>
<button type="button" class="btn btn-primary" (click)="item = copyRow">Add</button>
EDIT 2 (as per discussion in comments)
Try this template to iterate over same HTML on button click -
<div class="questions">
<ng-container *ngFor="let item of items">
<div class="form-group copy-row">
<label>Question</label>
<input type="text" class="form-control" placeholder="Question title" />
</div>
</ng-container>
<button type="button" class="btn btn-primary" (click)="items = items || []; items.push(1);">Add</button>
Just initialise your items array as -
public items: Array<number> = [1];
I hope this helps :)

Use ViewChild and ElementRef
import { Component, ViewChild, ElementRef } from '#angular/core'
#ViewChild('item')
item: ElementRef;
constructor() { }
ngOnInit() {
}
copyItem() {
// this.item -> now you have the reference of the element
}

Related

Kendo checkbox toggle true and false states not working properly

I'm trying to get it so when one or more checkbox is clicked, I'm able to get a button to appear. I would like the toggle function to work if one or more checkbox is clicked, but instead, it will turn on when I select one checkbox, then turn off during the next selection. I'm sure I've got a couple of unnecessary properties added into here as well, but not too concerned about that. Any help would be appreciated.
HTML: Button
<button class="btn btn-primary"
*ngIf="switchCase" style="float:right"
>Save</button>
HTML: Checkbox Column
<kendo-grid-column field="checkbox" editor="boolean">
<ng-template kendoGridCellTemplate let-dataItem id="flexSwitchCheckChecked"
>
<input type="checkbox" (click)="toggleButton(dataItem, 'checkbox'
[checked]="dataItem.checkbox"
[width]="40"
>
TS: Button click method
public switchCase: boolean = false;
toggleButton() {
this.switchCase = !this.switchCase;
pass an event to your function then access its value from typescript class:
Step1(pass an $event):
on *ngFor tag level add an index var
<div *ngFor="let item of items;let i = index" >
<input type="checkbox" (change)="toggleButton($event,i)">
</div>
Step1(get its value):
toggleButton($event,i) {
let newValue = $event.target.value;
// this.switchCase =newValue;
this.items[i].checked = newValue;
}

Problems with Angular Dynamic Reactive Form

I'm trying to create a dynamic reactive form. The user has the ability to choose between either a text (type = 1) input or img (type = 2) input. According to his choice, the right input is being added. - He can add as much input field as he wants.
I've never really used reactive forms before, hence this question.
The code below adds a control according to what module the user has chosen, but for instance adding a textarea only displays a textarea with [object Object] inside - clicking makes it disappear.
Additionally I haven't figured out yet how to submit the form's input. Logging form on submit returns the form, but without the textarea's input.
That's what I have so far:
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div *ngFor="let module of form.get('modules').controls; let i = index" class="position-relative" [style.padding-bottom.rem]="paddingValue" (mouseover)="showPaddingInput = true" formArrayName="modules">
<div class="padding-input position-absolute d-flex justify-content-center" *ngIf="showPaddingInput === true" [style.height.rem]="paddingValue">
<input type="text" class="align-self-center text-center padding-input-field" [value]="paddingValue" (input)="changePadding(padding.value)" #padding>
</div>
<div class="text" *ngIf="module.value.type === 1">
<textarea class="flow-text" placeholder="Some Text..." rows="3" [formControlName]="i"></textarea>
</div>
<div class="img-container" *ngIf="module.value.type === 2">
<div class="custom-file align-self-center">
<input type="file" id="i" class="custom-file-input" [formControlName]="i" (change)="handleFileInput($event.target.files)">
<label class="custom-file-label" for="i"></label>
</div>
</div>
</div>
<button class="btn btn-dark">Submit</button>
</form>
export class CreateCaseCmsComponent implements OnInit {
form: FormGroup;
constructor(private caseService: CasesService) { }
addModule(type) {
if (type === 1) {
const control = new FormControl({type: 1}, Validators.required);
(this.form.get('modules') as FormArray).push(control);
} else if (type === 2) {
const control = new FormControl({type: 2}, Validators.required);
(this.form.get('modules') as FormArray).push(control);
}
}
ngOnInit() {
this.form = new FormGroup({
modules: new FormArray([])
});
}
onSubmit() {
console.log(this.form);
}
}
the first argument to a form control is it's value, so you're setting the initial value as an object and that's why it's showing [object Object] in the text box... that's what you get if you call .toString() on an object, you need to instantiate them like this:
const control = new FormControl('', Validators.required);
or something like that... this affects how you're building your template, so you probably need something more like:
const group = new FormGroup({
type: new FormControl(1),
value: new FormControl('', Validators.required)
});
and add that group to your array and access it like:
<div class="text" *ngIf="module.get('type').value === 1" [formGroupName]="i">
<textarea class="flow-text" placeholder="Some Text..." rows="3" formControlName="value"></textarea>
</div>

How to focus a control that is inside a FormArray?

I have a FormArray with an email field and I need to focus the field whenever a new control is added inside FormArray for the user to type the email.
I tried with #ViewChild but it doesn't work correctly as it will repeat the elements with the same id #emailInput.
component.ts
public addEmail(): void {
this.emailArray.push(this.createControlEmail());
// Here I need to focus on the control that has been added.
this.form.markAsDirty();
}
component.html
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="start"
[formGroupName]="indexControlEmail">
<mat-form-field fxFlex="60" [floatLabel]="true">
<input #emailInput matInput placeholder="E-mail" formControlName="email">
</mat-form-field>
<button mat-icon-button aria-label="delete" matTooltip="Remove email"
[matTooltipPosition]="'after'" [matTooltipShowDelay]="1000"
(click)="removeEmail(indexControlEmail)">
<mat-icon class="secondary-text">close</mat-icon>
</button>
</div>
</div>
You can use ViewChildren to get list of email inputs on page.
Any time a child element is added, removed, or moved, the query list
will be updated, and the changes observable of the query list will
emit a new value.
#ViewChildren('emailInput') emailInputs: QueryList<ElementRef>;
Each time you do addEmail focus on last emailInput:
addEmail() {
this.emailInputs.changes.pipe(take(1)).subscribe({
next: changes => changes.last.nativeElement.focus()
});
this.emailArray.push(this.createControlEmail());
// const newControl = this.fb.control('');
// this.emailArray.push(newControl);
}

Unable to control html elements using form state

I am trying to create a form in angular
<form name="vm.mechanicalForm">
<div layout="column" layout-padding>
<div layout="row" layout-align="end center">
<md-button class="aq-btn md-accent" type="submit"
ng-show="vm.Auth.check({access: 'EDIT'})"
ng-click="vm.save()"
ng-disabled="vm.mechanicalForm.$invalid">
Save Button
</md-button>
</div>
<div>
<md-input-container class="md-block"
ng-if="vm.building.mainSourceOfCooling === 'CHILLERS'">
<label>Cooler</label>
<input name="numberOfChillers" class="form-control"
type="number"
ng-model="vm.building.numberOfChillers"
required min="0">
<div ng-messages="vm.mechanicalForm.numberOfChillers.$error"
ng-hide="vm.building.numberOfChillers">
<div ng-message="min">
<span class="red">Number of Chillers must be a positive number</span>
</div>
</div>
</md-input-container>
</div>
</div>
</form>
I cannot get any of the states connected to the form, for example vm.mechanicalForm.$invalid/$valid to work. I want to be able to disable my save button when there is a negative number of Coolers entered. Even though the error Number of chillers must be positive is displayed the save button still works. Additionally, I wanted to change the ng-hide on the ng-messages div to hide the div when the form is valid, but that seems to break functionality too. What can I do to correct this error? I have looked around on stack overflow and the answers I have seen seem to suggest I am using the form state correctly.
EDIT:
Here is my controller code:
namespace properties {
export class MechanicalCtrl {
constructor(
public Messages,
public building,
public BuildingService,
private allEnums,
private Auth
) {}
public save() {
this.BuildingService.updateBuilding(this.building)
.then(() => {
this.Messages.success('Successfully updated mechanical information');
})
.catch(() => {
this.Messages.error('Unable to update mechanical information');
});
}
}
angular
.module('properties')
.controller('MechanicalCtrl', MechanicalCtrl);
}
According to the answer here I should be able to reference vm.mechanicalForm in my MechanicalCtrl as this.mechanicalForm (unless I misunderstood that answer). Another thing I have noticed is that I am not able to do this. I had tried debugging this by trying to console.log properties on the form but it is returned as undefined if I do console.log(this.mechanicalForm). Have I done something wrong?
EDIT 2:
I am setting up my template and controller as
.state(...) {
templateUrl: 'my/template/url',
controller: 'MechanicalCtrl as vm',
...
}

*ngFor generated mat-button is focused on load

I'm generating a list of buttons using ngFor and when I load the site, the first generated button is already focused until I click anywhere else ...
<div align="center" class="buttonDiv" *ngFor="let tamplates of caseService.availableTaskTemplates">
<button mat-button class="button" *ngIf="tamplates.contentType == 1" (click)="createTask(tamplates.taskType)">
{{ tamplates.headerText }}
</button>
</div>
here's a picture:
https://ibb.co/imG7sy
what can I do to "unfocus" the button?
you can use autofocus property in HTML.
autofocus: focus on the element once the page is loaded
add it to a dummy element in your HTML in order to un focus the meta-button.
for example:
<label autofocus> </label>
---------------- Another Solution -------------------
you can use blur() method in javascript, after the view is rendered.
inside AfterViewInit
#Component({
selector: 'my-cmp',
templateUrl: `my-cmp.html`
})
class MyComponent implements AfterViewInit {
#ViewChild('buttonsList') buttonsList;
ngAfterViewInit() {
// execute blur on the first child of the list.
this.buttonsList.nativeElement.firstChild.blur();
}
}
in your template:
<div #buttonsList *ngFor='let item of items'>
<button meta-button> {{item}} </button>
</div>