Render filter component in a dialog - javascript

I'm trying render filter(s) in a mat-dialog, I came to a point where it is rendered correctly, but functionally not usable and I'm not sure if this is the right way or not.
here is the code
here is how I create and open the dialog:
public lol(colDef: ColDef): void {
console.log(colDef);
this.gridApi?.getFilterInstance(colDef.field, (foo) => {
console.log(foo.getModel());
foo.setModel(foo.getModel());
this.dialog.open(FilterWrapperDialogComponent, {
data: {
filterHtml: foo.getGui()
},
panelClass: 'ag-custom-component-popup'
}).afterClosed().subscribe((applyOrNot) => {
if (applyOrNot) {
this.gridApi.onFilterChanged();
console.log(foo.getModel());
}
});
})
}
the dialog content:
<mat-dialog-content>
<div class="ag-theme-material ag-custom-component-popup" [innerHTML]="data.filterHtml?.innerHTML | safeUrl: 'html' "></div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button i18n [mat-dialog-close]="true" color="primary">Apply</button>
</mat-dialog-actions>
it renders correctly, but it looses functionality.
My goal is to set a filter without actually adding to the ag-grid table, just through a dialog and ag-grid API.

The problem was that I was using [innerHTML]="data.filterHtml?.innerHTML | safeUrl: 'html' " which strips any functionality for safety reasons ( XSS ).
The solutions was to give an id to the div and append the html from getGui()
Dialog HTML:
<mat-dialog-content>
<div class="ag-theme-material ag-custom-component-popup" id="second"></div>
</mat-dialog-content>
<mat-dialog-actions>
<button mat-raised-button i18n [mat-dialog-close]="true" color="primary">Apply</button>
</mat-dialog-actions>
Appending HTML to the div; dialog logic:
import { Component, ChangeDetectionStrategy, Inject, AfterViewInit } from '#angular/core';
import { MAT_DIALOG_DATA } from '#angular/material/dialog';
#Component({
selector: 'app-filter-wrapper-dialog',
templateUrl: './filter-wrapper-dialog.component.html',
styleUrls: ['./filter-wrapper-dialog.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
})
export class FilterWrapperDialogComponent implements AfterViewInit {
constructor(
#Inject(MAT_DIALOG_DATA) public data: any
) {}
ngAfterViewInit(): void {
document.querySelector('#second').appendChild((this.data.filterHtml as HTMLElement));
}
}
With these changes I was able to render the filter correctly and functionally.

Related

How to Render Child Components Dynamically using loops if possible? (Angular 8)

I am trying to render my angular components dynamically using loops but I'm lost.
What I want to achieve is drag and re-arrange my components (I was able to achieve this using Dragula - ng2-dragula) but I want to save the arrangement to my local storage or session storage.
The tricky part is I am using child components
<section [dragula]="'VAMPIRES'">
<div class="first_element_class" id="first_element">
<my-first-component
[outputData]="outputData"></my-first-component>
</div>
<div class="second_element_class" id="second_element">
<my-second-component
[outputData]="outputData"></my-second-component>
</div>
</section>
I tried using the DOM sanitizer pipe to render them via for loops
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer, SafeHtml } from '#angular/platform-browser';
#Pipe({
name: 'trustHtml'
})
export class TrustHtmlPipe implements PipeTransform {
constructor(readonly sr: DomSanitizer){}
transform(html: string) : SafeHtml {
return this.sr.bypassSecurityTrustHtml(html);
}
}
Updated HTML Code (Let's assume I added my html elements to an array of object)
objectHTML: any[] = [{
toBeRender: `<div class="first_element_class" id="first_element">
<my-first-component
[outputData]="outputData"></my-first-component>
</div>`
}];
<section [dragula]="'VAMPIRES'">
<div *ngFor="let element of objectHTML">
<div [innerHTML]="element.toBeRender | trustHtml" ></div>
</div>
</section>
My question is is it really possible to render the html child components/elements using ngFor? I tried using the DOMSanitizer but I'm only getting a blank page (No error on console)
I have a similar solution for you. Firstly, in your <my-first-component> ts file declare "outputData" as #Input and use ngFor in HTML. Here is my sample code below.
CUSTOM Control
HTML: event.component.html
<h2>Event {{ event.id }}</h2>
<button (click)="event.hidden = true">Close me</button>
TS:event.component.ts
import { Component, Input } from '#angular/core';
import { Event } from './event';
#Component({
selector: 'my-event',
templateUrl: './event.component.html'
})
export class EventComponent {
#Input() event: Event;
}
export interface Event
{
id: number;
hidden: boolean;
}
**HTML:** app.component.html (You can use that code where you need to.)
<button (click)="addEvent()">Add event</button>
<div *ngFor="let event of events">
<my-event *ngIf="!event.hidden" [event]="event"></my-event>
</div>
Note: Here [event] is the #Input for your custom control and *ngFor is the loop.
TS: app.component.ts
import { Component } from '#angular/core';
import { Event } from './event';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
events: Array<Event> = [1, 2, 3, 4, 5].map(i => ({id: i, hidden: true}));
addEvent() {
const event = this.events.find(e => e.hidden);
if (event) {
event.hidden = false;
}
}
}
Note: Please check the code and let me know. This code is also available in Stackblitz
LINK .
You can iterate with ngFor and render many times the child component by his selector.
components = [1, 2, 3, 4, 5];
<ng-container *ngFor="let component of components">
<child-component></child-component>
</ng-container>

