Reload some directives after generated html code - html

I'm trying to create some html dynamically with angular framework from ngOnInit function. I want to add events thanks to directives on this generated html. The fact is that all directives are loaded before html generation and I didn't succeed to reload one of these.
I'm generating html in my components with Renderer2:
import { Component, AfterViewInit, ElementRef, ViewChild, Renderer2 } from '#angular/core';
import { AngularDraggableDirective } from 'angular2-draggable';
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.css']
})
export class TestComponent {
#ViewChild("myDiv", {static: false}) divView: ElementRef;
private myDiv: ElementRef;
constructor(private el: ElementRef, private renderer: Renderer2) {
this.myDiv = el;
}
ngOnInit(){
let div = this.renderer.createElement('div');
let text = this.renderer.createText('Generated draggable');
this.renderer.setAttribute(div, 'ngdraggable', '');
this.renderer.appendChild(div, text);
this.renderer.appendChild(this.myDiv.nativeElement, div);
}
}
And associate html is really simple:
<div #myDiv>
</div>
<div ngDraggable>Not generated draggable</div>
The first div (generated one) isn't draggable.
This second (initial one) is draggable.
Is there any way to reload my AngularDraggableDirective to set events on my generated div?
Thanks

You should write the code in ngafterviewinit method.

Related

Angular: sanitizer.bypassSecurityTrustHtml does not render attribute (click)

I try to render a button and it works fine, but when I click the button it doesn't execute alertWindow function, help!:
app.component.ts:
import {
Component,
ElementRef,
OnInit,
ViewEncapsulation } from '#angular/core';
import { DomSanitizer, SafeHtml } from "#angular/platform-browser";
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
encapsulation: ViewEncapsulation.ShadowDom,
})
export class AppComponent implements OnInit {
public content: SafeHtml;
constructor(private sanitizer: DomSanitizer) {}
async ngOnInit() { this.renderButton(); }
alertWindow() { alert("don't work"); }
renderButton() {
this.content =
this.sanitizer.bypassSecurityTrustHtml(`
<button (click)='connectWallet()' class="button">
Connect your wallet
</button>`);
}
app.component.ts;
<div [innerHTML]="content"></div>
Solution
Based on what I understand you wanted to display HTML dynamically at runtime? then solution is to use
ComponentFactoryResolver
and ViewContainerRef
It will be better if you can provide more details, what you are trying to achieve, so that people can guide you
Why it didn't work?
It doesn't work because it is outside of angular, when you use innerHTML then whatever you passed to it is pure vanilla HTML and JavaScript
Try this example
(window as any).alertWindow = function () {
alert("don't works");
};
#Component({...})
export class AppComponent {
...
renderButton() {
this.content = this.sanitizer.bypassSecurityTrustHtml(`
<button onclick='alertWindow()' class="button">Connect your wallet</button>
`);
}
}
It works right?
As you can see I have moved alrertWindow function outside of component's class and added to window variable and also changed (click) to onclick

Component Interaction #Input

I would like a component to send input to another component. Below is the code .ts and .html. of the two components.
Now the problem is that the html page of the parent component also shows the html part of the child component ... I want the component to pass only one string to the child component
Parent.ts
import ...
#Component({
selector: 'app-parent',
templateUrl: './parent.html',
styleUrls: ['./parent.css']
})
export class ParentComponent implements OnInit {
sostegno : string;
constructor() { }
ngOnInit() { }
avvia1() {
this.sostegno = "xxx";
this.router.navigate(['./xxx'], { relativeTo: this.route });
}
avvia2()
this.sostegno = "yyy";
this.router.navigate(['./yyy'], { relativeTo: this.route });
}
}
Parent.html
<div>
...
</div>
<app-child [sostegno]="sostegno"></app-child>
Child.ts
import ...
#Component({
selector: 'app-child',
templateUrl: './child.html',
styleUrls: ['./child.css']
})
export class ChildComponent implements OnInit {
#Input() sostegno : string;
constructor() { }
ngOnInit() {
console.log(this.sostegno);
}
}
There are some changes which you need to make because looking at the code which your currently have it seems incomplete.
You are using this.router without injecting the Router class in your constructor.
You are using this.route without injecting the ActivatedRoute class in your constructor.
To test that your parent > child interaction is working you can remove your param and instead place a test for the html
<app-child [sostegno]="'Test'"></app-child>
This should work for your ngOnInit function which is inside of your child component. If this works all you need to do now is either initialize sostegno in your parent component else your console log inside your child component will not reflect the changes when you call avvia1 or avvia2 inside of your parent class.
Hope this helps!

Open modal form containing form created from ngx-formly from another ngx-formly form

