How to pass Angular directive by reference? - angularjs-directive

In an existing component template I have this (simplified) element:
<input type="button" #refreshPrice />
This is picked up (I don't know the correct term) by this property so we can subscribe to it's click event and call a function when the input element is clicked.
I want to replace this input element with a component I've developed, which would make it look (simplified) like this:
<spinner-button #refreshPrice></spinner-button>
This child component has this as its (simplified) template:
<button>
<mat-spinner></mat-spinner>
</button>
So now the button element, in the child component template, needs to have the #refreshPrice hash attribute (?) attached.
To do this, perhaps the spinner-button element should take the name of the hash attribute as an attribute value. Here is the complete spinner component class file:
import { Component, Input, OnInit } from "#angular/core";
#Component({
selector: "spinner-button",
templateUrl: "./spinner-button.component.html",
styleUrls: ["./spinner-button.component.css"]
})
export class SpinnerButtonComponent implements OnInit {
constructor() {}
#Input() targetElement: string;
ngOnInit() {
}
}
In theory, the targetElement property can then be attached to the button element as a hash attribute - but how is this done?

the #Input() attribute here allows you to bind a value to a variable on your component, if you want to have the parent do something based on your components data, you might want to use #Output() and emit a custom event. If the requirement is just listen to a click event then adding a (click)=functionToBeCalled() should help your cause here.
You can refer to the official docs as well:
https://angular.io/guide/inputs-outputs

Related

Angular Parent to Child Data Sending

