*ngFor generated mat-button is focused on load - html

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>

Related

Best practice for abstracting Angular HTML code?

In my Angular project I have a basic component
TS
#Component({
selector: 'app-cardboxes',
templateUrl: './cardboxes.component.html',
styleUrls: ['./cardboxes.component.scss']
})
export class CardboxesComponent implements OnInit {
constructor(public dialog: MatDialog) { }
ngOnInit() {}
}
In the HTML file, I use this component about 16 times. I have it completely hardcoded for 16 different "cardboxes", like so:
HTML
<mat-card id="CARDBOX">
<img class="logoy" src="assets/image" height=35px>
Box1
<input type="image" id="info" title="Click for description" src="assets/image2" height=20px/>
</mat-card>
<mat-card id="CARDBOX">
<img class="logoy" src="assets/image3" height=35px/>
Box2
<input type="image" id="info" title="Click for description" src="assets/image2.png" height=20px/>
</mat-card>
Essentially this renders a little box with a couple images, and a button that takes the user to an external link. There are 16 of these boxes typed into the HTML file, and everytime I want to add a new one, I have to rewrite essentially the same code with only the inputs different.
As you can see this format is heavily reused, but it is arduously hardcoded into the HTML file. Is there a way that I could abstract the reused code so that it is not tediously hardcoded in? How can I make a template for this that can be used multiple times? Going further, how can I add to, remove from, and alter this list of items?
In your component.ts file, you can add a property called something like: cards.
#Component({
selector: 'app-cardboxes',
templateUrl: './cardboxes.component.html',
styleUrls: ['./cardboxes.component.scss']
})
export class CardboxesComponent implements OnInit {
cards = [
{
'imageUrl': '/assets/image.png',
'link': 'link1.com',
... More Stuff YOu like
},
{
'imageUrl': '/assets/image1.png',
'link': 'link1.com',
... More Stuff YOu like
},
.. More items ...
];
constructor(public dialog: MatDialog) { }
ngOnInit() {}
}
Then in your html, you can loop through that cards array:
<mat-card id="CARDBOX" *ngFor="let card of cards">
<img class="logoy" src="{{ card.imageUrl }}" height=35px/>
Box2
<input type="image" id="info" title="Click for description" src="assets/image2.png" height=20px/>
</mat-card>
Note: I would also avoid adding the CARDBOX id to each element, as that is bad practice in HTML5.
Then your problem should be solved ... Let me know if it helps you.
Did not test it, but a ngFor can work wonders in this cases.
Make an array of a new interface cardDetails
export interface CardDetail {
id: number;
image: string;
link: string;
button: string;
input: string;
}
<mat-card *ngFor="let item of cardDetailArray">
<img class="logoy" src="{{item.image}}" height=35px>
{{item.button}}
<input type="image" id="info" title="Click for description" src="{{image.input}}" height=20px/>
</mat-card>
If it's literally only a diff on numbers. just make a number array[1,2,3,...x] and use the ngfor let item in numberArray , concatenate all things with the number variable.

Angular 10 scrollTo from parent component to an element in child component html

parent html:
<div>
<button type="button" (click)="scroll(childelementTwo)">TO CHILD</button>
</div>
<div>
<app-child>
</div>
child html:
<div>
<div #childelementOne>
lot of stuff
</div>
<div #childelementTwo>
another lot of stuff
</div>
</div>
if all this html code were in the "same" component.html I would use this function, but not:
scroll(el: HTMLElement) {
el.scrollIntoView();
}
So: How can I scroll to an html element in child component ?
You can use #ViewChildren for this.
List-Item:
#Component({
selector: 'app-list-item',
templateUrl: './list-item.component.html',
styleUrls: ['./list-item.component.css']
})
export class ListItemComponent implements OnInit {
#Input() list;
constructor(private elRef: ElementRef) { }
ngOnInit() {
}
scrollIntoView() {
this.elRef.nativeElement.scrollIntoView();
}
}
List-Component:
#ViewChildren(ListItemComponent) viewChildren!: QueryList<ListItemComponent>;
list = new Array(1000).fill(true)
scrollTo() {
this.viewChildren.toArray()[100].scrollIntoView()
}
HTML:
<button (click)="scrollTo()">scroll to 100</button>
<app-list-item *ngFor="let item of list">
list works!
</app-list-item>
stackblitz: https://stackblitz.com/edit/angular-ivy-6ccaav?file=src%2Fapp%2Fapp.component.html
Mat, your "elements" are in child and you want control in parent. So, first make access to the elements in child using ViewChild
//in your child.component
#ViewChild("childelementOne") childelementOne;
#ViewChild("childelementTwo") childelementTwo;
Then in parent you can do
<div>
<button type="button" (click)="scroll(childComponent.childelementTwo)">
TO CHILD
</button>
</div>
<div>
<!--see that use a template reference variable to the childComponent-->
<app-child #childComponent></app-child>
</div>
scroll(el: ElementRef) {
el.nativeElement.scrollIntoView();
}
See how, in the .html we are using childComponent.childelementTwo. childComponentis the own component app-child, childComponent.childelementTwo is the "variable" that we get in the #ViewChild. By defect is an ElementRef. You get to the HTMLElement using el.nativeElement. Yes, using a template reference we can access to all the public variables and public function of your child.component
I create a stackblitz that is looks like the stackblitz in enno's answer, but see that is complety different
NOTE. You can also use the same referenceVariable in the child.component and use ViewChildren, so you can pass to the function the QueryList and the index

