I try to create my own structure directive that can create some DOM structure (button group in my case). I want to create custom tags for wrapper and child and apply custom classes.
I stuck on moment - Output event. I finished directive. It's working. (I make DOM manipulation inside this directive).
But now I can't emit event from this directive 9I found answers that it's because events should be static. like - (click)="some()")
But how I can do it if my child's created dynamically? (For now I use AddEventListener
).
and have this error: Event binding onBtnClick not emitted by any directive on an embedded template
Thanks for any information.
Here is my component code:
import {Component, Injectable, Input, Output, EventEmitter, OnInit, ElementRef} from '@angular/core';
import { BtnGroupChildStructure } from './button_group.directive';
import { Config } from 'appConfig';
/* ------- !Config ---------*/
const MODULE_NAME: string = 'button_group';
const MODULE_PATH: string = `${Config.getProdFolderName()}/shared/components/${MODULE_NAME}`;
@Component({
selector : 'cgm_button_group',
template : `<template *btnGroupChildStruct="config"
(onBtnClick)="t_handleBtnClick($event)"></template>`,
host : {'class': 'button-group-box'},
directives : [
BtnGroupChildStructure
]
})
@Injectable()
export class ButtonGroupComponent implements OnInit {
private _config: Object;
@Input() config(val: Object) {
console.log(val);
Object.assign(this._config, val);
console.log(this._config);
}
@Output onBtnClick = new EventEmitter<string>();
ngOnInit() {
}
private t_handleBtnClick(buttonName: string): void {
this.onBtnClick.emit(buttonName);
}
}
Here is Directive code
import { Directive, Input,
ViewContainerRef, ElementRef, TemplateRef, OnInit, Output, EventEmitter } from '@angular/core'
@Directive({ selector : '[btnGroupChildStruct]'})
export class BtnGroupChildStructure implements OnInit {
private _hostEl;
private _config: Object = {};
@Input() set btnGroupChildStruct(val: Object) {
console.log(val);
Object.assign(this._config, val);
}
@Output() onBtnClick = new EventEmitter<string>();
constructor(
private _templateRef: TemplateRef<any>,
private _viewContainer: ViewContainerRef,
private _elementRef: ElementRef
) {
this._hostEl = this._elementRef.nativeElement.parentElement;
}
ngOnInit() {
//console.log(this._templateRef);
this._generateChilds();
this._hostEl.appendChild(this._generateGroup());
//this._viewContainer.createEmbeddedView(this._templateRef);
}
/**
* _generateChilds -> generate childs structure
* @returns {DocumentFragment}
* @private
*/
private _generateChilds(): DocumentFragment {
let fragment = document.createDocumentFragment();
if (this._config.hasOwnProperty('buttonNamesList')) {
this._config.buttonNamesList.forEach((buttonName) => {
let item = document.createElement(this._config.tagChild);
this._addClasses(item, this._config.childClasses);
item.textContent = buttonName;
this._addClickHandler(item);
fragment.appendChild(item);
})
} else {
throw new Error('Button group Directive. _generateChilds. Can"t get _config buttonNamesList prop');
}
return fragment;
}
/**
* _generateGroup -> generate button group
* @returns {HTMLElement}
* @private
*/
private _generateGroup(): HTMLElement {
let wrapperTag;
if (this._config.hasOwnProperty('tagWrapper')) {
wrapperTag = document.createElement(this._config.tagWrapper);
this._addClasses(wrapperTag, this._config.wrapperClasses);
wrapperTag.appendChild(this._generateChilds());
} else {
throw new Error('Button group Directive. _generateGroup. Can"t get _config tagWrapper prop');
}
return wrapperTag;
}
/**
* _addClasses -> Add classes to el
* @param el (HTMLElement) -> handled Element
* @param classesStr (String) -> string of classes
* @private
*/
private _addClasses(el: HTMLElement, classesStr: string): void {
if (!el || !classesStr) throw new Error('_addClasses. Missed one of parameter');
el.className = classesStr
}
private _addClickHandler(el: HTMLElement): void {
if (!el) throw new Error('_addClickHandler. Non exist element');
el.addEventListener('click', (el) => {
this.onBtnClick.emit(el.textContent);
})
}
}