How to send data on a button click event from parent to child ?
I was sending data from parent to child using a button click event but i unable to send
In Angular, you can send data from a parent component to a child component using property binding:
// Parent component
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
<app-child [message]="message"></app-child>
<button (click)="sendMessage()">Send Message</button>
`
})
export class ParentComponent {
message = 'Hello from parent';
sendMessage() {
this.message = 'Message sent from parent';
}
}
// Child component
import { Component, Input } from '#angular/core';
#Component({
selector: 'app-child',
template: `
<p>{{ message }}</p>
`
})
export class ChildComponent {
#Input() message: string;
}
ParentComponent has a property message that is bound to a message input in the ChildComponent using the square bracket syntax [message]. The ParentComponent also has a sendMessage method that changes the value of message when a button is clicked. The ChildComponent displays the value of message in its template. When the button is clicked, the message value is updated in the ParentComponent and automatically passed down to the ChildComponent through property binding.
You can solve this in couple of ways:
use #Input() someVariable in the child component then
<app-parent>
<app-child [someVariable]="dataOnClickChange"> <app-child>
</app-parent>
that is good if the data on the click is changing.
use service and listen for changes/events. Just subscribe to it and in order to pass the click you can use observable or behavioralSubject with next()
More information on:
data binding
observable
behavioralSubject
Live example
why you need to pass the click event to child component in the first place maybe there is a better way to accomplish what you need.

Angular change html tag programmatically when inheriting template

I have a Component, DataGrid, which represents a table with expandable rows. Each row, when expanded, shows a child DataGrid table, which is very similar to the parent DataGrid component.
Therefore I defined a base class DataGridComponent, from which the child inherits the both the component and the template. however, I need to change one of the tags in the child's template. Do I have to rewrite the entire template, or could I just point the templateUrl to the parent's template and programmatically change the one html tag that I need to change?
Minimal Example:
base.component.ts
#Component({
selector: 'datagrid',
templateUrl: 'datagrid.component.html'
})
export class DataGridComponent {
childEnabled:boolean = true;
// stuff
}
datagrid.component.html
<div>...</div>
<div *ngIf="childEnabled">
<childgrid
[options]="childOptions"
>
</childgrid>
</div>
child.component.ts
#Component({
selector: 'childgrid',
templateUrl: 'datagrid.component.html' // <-- POINT TO BASECLASS TEMPLATE
})
export class ChildGridComponent extends DataGridComponent{
}
childgrid.component.html // <-- HOW THE (REAL) TEMPLATE SHOULD BE
<div>...</div>
<div *ngIf="childEnabled">
<grandchildgrid
[options]="childOptions"
>
</grandchildgrid>
</div>
grandchild.component.ts
#Component({
selector: 'grandchildgrid',
templateUrl: 'datagrid.component.html' // <-- POINT TO BASECLASS TEMPLATE
})
export class GrandChildGridComponent extends DataGridComponent{
constructor() {
super();
childEnable=false;
}
}
grandchildgrid.component.html // <-- HOW THE (REAL) TEMPLATE SHOULD BE
<div>...</div>
<div *ngIf="childEnabled">
<grandchildgrid
[options]="childOptions"
>
</grandchildgrid>
</div>
and so on until childEnabled is set to false. Is there any chance to do something like this and is it something that would make sense from an angularly point of view? Would ng-content be of any help in this case?
The content of DataGrid can go into a separate component and that can be used as a template in both parent and child DataGrid.
Alternative option is to have the same tags and control behavior using different class and id for parent and child

Angular interpolate inside a component like mat-checkbox

So I want to have a mat-checkbox component with a HTML string inside the label.
I tried the following:
<mat-checkbox class="check">
{{ someHtml }}
</mat-checkbox>
But it prints the HTML string as a string and doesn't render it.
Using the following doesn't work either:
<mat-checkbox class="check" [innerHtml]="someHtml">
</mat-checkbox>
This just replaces the whole content, including the checkbox that gets generated at runtime. Is there any way to inject the html into the label?
You could use Angular Directives
The idea here is to fetch the element from the HTML, then append some raw HTML dynamically.
Supose this scenario
app.component.html
<mat-checkbox class="check" [appendHtml]="innerHtml"></mat-checkbox>
app.component.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
innerHtml = `<div style="border: 1px solid red;"> Text inside </div>`;
constructor() {}
}
As you can see, I added a appendHtml attribute to the mat-checkbox element. This is a custom directive that expects a string as "raw" HTML.
append-html.directive.ts
#Directive({
selector: '[appendHtml]'
})
export class AppendHtmlDirective implements AfterViewInit {
#Input('appendHtml') html: string
constructor(private element: ElementRef) {
}
ngAfterViewInit() {
const d = this.element.nativeElement.querySelector('label');
d.insertAdjacentHTML('beforeend', this.html);
}
}
The AppendHtmlDirective expects an html property of type string and implements AfterViewInit interface (from Angular) to fetch the element once it is rendered. By injection, Angular provides us the element which is being applied; so, the ElementRef from the constructor is our MatCheckbox element, in that case.
We can use the insertAdjacentHTML function to append childs to the element. I just fetched the label element from the MatCheckbox to fit inside of it. In every case, you should see where to append the HTML.
I mean, label here works, bc MatCheckbox has a tag whitin matching that. If you want to reuse this Directive for other elements, you should be passing the literal to find inside.
i.e.:
append-hmtl.directive.ts
// ...
#Input() innerSelector: string
// ...
ngAfterViewInit() {
const d = this.element.nativeElement.querySelector(this.innerSelector);
d.insertAdjacentHTML('beforeend', this.html);
}
app.component.hmtl
<mat-checkbox class="check" [appendHtml]="innerHtml" innerSelector="label"></mat-checkbox>
Moreover, you can pass as many inputs as you need to customize the styling or behavior of your directive.
Cheers
I think you should just wrap everything in a div and put it on the outside.
<div>
<mat-checkbox class="check"> </mat-checkbox>
{{ someHtml }}
</div>

When a ViewContainerRef is injected into a directive what element is it bound to?

When a ViewContainerRef is injected into a directive what element is it bound to?
For example if we have a template:
template `<div><span vcdirective></span></div>`
And the constructor for the vcdirective looks like this:
constructor(vc: ViewContainerRef) {
}
Is the element that the vc:ViewContainerRef is bound to the span element?
Yes, it will bind to span element. If you console the vc in constructor, you will see ViewContainerRef object, check the element property of it, you will find span there. But when you will attach the view in the container, it will be appended next to span instead of being inserted inside it and span will be left hanging.
Yes, it's bound to span. ViewContainerRef represents a container where one or more views can be attached. We can use its methods such as createEmbeddedView() and createComponent(). ViewContainerRef is used to build dynamic components.
please check this to better understand. it is a placeholder that can attach to another elemetent. check https://netbasal.com/angular-2-understanding-viewcontainerref-acc183f3b682
something like:
#Component({
selector: 'vcr',
template: `
<template #tpl>
<h1>ViewContainerRef</h1>
</template>
`,
})
export class VcrComponent {
#ViewChild('tpl') tpl;
constructor(private _vcr: ViewContainerRef) {
}
ngAfterViewInit() {
this._vcr.createEmbeddedView(this.tpl);
}
}

How to access html elements of component tag?

I want to access plain HTML declared in my component tag. Suppose I have component
#Component({
selector: 'app-demo'
template: '<some_template></some_template>'
})
export class AppDemoComponent {
}
if I am defining h1 inside the tag in another component
<app-demo>
<h1> demo text </h1>
</app-demo>
How can I access the h1 element inside the AppDemoComponent?
Edit:
This question is not about ViewChild as ViewChild gets information from the current template of the component. I'm asking if the component tag is called in the different file and the tag has HTML elements then how to access it.
Use ElementRef
You can use ElementRef to access the current component reference, allowing you to query it for nested elements.
getElementsByTagName, querySelectorAll, and getElementsByClassName will look down into the nested elements as they operate by inspecting what's rendered in the DOM, ignoring any encapsulation Angular does.
I am not sure if there is an Angular specific way to do it, but using vanilla JS lets you get there.
Child Component
import { Component, OnInit } from "#angular/core"
#Component({
selector: 'app-demo-child'
template: `
<h1> demo text </h1>
<h1 class="test"> demo text2 </h1>
<h1> demo text3 </h1>
`
})
export class AppChildComponent {
}
Parent Component
import { Component, OnInit, ElementRef } from "#angular/core"
#Component({
selector: 'app-demo-parent'
template: `
<app-demo-child></app-demo-child>
`
})
export class AppParentComponent {
constructor(
private elRef:ElementRef
) { }
doStuff() {
let HeaderElsTag = this.elRef.nativeElement.getElementsByTagName('h1') // Array with 3 h3 elements
let HeaderElsQuer = this.elRef.nativeElement.querySelectorAll('h1') // Array with 3 h3 elements
let HeaderElsClass = this.elRef.nativeElement.getElementsByClassName('test') // Array with 1 h3 element
}
}
Warning, this will look indiscriminately within your component, so be careful that you don't have nested elements with the same class name otherwise you'll have some hard to debug side effects
You can use content children. for your reference please follow the link below:
content children vs view children