System dialog Angular Material

I work for system of dialog (Angular Material).
I create dialog-service for controll and container dialog. Dialog-service has methods for open/show different dialoges.
And I create dialog-component for contain data for dialog (it is single component of dialog). It is universal component.
I add StackBlitz
I have problem with closing dialoges after callback.
How I can close dialog after callback? I try using [mat-dialog-close] - but I was not able to somehow parameterize - enable and disable the [mat-dialog-close] of different buttons.
And a have little problem. How I can add dynamicly mat-button to button element?
(I add class 'mat-button', but this don't full imitation mat-button)
<div *ngIf="getButtons().length > 0 || getCloseButton()" mat-dialog-actions>
<ng-container *ngFor="let button of getButtons()">
<button [attr.class]="button.class"
(click)="button.callback(button.callbackItem || dialogForm)">{{button.title}}</button>
</ng-container>
</div>
In your dialog.html you must have something like this:
<button mat-stroked-button (click)="closeDialog()">Close</button>
and in your dialog.ts:
import { Component, OnInit, Inject } from '#angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '#angular/material';
#Component({
selector: 'dialog',
templateUrl: './dialog.component.html',
styleUrls: ['./dialog.component.scss']
})
export class DialogComponent implements OnInit {
constructor(public dialogRef: MatDialogRef<DialogComponent>) { }
ngOnInit() {
}
closeDialog() {
this.dialogRef.close();
}
}

How Can I Make Custom Tabs Using Angular Materials Tabs?

I want to use Angular Material library and build my own library with some custom designs. But facing some problems while splitting the material components. I think the problem is with shadow DOM. Here is the code that i want to achieve.
Code
custom-tabs-group.html -parent
<div class="custom-tabs">
<mat-tab-group disableRipple>
<ng-content></ng-content>
</mat-tab-group>
</div>
custom-tabs.html -child
<custom-tabs-group [tabContent]="tabContent">
<mat-tab *ngFor="let tab of tabContent" label="{{tab.title}}">{{tab.content}} </mat-tab>
</custom-tabs-group>
is it even possible? Please let me know
the code you shared got the ng-content usage backwards... the <custom-tabs-group> will be at the parent level and <ng-content> at the child level.
I tried 2 approaches:
strategy #1: pass the content to the custom child inside the <mat-tab>... this worked
strategy #2: pass the content to the custom child where <mat-tab> is inside the child... this didn't work
you can check the demo here
Actually i figured it out with some hack i don't know if its a good approch or not
custom-tabs.component.html
<div class="custom-tabs">
<mat-tab-group disableRipple>
<mat-tab *ngFor="let tab of tabsContentList" label="{{tab.label}}">
<div [innerHTML]="tab.htmlContent"></div>
</mat-tab>
</mat-tab-group>
</div>
custom-tabs-component.ts
import { DomSanitizer } from '#angular/platform-browser';
import { Component, OnInit, ViewEncapsulation, AfterContentInit, ContentChildren, Input, ViewChild, ElementRef, QueryList } from '#angular/core';
#Component({
selector: 'il-tabs-content',
template: `
<div #content>
<ng-content></ng-content>
</div>
`
,
})
export class TabsContentComponent implements OnInit {
#Input() label: String;
#ViewChild('content') set content(content: ElementRef) {
console.log("block three", content)
this.htmlContent = content;
if (this.htmlContent) {
this.htmlContent = this.htmlContent.nativeElement.innerHTML;
}
}
htmlContent: any;
constructor() { }
ngOnInit() {
}
}
#Component({
selector: 'il-tabs-group',
templateUrl: './tabs.component.html',
styleUrls: ['./tabs.component.css'],
encapsulation: ViewEncapsulation.None
})
export class TabsGroupComponent implements OnInit, AfterContentInit {
#ContentChildren(TabsContentComponent) tabsContentList: QueryList<TabsContentComponent>;
constructor(public sanitizer: DomSanitizer) { }
ngOnInit() {
}
ngAfterContentInit() {
this.tabsContentList.forEach((tabInstance) => {
var sanEle = this.sanitizer.bypassSecurityTrustHtml(tabInstance.htmlContent)
tabInstance.htmlContent = sanEle;
return tabInstance
})
}
}
usage
<il-tabs-group>
<il-tabs-content label="hello-1">
<h1>hello-1 content</h1>
</il-tabs-content>
<il-tabs-content label="hello-2">
<h1>hello-2 content</h1>
</il-tabs-content>
<il-tabs-content label="hello-3">
<h1>hello-3 content</h1>
<h2>extra content</h2>
</il-tabs-content>
</il-tabs-group>
i defined two components 'il-tabs-content' and 'li-tabs-group'. with this now i can use my own custom tabs build over angular material tabing with dynamic tabs. Anyone with better approch are welcome to share their ideas. thanks

