Issue
I have created a custom component that has many input properties. However, one thing I have noticed when using this component is that many of the input properties are the same within the parent component.
An example usage looks like this:
<yes-no
controlName="isAwesome"
labelKeyBase="MODULE.PAGE_X"
validationKeyBase="VALIDATION.CUSTOM_X"
someOtherProp="true"
></yes-no>
I have been using a library called transloco and noticed some functionality that would be very useful in this situation. Unfortunately, I have no idea how it would be implemented.
https://ngneat.github.io/transloco/docs/translation-in-the-template#utilizing-the-read-input
I'm wondering if there is a simplified example out there that demonstrates how to pass a value from a parent directive to a child component. Basically, my goal is to turn the above code into something like this:
<ng-container *yesNoConfig="labelKeyBase: 'MODULE.PAGE_X'; validationKeyBase: 'VALIDATION.CUSTOM_X'">
<yes-no controlName="isAwesome"></yes-no>
<yes-no controlName="isClean"></yes-no>
<yes-no controlName="isShort"></yes-no>
</ng-container>
Solution
Here's a directive that applies arbitrary attributes to all immediate children of the container:
@Directive({
selector: '[setAttributes]',
})
export class SetAttributesDirective {
@Input('setAttributes') atts: { [key: string]: string } = {};
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
ngOnInit() {
const keys = Object.keys(this.atts);
const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
for (const node of viewRef.rootNodes) {
if (typeof node.setAttribute !== 'function') continue;
const el = node as HTMLElement;
for (const key of keys) el.setAttribute(key, this.atts[key]);
}
}
}
Not very typesafe since viewRef.rootNodes
is type any[]
but what can you do... I put somewhat of a typeguard anyway. The first entry is actually the ng-container
itself, and nested templates / ng-containers will also not have a setAttribute
function, throwing an error without the typeguard. Could definitely be improved, but at least you get the gist.
You'd use it like this:
<ng-container *setAttributes="{ type: 'number', value: '5', min: '0', max: '10' }">
<input />
<input />
<input />
<input />
</ng-container>
Associated docs: https://angular.io/guide/structural-directives
Stackblitz example: https://stackblitz.com/edit/angular-ivy-1eyjlo?file=src/app/app.component.html
For your example you need to get the component instance from the DOM node. There is a function for that, although it's meant for debugging, not sure why it's not part of the regular API: https://angular.io/api/core/global/ngGetComponent
You'll have to tell tsc that the function exists, then you can set your component's properties:
declare global {
interface Window {
ng: { getComponent: (el: Element) => { [key: string]: any } | null };
}
}
@Directive({
selector: '[setInputs]',
})
export class SetInputsDirective {
@Input('setInputs') atts: { [key: string]: any } = {};
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}
ngOnInit() {
const keys = Object.keys(this.atts);
const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
for (const node of viewRef.rootNodes) {
if (!(node instanceof Element)) continue;
const component = window.ng.getComponent(node);
if (component === null) continue;
for (const key of keys) component[key] = this.atts[key];
}
}
}
<ng-container *setInputs="{ labelKeyBase: 'MODULE.PAGE_X', validationKeyBase: 'VALIDATION.CUSTOM_X' }">
<yes-no controlName="isAwesome"></yes-no>
<yes-no controlName="isClean"></yes-no>
<yes-no controlName="isShort"></yes-no>
</ng-container>
Stackblitz: https://stackblitz.com/edit/angular-ivy-mbcw9o?file=src/app/app.component.html
Answered By - Chris Hamilton
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.