Angular interpolate inside a component like mat-checkbox - html

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>

Related

How to pass Angular directive by reference?

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

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

Host Binding rearranges applied classes

I am using #HostBinding('class') to inject classes into the host element. The classes to be injected are generated based on user-supplied parameters. The problem I ran into and I could not find anyone else experiencing is that the classes are applied in an order different from the way I expected them.
For example, having a component defined below:
import {Component, HostBinding, Input} from '#angular/core';
#Component({
selector: '[icon]',
template: `
<ng-content></ng-content>
`
})
export class SuiIconComponent {
#Input() iconType = '';
#HostBinding('class')
get classes(): string {
return [this.iconType, 'icon'].join((' '));
}
}
When I apply the component like shown below:
<div icon iconType="car"></div>
And inspect, I see <div class="icon car"></div> instead of the appropriately formatted <div class="car icon"></div>.
I have tried reversing the array before joining but that did not help either.
Is there any way I get the classes to get rendered in the proper order?
Edit: I realized the classes are being rearranged in alphabetic order.
I'm not sure why angular changes the order, but you can solve your problem with little bit of change in your template.
#Component({
selector: 'icon',
template: `
<div [ngClass]="iconType + ' icon'">
<ng-content></ng-content>
</div>
`
})
export class SuiIconComponent {
#Input() iconType = '';
}
and use it as follows
<icon iconType="car">
Some content here
</icon>

Inner HTML of ng-container, Angular 4?

I want to dynamically place an html code in my html code, So I write the following code:
<ng-container [innerHTML]="buttonIcon"></ng-container>
Angular says innerHTML is not valid attribute for ng-container
I don't want to use third html tag like follows:
<div [innerHTML]="buttonIcon"></div>
So how can I insert html codes without any tag inner html binding?
[outerHTML]
will do the trick to replace the outer element.
In your case
<div [outerHTML]="buttonIcon"></div>
It's true, that it's important to have a clean HTML structure for e.g. keeping CSS rules as simple as possible.
You can use ngTemplate:
<ng-template #buttonIcon>
<div> Your html</div>
</ng-template>
<ng-container
*ngTemplateOutlet="buttonIcon">
</ng-container>
** Please read the comments. This answer might be wrong. I dont know, have not looked into it again **
ng-container does not get rendered to html, it is a mere structural directive.
The Angular is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.
So there is no element to put html into. You need to work with a sub-div. If there is no need for a sub-div in your opinion, then you could most probably also just replace ng-container with div itself and not use the container at all.
If for any reason you need to replace the DOM element you can use a div with an id and then use the #ViewChild decorator and ElementRef to get access to the nativeElement from the controller and set the outerHtml property.
app.component.tml
<div #iconButton></div>
app.component.ts
import { Component, ViewChild, ElementRef, ViewEncapsulation, AfterViewInit }from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ],
encapsulation: ViewEncapsulation.None
})
export class AppComponent implements AfterViewInit{
#ViewChild('iconButton')
iconButton: ElementRef;
ngAfterViewInit(){
this.iconButton.nativeElement.outerHTML = '<button>My button</button>'
}
}
We need to use none as encapsulation policy because our template only includes the div to be replaced.
Stackblitz example: https://stackblitz.com/edit/angular-fa1zwp

How do you pass the element in Angular2 Typescript binding?

How do I get the specific HTML dom element passed through a binding. Sorry if this is hard to understand, here's the code.
donut-chart.html
<div class="donut-chart" (donut)="$element"></div>
How do I pass the div element to donut?
This is the TS
dount-chart.ts
#Component({
selector: 'donut-chart',
template: require('./donut-chart.html'),
directives: [Widget, MorrisChart],
encapsulation: ViewEncapsulation.None,
styles: [require('./donut-chart.scss')]
})
export class DonutChart implements OnChanges{
#Input() height: number = 300;
#Input() data: any[] = [{label: 'loading', value: '0', color: '#eee'}];
morris3Options: any;
donut: any;
render(): void {
jQuery(this.donut).css({height: this.height}); // safari svg height fix
window['Morris']['Donut']({element: this.donut}, this.morris3Options);
}
I want the donut variable to be the element without using class/id. I need to use multiple donuts and jQuery, so I need a way to get the unique donut.
<div #someElement></div>
<div class="donut-chart" (donut)="someElement"></div>
it can also be the current element
<div #someElement class="donut-chart" (donut)="someElement"></div>