Angular 5 Dynamic template in MatDialog

in my component I open a MatDialog and pass data to it. In my object data.obj is under this.data.obj.html html-code stored.
In electron I would use a webview to display the html-site.
How do I display the html-code in proper way in my MatDialog in angular 5? Its possible to create the template dynamically or is there any smoother way?
#Component({
selector: 'dialog-popup',
template:`
<h1 mat-dialog-title>Content-HTML</h1>
<mat-dialog-content>
{{this.data.obj.html}}
</mat-dialog-content>
<mat-dialog-actions>
<button mat-button>Complain</button>
<button mat-button (click)=onNoClick()>Cancel</button>
</mat-dialog-actions>
`
})
export class DialogOverview {
constructor(
public dialogRef: MatDialogRef<DialogOverview>,
#Inject(MAT_DIALOG_DATA) public data: any) { }
ngOnInit() {
console.log(this.data.obj.html);
}
onNoClick(): void {
this.dialogRef.close();
}
}
You can bind it to the [innerHtml] property of a html element
<mat-dialog-content>
<div [innerHtml]="data.obj.html | keepHtml"></div>
</mat-dialog-content>
You can use dom sanitizer and write a html pipe like below to escape html sanitizing
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({ name: 'keepHtml', pure: false })
export class KeepHtmlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(content) {
return this.sanitizer.bypassSecurityTrustHtml(content);
}
}

Bindings not working in dynamically loaded component

I'm encountering a problem where if I dynamically load a component, none of the bindings in the template are working for me. As well as this the ngOnInit method is never triggered.
loadView() {
this._dcl.loadAsRoot(Injected, null, this._injector).then(component => {
console.info('Component loaded');
})
}
Dynamically loaded component
import {Component, ElementRef, OnInit} from 'angular2/core'
declare var $:any
#Component({
selector: 'tester',
template: `
<h1>Dynamically loaded component</h1>
<span>{{title}}</span>
`
})
export class Injected implements OnInit {
public title:string = "Some text"
constructor(){}
ngOnInit() {
console.info('Injected onInit');
}
}
This is my first time using dynamically loaded components so I think may be attempting to implement it incorrectly.
Here's a plunkr demonstrating the issue. Any help would be appreciated.
As Eric Martinez pointed out this is a known bug related to the use of loadAsRoot. The suggested workaround is to use loadNextToLocation or loadIntoLocation.
For me this was problematic as the component I was trying to dynamically load was a modal dialog from inside a component with fixed css positioning. I also wanted the ability to load the modal from any component and have it injected into the same position in the DOM regardless of what component it was dynamically loaded from.
My solution was to use forwardRef to inject my root AppComponent into the component which wants to dynamically load my modal.
constructor (
.........
.........
private _dcl: DynamicComponentLoader,
private _injector: Injector,
#Inject(forwardRef(() => AppComponent)) appComponent) {
this.appComponent = appComponent;
}
In my AppComponent I have a method which returns the app's ElementRef
#Component({
selector: 'app',
template: `
<router-outlet></router-outlet>
<div #modalContainer></div>
`,
directives: [RouterOutlet]
})
export class AppComponent {
constructor(public el:ElementRef) {}
getElementRef():ElementRef {
return this.el;
}
}
Back in my other component (the one that I want to dynamically load the modal from) I can now call:
this._dcl.loadIntoLocation(ModalComponent, this.appComponent.getElementRef(), 'modalContainer').then(component => {
console.log('Component loaded')
})
Perhaps this will help others with similar problems.
No need to clean component instance from DOM.
use 'componentRef' from angular2/core package to create and dispose component instance.
use show() to load the modal component at desired location and hide() to dispose the component instance before calling loadIntoLocation secondtime.
for eg:
#Component({
selector: 'app',
template: `
<router-outlet></router-outlet>
<div #modalContainer></div>
`,
directives: [RouterOutlet]
})
export class AppComponent {
private component:Promise<ComponentRef>;
constructor(public el:ElementRef) {}
getElementRef():ElementRef {
return this.el;
}
show(){
this.component = this._dcl.loadIntoLocation(ModalComponent,this.appComponent.getElementRef(), 'modalContainer').then(component => {console.log('Component loaded')})
}
hide(){
this.component.then((componentRef:ComponentRef) => {
componentRef.dispose();
return componentRef;
});
}
}

Categories

Resources