N
Novuโ€ข5mo ago
Luiz Carvalho

renderBody handler in Angular

Hey everyone! ๐Ÿ‘‹ I'm using Novu Inbox in an Angular project and I'm trying to use renderBody to allow rendering notification.body as HTML. In the React docs, I see this example:
<Inbox
applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
subscriber="YOUR_SUBSCRIBER_ID"
renderBody={(notification) => (
<div>
<p dangerouslySetInnerHTML={{ __html: notification.body }} />
</div>
)}
/>
<Inbox
applicationIdentifier="YOUR_APPLICATION_IDENTIFIER"
subscriber="YOUR_SUBSCRIBER_ID"
renderBody={(notification) => (
<div>
<p dangerouslySetInnerHTML={{ __html: notification.body }} />
</div>
)}
/>
In Angular, I tried:
renderBody: (el: HTMLDivElement, notification: Notification) => () => {
console.log('renderBody', el, notification);
},
renderBody: (el: HTMLDivElement, notification: Notification) => () => {
console.log('renderBody', el, notification);
},
But renderBody is never called. Only renderNotification seems to trigger. Is renderBody supported outside of React? Any recommended approach to customize the body rendering in Angular, ideally allowing raw HTML? Thanks a lot! ๐Ÿ™
7 Replies
Dima Grossman
Dima Grossmanโ€ข5mo ago
Hey, unfortuently this is not documented yet, but you can take a look at how our React package does it: https://github.com/novuhq/novu/blob/nv-5948-payload-schema-management/packages/react/src/components/Inbox.tsx#L50 we use the props method of mountComponent, and this is the react renderer implementation: https://github.com/novuhq/novu/blob/next/packages/react/src/components/Renderer.tsx
GitHub
novu/packages/react/src/components/Renderer.tsx at next ยท novuhq/novu
The open-source notification Inbox infrastructure. E-mail, SMS, Push and Slack Integrations. - novuhq/novu
GitHub
novu/packages/react/src/components/Inbox.tsx at nv-5948-payload-sch...
The open-source notification Inbox infrastructure. E-mail, SMS, Push and Slack Integrations. - novuhq/novu
Luiz Carvalho
Luiz CarvalhoOPโ€ข5mo ago
@Dima Grossman Hey, quick update: I confirmed that renderBody is being called in my Angular setup. However, rendering dynamic content inside el does not work โ€” Iโ€™ve tried: el.innerHTML = notification.body Using Renderer2 to create elements and append them Running it within ngZone.runOutsideAngular But the <div> stays empty in the DOM. Is there anything in the Inbox implementation that might prevent manual DOM manipulation in renderBody? Or is there a specific way you recommend rendering dynamic HTML when using Inbox outside of React? Thanks again for your help! ๐Ÿ™
Dima Grossman
Dima Grossmanโ€ข5mo ago
I will be on the computer a bit later, but here are some opus suggestions based on our indexed codebase:
import { Component, Renderer2, ViewContainerRef } from '@angular/core';
import { Novu } from '@novu/js';
import { NovuUI } from '@novu/js/ui';

@Component({
selector: 'app-notifications',
template: '<div #container></div>'
})
export class NotificationsComponent {
constructor(
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef
) {}

ngOnInit() {
const novu = new Novu({
applicationIdentifier: 'YOUR_APP_ID',
subscriberId: 'USER_ID'
});

const novuUI = new NovuUI({
novu,
renderBody: (el: HTMLDivElement, notification) => {
// Clear the element
el.innerHTML = '';

// Create custom content
const content = this.renderer.createElement('div');
const text = this.renderer.createText(`Custom body: ${notification.body}`);
this.renderer.appendChild(content, text);
this.renderer.appendChild(el, content);

// Return cleanup function
return () => {
el.innerHTML = '';
};
}
});
}
}
import { Component, Renderer2, ViewContainerRef } from '@angular/core';
import { Novu } from '@novu/js';
import { NovuUI } from '@novu/js/ui';

