|
import {AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, ViewEncapsulation,} from '@angular/core'; |
|
import {LemonadeComponent} from "@elements/base/lemonade-component"; |
|
import {InputConverter} from "@elements/base/input-converter"; |
|
import {ElementReady} from "@elements/base/element-ready.interface"; |
|
import get from "lodash.get"; |
|
import {DomSanitizer} from "@angular/platform-browser"; |
|
|
|
@Component({ |
|
templateUrl: './test.component.html', |
|
styleUrls: ['../../scss/base.scss', './test.component.scss'], |
|
encapsulation: ViewEncapsulation.Emulated |
|
}) |
|
export class Test extends LemonadeComponent implements ElementReady, AfterViewInit { |
|
@Input() @InputConverter() data: any[]; |
|
|
|
private COLUMN_NAME_ATTRIBUTE: string = 'column'; |
|
private CELL_VARIABLE_ATTRIBUTE: string = 'var'; |
|
|
|
private columns: any[]; |
|
private isElementReady: boolean; |
|
|
|
constructor(protected $el: ElementRef, protected $cd: ChangeDetectorRef, private _sanitizer: DomSanitizer) { |
|
super($el, $cd); |
|
this.isElementReady = false; |
|
} |
|
|
|
ngAfterViewInit(): void { |
|
// console.log(`@ViewChildren: ${this.tmp1.toArray().length}, @ContentChildren: ${this.tmp2.toArray().length}`) |
|
} |
|
|
|
onElementReady() { |
|
let columnDefs: HTMLElement[] = Array.from( |
|
this.$el.nativeElement.querySelectorAll(`[${this.COLUMN_NAME_ATTRIBUTE}]`) |
|
|| []); |
|
|
|
columnDefs.forEach(a => console.log(a.outerHTML, a.querySelector('td'))) |
|
this.columns = columnDefs.map(columnDef => ({ |
|
name: columnDef.getAttribute('column'), |
|
headerTemplate: columnDef.querySelector('th'), |
|
cellTemplate: columnDef.querySelector('td') |
|
})); |
|
|
|
this.isElementReady = true; |
|
/* |
|
let headerContainer = this.$el.nativeElement.shadowRoot.querySelector('thead'); |
|
this.renderHeaders(headerContainer, columns); |
|
|
|
let bodyContainer = this.$el.nativeElement.shadowRoot.querySelector('tbody'); |
|
this.renderBody(bodyContainer, columns); |
|
*/ |
|
} |
|
|
|
private renderHeaders2() { |
|
if (this.isElementReady) { |
|
let headers = this.columns.map(column => column.headerTemplate.outerHTML); |
|
return this._sanitizer.bypassSecurityTrustHtml(`<tr>${headers.join('')}</tr>`); |
|
} |
|
} |
|
|
|
private renderBody2() { |
|
if (this.isElementReady) { |
|
let rows = this.data.map(data => this.evaluateRow(this.columns, data)); |
|
return this._sanitizer.bypassSecurityTrustHtml(rows.join('')); |
|
} |
|
} |
|
|
|
private renderHeaders(target, columns) { |
|
let headers = columns.map(column => column.headerTemplate.outerHTML); |
|
target.innerHTML = `<tr>${headers.join('')}</tr>`; |
|
} |
|
|
|
private renderBody(target, columns) { |
|
let rows = this.data.map(data => this.evaluateRow(columns, data)); |
|
target.innerHTML = rows.join(''); |
|
} |
|
|
|
/** |
|
* Evaluate a full table row from columns templates |
|
* and the row data |
|
* @param columns All columns templates |
|
* @param data Row data |
|
*/ |
|
private evaluateRow(columns, data): string { |
|
let cells = columns.map(column => { |
|
let template = column.cellTemplate.outerHTML; |
|
|
|
let templateVariable = { |
|
name: column.cellTemplate.getAttribute(this.CELL_VARIABLE_ATTRIBUTE), |
|
value: data |
|
}; |
|
|
|
let bindings = this.findBindings(template, templateVariable); |
|
|
|
// Replace bindings in the cell template |
|
return bindings.reduce((evaluatedTemplate, binding) => |
|
evaluatedTemplate.replace(new RegExp(`%${binding.token}%`), binding.value), |
|
template |
|
); |
|
}); |
|
|
|
return `<tr>${cells.join('')}</tr>`; |
|
} |
|
|
|
/** |
|
* Find expressions token in the template |
|
* and evaluate their value from the template variable |
|
* |
|
* The token syntax is anything accepted by lodash get function |
|
* wrapped between percent signs (ex: %object.list[0].property%) |
|
* |
|
* @param template containing expressions tokens |
|
* @param templateVariable |
|
*/ |
|
private findBindings(template, templateVariable) { |
|
let tokenPattern = /%([^%]+)%/g; |
|
return [...template.matchAll(tokenPattern)] |
|
.filter(matching => matching.length > 1) |
|
.map(([, token, ]) => ({ |
|
token, |
|
// When no value for a token: the value is the token itself |
|
value: this.evaluateToken(token, templateVariable.name, templateVariable.value) || `%${token}%` |
|
})); |
|
} |
|
|
|
/** |
|
* Get token value from then given variable |
|
* |
|
* For a global variable: element = { message: 'hello' } |
|
* the token "element.message" is evaluated to 'hello' |
|
* |
|
* @param token Expression token |
|
* @param variableName Global variable name |
|
* @param variableValue Global variable value |
|
*/ |
|
private evaluateToken(token, variableName, variableValue) { |
|
// Remove variable name from token |
|
let tokenValuePath = token.replace(new RegExp(`${variableName}.`), ''); |
|
return get(variableValue, tokenValuePath); |
|
} |
|
} |