I am finding some problem trying to use @ViewChild() decorator.
I will try to explain in details what I am trying to do.
I have a correctly working version of my project (at the moment it is just a minimal prototype on which I am working on), in case you can see the complete code here: https://bitbucket.org/dgs_poste_team/soc_calendar/src/master/
So basically I have this main app-component.html main view:
<div class="container">
<div class="row">
<div class="col-12">
<app-header></app-header>
</div>
<div class="row">
<div class="col-4">
<p>DRAGGABLE</p>
</div>
<div class="col-8">
<app-fullcalendar></app-fullcalendar>
</div>
</div>
</div>
As you can see it contains the reference to the app-fullcalendar component. This component contain a calendar implementing something like this: https://fullcalendar.io/docs/external-dragging-demo
Where is shown a calendar and an "external events" box where I can pick an event and drag into inside my calendar.
In my original version it works fine but at the moment I implemented it in the following simple way:
1) This is my fullcalendar.component.html view file:
<p>fullcalendar works!</p>
<div class="content-section implementation">
<p-fullCalendar #fullcalendar
[events]="events"
[options]="options">
</p-fullCalendar>
</div>
<div #external>
<p>
<strong>Draggable Events</strong>
</p>
<app-people-list></app-people-list>
</div>
As you can see it contains a div having reference name set to external and this div contain a sub component of my app-fullcalendar component that simply renders the event list (app-people-list, in my use case an event is a person that I have to put on a specific date of my calendar, but this is not so important now).
Then into the typescript code handling the app-fullcalendar component behavior I have this code:
import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import { EventService } from '../event.service';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import listPlugin from '@fullcalendar/list';
import interactionPlugin, { Draggable } from '@fullcalendar/interaction';
import { FullCalendar } from 'primeng';
@Component({
selector: 'app-fullcalendar',
templateUrl: './fullcalendar.component.html',
styleUrls: ['./fullcalendar.component.css']
})
export class FullcalendarComponent implements OnInit {
events: any[];
options: any;
header: any;
//people: any[];
@ViewChild('fullcalendar') fullcalendar: FullCalendar;
@ViewChild('external') external: ElementRef;
constructor(private eventService: EventService) {}
ngOnInit() {
this.eventService.getEvents().then(events => { this.events = events;});
//this.eventService.getPeople().then(people => {this.people = people;});
//console.log("PEOPLE: " + this.people);
this.options = {
plugins:[ dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin ],
defaultDate: '2017-02-01',
header: {
left: 'prev,next',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
editable: true,
dateClick: (dateClickEvent) => { // <-- add the callback here as one of the properties of `options`
console.log("DATE CLICKED !!!");
},
eventClick: (eventClickEvent) => {
console.log("EVENT CLICKED !!!");
},
eventDragStop: (eventDragStopEvent) => {
console.log("EVENT DRAG STOP !!!");
},
eventReceive: (eventReceiveEvent) => {
console.log("EXTERNAL EVENT LAND ON THE CALENDAR");
//console.log(eventReceiveEvent);
this.eventService.addEvent(eventReceiveEvent);
}
};
}
ngAfterViewInit() {
//console.log("ngAfterViewInit() START !!!")
new Draggable(this.external.nativeElement, {
itemSelector: '.fc-event',
eventData: function(eventEl) {
console.log("DRAG !!!");
return {
title: eventEl.innerText
};
}
});
}
}
So in this code I take the reference to the div having reference named external, by this line:
@ViewChild('external') external: ElementRef;
So this external properties contains the reference of this specific div that will contains my list of events\people that I have to drag inside my calendar.
Then I use this reference property inside the ngAfterViewInit() to allow the drag and drop of the element shown inside the div having refernce external.
ngAfterViewInit() { //console.log("ngAfterViewInit() START !!!")
new Draggable(this.external.nativeElement, {
itemSelector: '.fc-event',
eventData: function(eventEl) {
console.log("DRAG !!!");
return {
title: eventEl.innerText
};
}
NOTE: the .fc-class specified in the item selector is rendered inside the and it is put on each element of the event\person list rendered.
Perfect it works fine !!!
Now my problem: my idea is that in this way the code is too strongly coupled and I was thingkin to decouple it in this way:
1) The div referenced my #external and containing my rendering the list of people\event that have to be dragged inside my calendar is no more a sub component of my full-calendar component but it is at the same level inside my app-component.
So basically I remove it from my fullcalendar.component.html that become:
<p>fullcalendar works!</p>
<div class="content-section implementation">
<p-fullCalendar #fullcalendar
[events]="events"
[options]="options">
</p-fullCalendar>
</div>
<!-- REMOVED -->
<!--
<div #external>
<p>
<strong>Draggable Events</strong>
</p>
<app-people-list></app-people-list>
</div>
-->
And I put it in the left column of my app.component.html file, that now is:
Ok now the list of people\events is rendered on the left side but I can't anymore drag the event\person from this list into my calendar. This because in the JavaScript console I am obtaining this error message:
core.js:6272 ERROR TypeError: Cannot read property 'nativeElement' of undefined
at FullcalendarComponent.ngAfterViewInit (fullcalendar.component.ts:67)
at callHook (core.js:4770)
at callHooks (core.js:4734)
at executeInitAndCheckHooks (core.js:4674)
at refreshView (core.js:12060)
at refreshComponent (core.js:13461)
at refreshChildComponents (core.js:11701)
at refreshView (core.js:12036)
at renderComponentOrTemplate (core.js:12114)
at tickRootContext (core.js:13668)
This error happen into the ngAfterViewInit() function of the fullcalendar.app.component.ts file:
ngAfterViewInit() { //console.log("ngAfterViewInit() START !!!")
new Draggable(this.external.nativeElement, {
itemSelector: '.fc-event',
eventData: function(eventEl) {
console.log("DRAG !!!");
return {
title: eventEl.innerText
};
}
});
It seems that this component can't retrieve anymore the div with the #external reference when this hook is executed. I suspect that this happens because this hook function is automatically executed after that Angular have completly rendered the fullcalendar.app.component component.
In the first working example it worked because the app-people-list was a sub component so this function execution happened after that all the subcomponent was rendered. But now my app-people-list is no more a subcomponent and I suspect that this ngAfterViewInit() is no more the right solution. Is it this idea correct?
What can I do to solve the situation and have my example correctly working? Have I to use another hook or there is some other good solution?