@Component({
selector: 'app-notifications',
template: '<div #container></div>'
})
export class NotificationsComponent {
constructor(
private renderer: Renderer2,
private viewContainerRef: ViewContainerRef
) {}

ngOnInit() {
const novu = new Novu({
applicationIdentifier: 'YOUR_APP_ID',
subscriberId: 'USER_ID'
});

const novuUI = new NovuUI({
novu,
renderBody: (el: HTMLDivElement, notification) => {
// Clear the element
el.innerHTML = '';

// Create custom content
const content = this.renderer.createElement('div');
const text = this.renderer.createText(`Custom body: ${notification.body}`);
this.renderer.appendChild(content, text);
this.renderer.appendChild(el, content);

// Return cleanup function
return () => {
el.innerHTML = '';
};
}
});
}
}
Version #2
import {
Component,
ComponentRef,
ViewContainerRef,
ComponentFactoryResolver,
ApplicationRef,
Injector,
EmbeddedViewRef
} from '@angular/core';

@Component({
selector: 'app-custom-notification-body',
template: `
<div class="custom-notification">
<h3>{{ notification.subject }}</h3>
<p>{{ notification.body }}</p>
</div>
`
})
export class CustomNotificationBodyComponent {
notification: any;
}

@Component({
selector: 'app-notifications',
template: '<div #container></div>'
})
export class NotificationsComponent {
private componentRefs: Map<HTMLElement, ComponentRef<any>> = new Map();

constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private appRef: ApplicationRef,
private injector: Injector
) {}

ngOnInit() {
const novuUI = new NovuUI({
renderBody: (el: HTMLDivElement, notification) => {
// Create component
const componentRef = this.componentFactoryResolver
.resolveComponentFactory(CustomNotificationBodyComponent)
.create(this.injector);

// Set data
componentRef.instance.notification = notification;

// Attach to Angular change detection
this.appRef.attachView(componentRef.hostView);

// Get DOM element and append
const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
el.appendChild(domElem);

// Store reference for cleanup
this.componentRefs.set(el, componentRef);

// Return cleanup function
return () => {
const ref = this.componentRefs.get(el);
if (ref) {
this.appRef.detachView(ref.hostView);
ref.destroy();
this.componentRefs.delete(el);
}
};
}
});
}

ngOnDestroy() {
// Clean up all component references
this.componentRefs.forEach(ref => {
this.appRef.detachView(ref.hostView);
ref.destroy();
});
}
}
import {
Component,
ComponentRef,
ViewContainerRef,
ComponentFactoryResolver,
ApplicationRef,
Injector,
EmbeddedViewRef
} from '@angular/core';

@Component({
selector: 'app-custom-notification-body',
template: `
<div class="custom-notification">
<h3>{{ notification.subject }}</h3>
<p>{{ notification.body }}</p>
</div>
`
})
export class CustomNotificationBodyComponent {
notification: any;
}

@Component({
selector: 'app-notifications',
template: '<div #container></div>'
})
export class NotificationsComponent {
private componentRefs: Map<HTMLElement, ComponentRef<any>> = new Map();

constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private appRef: ApplicationRef,
private injector: Injector
) {}

ngOnInit() {
const novuUI = new NovuUI({
renderBody: (el: HTMLDivElement, notification) => {
// Create component
const componentRef = this.componentFactoryResolver
.resolveComponentFactory(CustomNotificationBodyComponent)
.create(this.injector);

// Set data
componentRef.instance.notification = notification;

// Attach to Angular change detection
this.appRef.attachView(componentRef.hostView);

// Get DOM element and append
const domElem = (componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
el.appendChild(domElem);

// Store reference for cleanup
this.componentRefs.set(el, componentRef);

// Return cleanup function
return () => {
const ref = this.componentRefs.get(el);
if (ref) {
this.appRef.detachView(ref.hostView);
ref.destroy();
this.componentRefs.delete(el);
}
};
}
});
}

ngOnDestroy() {
// Clean up all component references
this.componentRefs.forEach(ref => {
this.appRef.detachView(ref.hostView);
ref.destroy();
});
}
}
It will probably won't work, but worth a try hehe and I'll try to setup some local repo a bit later
Luiz Carvalho
Luiz CarvalhoOPโ€ข5mo ago
version 1 works thanks!!!
Dima Grossman
Dima Grossmanโ€ข5mo ago
Great ๐Ÿ˜ It's been a while since i've used angular, even tho started from angularjs 0.12 or something like this hehe If you would love to contribute to the docs quickstart page of angular as a section on rendering custom elements (at the bottom of the quick start we can do advanced section or something.
Luiz Carvalho
Luiz CarvalhoOPโ€ข5mo ago
@Dima Grossman Hi, could you help me with this topic? #isOnline always false in Angular project โ€” how to fix?
Novu_Bot
Novu_Botโ€ข5mo ago
@Luiz Carvalho, you just advanced to level 2!

Did you find this page helpful?