I'm currently using ngx-formly to dynamically create a bunch of Angular forms from JSON, which works really nicely. I have a peculiar use case where a custom button on a form, should open a modal dialog containing another form on click, which would also contain a form created using ngx-formly. The example I saw on the ngx-formly site use a custom button, and creates a custom component with .ts files, but I want to avoid that since I would have several forms doing this, and I don't want to create different components for this.
Is there a way to trigger a modal dialog from an ngx-formly form, to show the modal with ngx-formly form without having to create multiple components(.ts) files for them?
Common Bootstrap Model with dynamic data
Example with jQuery:
https://stackblitz.com/edit/ngx-bootstrap-fh92s3
modal.service.ts
import {Injectable} from '#angular/core';
import {ModalModel} from './modal.model';
import {Subject} from "rxjs/Subject";
declare let $: any;
#Injectable()
export class ModalService {
modalData = new Subject<ModalModel>();
modalDataEvent = this.modalData.asObservable();
open(modalData: ModalModel) {
this.modalData.next(modalData);
$('#myModal').modal('show');
}
}
modal.component.ts
import { Component } from '#angular/core';
import { ModalService } from './modal.service';
import {ModalModel} from './modal.model';
declare let $: any;
#Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
styleUrls: [ './modal.component.css' ]
})
export class ModalComponent {
modalData: ModalModel;
constructor(private modalService: ModalService) {
this.modalService.modalDataEvent.subscribe((data) => {
this.modalData = data;
})
}
}
calling this service from any component
import { Component } from '#angular/core';
import { ModalService } from '../modal/modal.service';
import { ModalModel } from '../modal/modal.model';
declare let $: any;
#Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: [ './home.component.css' ]
})
export class HomeComponent {
modelData = new ModalModel();
constructor(private modalService: ModalService) {
}
open() {
this.modelData.header = 'This is my dynamic HEADER from Home component';
this.modelData.body = 'This is my dynamic BODY from Home component';
this.modelData.footer = 'This is my dynamic footer from Home component';
this.modalService.open(this.modelData);
}
}
Example without jQuery i.e with ngx-bootstrap: https://stackblitz.com/edit/angular-ngx-bootstrap-modal

Create new Child Component within a Child Component

I have an issue about creating new Components with the resolveComponentFactory. First there is a startComponent (parent component) and from this component there are several buttons and every btn creates an new child Component. For example now I create a "childComponent" and this also works. But now I want to create an new childComponent within the childComponent and this new component shall have the startComponent as parent component, not the childComponent itself. So I Need a way to call the addComponent() method from the startComponent with my childComponent.
Here is the way I'm doing it at the moment:
startComponent.ts:
import {
Component, OnInit, ViewChild,
ComponentFactoryResolver,
ViewContainerRef
} from '#angular/core';
Import { childComponent } from '../childComponent/child.component';
import { DataService } from '../data.service';
#Component({
selector: 'app-start',
templateUrl: './start.component.html',
styleUrls: ['./start.component.css']
})
export class startComponent implements OnInit {
#ViewChild('parent', { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private dataService: DataService, private componentFactoryResolver: ComponentFactoryResolver){}
ngOnInit(){}
addComponent(){
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(childComponent);
let component = this.container.createComponent(componentFactory);
// next Line i save the reference of the "childComponent" to a Service
// so the "childComponent" can get it and destroy himself if wanted
this.createComponentService.setReference(component, type);
}
}
startComponent.html
<div>
<span matTooltip="Select"><button (click)="addComponent()" class="btn">
<img class="img" src="./assets/StartIcons/childCreate-icon.png" alt="not found"> </button></span>
</div>
<div #parent></div>
It does work if I want to create a childComponent with my childComponent, but the startComponent is not the parent component then.
I hope you understand my Problem, else I can try to explain it again.
But now I want to create an new childComponent within the
childComponent and this new component shall have the startComponent as
parent component, not the childComponent itself. So I Need a way to
call the addComponent() method from the startComponent with my
childComponent.
So, in childComponent you should have reference to parentComponent(StartComponent). You can get it by injecting to new added childComponent:
childComponent:
constructor(private parentComp: StartComponent){
}
As you have reference to it, you get access to properties, methods of parent and within childComponent can call addComponent() easily like:
parentComp.addComponent();
Update
Interesting, dynamically created component doesn't have parent component in injector. So, it can't inject parent StartComponent.
Another solution
Set child component's parent property with StartComponet:
ngOnInit() {
this.comps.clear();
let aComponentFactory =
this.componentFactoryResolver.resolveComponentFactory(this.compArr[0]);
let aComponentRef = this.comps.createComponent(aComponentFactory);
(<AComponent>aComponentRef.instance).name = 'A name';
(<AComponent>aComponentRef.instance).parent = this;
}
StackBlitz Demo. Look at the console
Manually inject parent component in child:
constructor(public injector: Injector ) {
console.log('child injector', injector);
this.parent = injector.get(AppComponent);
}
ngOnInit() {
console.log('parent is here', this.parent);
this.parent.test();
}
StackBlitz Demo. Look at the console

Angular html nesting

Let's say I have in some upper level class some angular template code that looks like this
<outer-component>
<a></a>
</outer-component>
Where <a> can be any module that extends a certain interface defined elsewhere, is there a way for <outer-component> be able to take <a> or whatever is placed inside the tags and communicate with it specifically be able to listen to functions or bind to variables in a way that is as succinct as the snippet above?
If you want to share data between a parent and a child (hierarchical relationship) you can use EventEmitter to allow the parent to get data from the child.
In the child component:
import { Component, Input, Output, EventEmitter } from 'angular/core';
#Component({
selector: 'app-child',
template: `
<h3>Child</h3>
Say {{message}}
<button (click)="sendMessage()"></button>
ยด,
styleUrls: ['pathToStyles.css']
})
export class ChildComponent {
message: string = "Hello world";
#Output() messageEvent = new EventEmitter<string>();
constructor() {}
sendMessage() {
this.messageEvent.emit(this.message);
}
}
In the parent component:
import { Component } from '#angular/core';
#Component({
selector: 'app-parent',
template: `
Message: {{message}}
<app-child (messageEvent)="receiveMessage($event)"></app-child>
`,
styleUrls: ['pathToStyles.css']
})
export class ParentComponent {
constructor() { }
message:string;
receiveMessage($event) {
this.message = $event
}
}