How can I change the visibility of a div through the click event of a button or link that is on another page (html) in Angular

How can I change the visibility of a div through the click event of a button or link that is on another page (html)? I have a button on the "main.html" page that I want, which when I click, it displays a hidden div in "header.html". Is there a way for me to do this?
I have in index.html:
<button type="button" class="btn btn-default px-4" [routerLink]="['../home']" (click)="">Entrar</button>
And in Header.html:
`<div class="page">
Olá {{'usuario'}}, seja bem vindo! Hoje é dia {{today | date:'dd/MM/yyyy'}}
</div>`
The div that is in header.html must be hidden and when I click the button that is in index.html it should be displayed.
One way to do it is that you emit an event wherever the button is:
import { Component, EventEmitter, Output } from '#angular/core';
#Component({
selector: 'btn-component',
template: `<button type="button" class="btn btn-default px-4" [routerLink]="['../home']" (click)="buttonClicked()"> Entrar </button> `
})
export class ButtonComponent {
#Output() buttonClickedEvent = new EventEmitter();
public buttonClicked() { // You can give any function name
this.buttonClickedEvent.emit("click!");
}
}
and then you catch it at the 'div' level:
`<div class="page" [hidden]="!showDiv" (buttonClickedEvent)='showDiv = !showDiv'>
Olá {{'usuario'}}, seja bem vindo! Hoje é dia {{today | date:'dd/MM/yyyy'}}
</div>`
See more about event emitters here

Angular 5 [object HTMLDivElement]

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
}

Angular 4/5 material raised button with input file

I am working on a angular application, currently to upload a file I am using this :
<label class="btn btn-default">
<input type="file" (change)="selectFile($event)">
</label>
<button class="btn btn-success" [disabled]="!selectedFiles"
(click)="upload()">Upload</button>
with methods on my .ts file, it is working well.
I want to upgrade this to material angular components raised button like that right now :
<button mat-raised-button>
<input type="file" (change)="selectFile($event)">
</button>
<button mat-button disabled [disabled]="!selectedFiles" (click)="upload()">Upload</button>
the disabled button is doing well but the input file part doesnt work , it prints baddly and does not open a file folder search window. any ideas?
Won't advise using input field within a button, better you hide the file input and then a button to trigger it. The below example will show a minimal example of it
#Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}} is for Uploading</h2>
</div>
<button mat-raised-button (click)="openInput()">
Select File to Upload
</button>
<input id="fileInput" hidden type="file" (change)="fileChange($event.target.files)" >
<button mat-button [disabled]="!ourFile" (click)="upload()">Upload</button>
`
})
export class App {
name:string;
ourFile: File; // hold our file
constructor() {
this.name = `Angular! v${VERSION.full}`
}
/**
* this is used to trigger the input
*/
openInput(){
// your can use ElementRef for this later
document.getElementById("fileInput").click();
}
fileChange(files: File[]) {
if (files.length > 0) {
this.ourFile = files[0];
}
}
/**
* this is used to perform the actual upload
*/
upload() {
console.log('sending this to server', this.ourFile);
}
}
Check this plnk
With the above example, you should be able to style your button without distorting HTML semantics