New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom Event rendering using ng-template #204
Comments
please write down how you would like to see it, and at what level? |
I'd like to see support for Angular templates as well, similar to the support for templates/slots/components in the Vue & React plugins in v5. For example, "Angularizing" the Vue example in the v5 changelog, you might have an fcEventContent directive that would render the template in the eventContent Content Injection input:
This would also make it a lot easier to use an Angular component to render events. In v5 right now you have to manually build and destroy the template views... something like this:
|
@daniel-cmd Thanks for your example, I would have liked a better looking solution too. When I use your example, I have to force periodic change detection upon the rendered views: …
export class CalendarComponent
…
ngDoCheck() {
contentRenderers.forEach(r => r.detectChanges());
}
…
} I load a component which has an Do you know a more elegant way to render a fully functional Angular component into an event? This might seem off-topic, but I think the use-case should be considered part of the scope of this issue. In general Angular developers will want to render functional Angular components inside an event. EDIT: Besides that, I had to change the return of |
I tried this approach, but it didn't work either. This is based on https://angular.io/guide/dynamic-component-loader @ViewChild(AdDirective, { static: true }) eventComponentHost: AdDirective;
private readonly contentComponents = new Map<string, ComponentRef<EventContentComponent>>();
// Arrow notation so we don't have to wrap it to preserve the this scope added to calendar options
renderEventContent = (arg) => {
let componentRef = this.contentComponents.get(arg.event.id);
if (!componentRef) {
// Make a new component and save it so we can destroy it when the event is unmounted.
componentRef = this.eventComponentHost.viewContainerRef.createComponent(
this.componentFactoryResolver.resolveComponentFactory(EventContentComponent)
);
}
componentRef.instance.inputEvent = arg.event.extendedProps.event;
// Trigger component loading
componentRef.changeDetectorRef.markForCheck();
componentRef.changeDetectorRef.detectChanges();
// Update once entity has been emitted to view.
componentRef.instance.event$.pipe(timeout(10000), first()).subscribe(() => {
componentRef.changeDetectorRef.markForCheck();
componentRef.changeDetectorRef.detectChanges();
});
return { domNodes: [componentRef.location.nativeElement] };
}
unrenderEvent = (arg) => {
this.contentRenderers.get(arg.event.id)?.destroy();
this.contentRenderers.delete(arg.event.id);
} For some reason this works even worse. Change detection never fires even once on the dynamic component. |
Thanks @Ghostbird, I wrote up the example quickly based on some work I had done to integrate with version 3, so I didn't check the return value carefully enough. I think that the below service works better with regards to change detection. I originally wrote it for v3, but I adapted it to v5 by just having it return the root nodes, which you can add into an object and return from eventContent. By adding it as a provider in the component that hosts the FullCalendar and injecting it, you get access to that component's ViewContainerRef, which means that the views that we return are part of that component's change detection tree. I send in a comparator so that I can compare our internal appointment reference (as FullCalendar was returning a clone of its Event so it always appeared to change). I haven't tried this with async, but dynamic context menus, ng-bootstrap popovers & tooltips, and dynamic FontAwesome icons all seem to work OK (since I set the data for the icons in the hosting component, I call There definitely might be better ways of handling this, but this seems to work OK for our use cases.
|
@daniel-cmd Thanks a lot! This was the crucial part that we needed:
Apparently this doesn't work well with the viewContainerRef from the AdDirective, but works perfectly when using your method. EDIT: I'm running into a curious issue now. Moving an event on the calendar fires the EDIT: I've managed to solve that issue by using a hash over EDIT: In the end I included the |
I'm trying to achieve the exact thing - rendering Angular components as events, but I need to support dragging and resizing. The problem is, when I drag the element far enough to trigger mirror rendering (the overlay going after the pointer) then the custom HTML disappears - rendered event is 'empty' and it stays like this until 'eventContent' callback is triggered. It doesn't occur for plain HTML nodes, only for templates and components. Am I missing something or whole method has a flaw? It would be nice to see declarative templates, but this functionality need to support plugins like 'interaction'. My experiments sadly didn't succeed. |
facing same issue when drag the event where it started, empty event is rendered, forcedly refresh the dragging event with mentioned code, because eventDrop triggered not fired due to same day/time.
let me know, if any better solution to handle this situation. Thanks |
@irustm @arshaw it is very much needed. The ng-template approach is common for many Angular applications. For example, I am using angular-callendar npm package, which has eventTemplate input: The usage is pretty much straightforward:
I understand that you have to connect non-angular to angular app and cannot use pure ngTemplateOutlet, but probably method in this section can help render custom angular template. |
@muhammadumairaslam facing the same issue but your solution did not work for me somehow, did you find a better solution |
There are two problems with drag & drop and angular templates:
Our solution to this is to alter the TemplateHelperService to not actively destroy views, but rather move them to something like a Then, it is paramount you pass the correct ID of the view to the helper service. In our case, we keep a separate view for the actively dragging item (which you can identify from the In our case, this means that dragging the event around the calendar will destroy + recreate the view in some cases (for example, when using resources and switching lanes), but I found that this is not too heavy in my testing. It can surely be optimized to only recreate the view when we identify FC deleted/detached the DOM nodes You can find our alterated implementation of the TemplateHelperService here: https://github.com/opf/openproject/blob/dev/frontend/src/app/features/team-planner/team-planner/planner/event-view-lookup.service.ts and the changes leading up to that implementation here: opf/openproject#10146 |
The solution is what I mentioned above: #204 (comment) I include the start-time of the event and the timeText in the hash that's used as identifier for the view in the template service. That way, when an event is moved, the previous view and the new view are uniquely identifiable and you avoid any creation/destruction order issues. |
This has been implemented in v6.0.0-beta.3 |
Could people please try out the new |
That's excellent news! Thanks for working on that. I've planned to look at the beta for Angular 15 support. I'll give this a go tomorrow and let you know. Do you want feedback here or prefer some other means? |
I'm sorry, I'd love to try this, but I can't do it right now. When FullCalendar 6 dropped Angular support, I removed this library from our code. I'll re-add it now that Angular support is back and this feature has landed. When I removed it, I realised that we didn't actually have much use for the Angular bindings that this library added. However the Angular template integration is something we use a lot. I'm currently working on another part of our software for another week or two, so I can't do it right now. |
Hi @arshaw , I was able to replace I can't seem to get the I have a minimal repo example here: |
Thanks for testing it out @oliverguenther. I've figured out the problem, see #426 |
This feature has been released in v6.0.0 |
@arshaw only been back to implement the new version now, works perfectly and our v15 upgrade is almost completed as a result of that. Thanks for all the time spent on the upgrade, this is greatly appreciated! 🙇 |
@arshaw how can I set different ng-templates for the same content injection area but for different views? I can use ngIf in the template and render different templates, but what can I do in case I need to render default template (no custom render)? |
Please consider support for defining custom event rendering using ng-template definitions, preferably by View.
This prevents needing to use HTMLElement manipulation directly (which is an Angular anti-pattern), and makes the use of styles defined per angular component possible.
The text was updated successfully, but these errors were encountered: