我在尝试使用@ ViewChild()装饰器时发现了一些问题。
我将尝试详细解释我要做什么。
我有我的项目的正确版本(目前,这只是我正在研究的最小原型),以防您可以在此处看到完整的代码:https://bitbucket.org/dgs_poste_team/soc_calendar/src/master/
所以基本上我有这个主app-component.html主视图:
<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>
如您所见,它包含对app-fullcalendar组件的引用。该组件包含一个实现以下内容的日历:https://fullcalendar.io/docs/external-dragging-demo
在这里显示了一个日历和一个“外部事件”框,我可以在其中选择一个事件并将其拖到日历内。
在我的原始版本中,它可以正常工作,但目前我以以下简单方式实现了它:
1)这是我的fullcalendar.component.html视图文件:
<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>
如您所见,它包含一个引用名称设置为external的div,并且该div包含我的app-fullcalendar组件的一个子组件,该子组件仅呈现事件列表(app-people- list,在我的用例中,一个事件是我必须在日历的特定日期上放一个人,但是现在这并不重要了。]
然后进入处理app-fullcalendar组件行为的打字稿代码,我有以下代码:
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
};
}
});
}
}
因此,在此代码中,我通过以下代码引用了具有名为external的div的引用:
@ViewChild('external') external: ElementRef;
因此,此外部属性包含此特定div的引用,该引用将包含我必须在日历内拖动的事件\人员列表。
然后,我在ngAfterViewInit()内部使用此引用属性,以允许对具有引用external的div中显示的元素进行拖放。
ngAfterViewInit(){//console.log(“ ngAfterViewInit()START !!!”)
new Draggable(this.external.nativeElement, {
itemSelector: '.fc-event',
eventData: function(eventEl) {
console.log("DRAG !!!");
return {
title: eventEl.innerText
};
}
注意:在项目选择器中指定的。fc-class呈现在内部,并放置在呈现的event \ person列表的每个元素上。
完美,效果很好!
现在是我的问题:我的想法是,这种方式使代码过于紧密地耦合在一起,所以我很想以这种方式将其解耦:
1)div引用了我的[[#external并包含了我的[[,这使必须拖到日历中的人物\事件列表不再是我的[[full-calendar] >组件,但它在我的[[app-component。]中处于同一级别。所以基本上我从我的fullcalendar.component.html中将其删除:
<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>
-->
并且我将其放在我的[[app.component.html文件的左列中,现在是:现在确定人员\事件列表显示在左侧,但是我再也无法将事件\人员从该列表拖到我的日历中。这是因为在JavaScript控制台中,我收到此错误消息:
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)
此错误发生在函数中:fullcalendar.app.component.ts
文件的ngAfterViewInit()
ngAfterViewInit(){//console.log(“ ngAfterViewInit()START !!!”)
组件之后自动执行。new Draggable(this.external.nativeElement, { itemSelector: '.fc-event', eventData: function(eventEl) { console.log("DRAG !!!"); return { title: eventEl.innerText }; } });
似乎在执行此钩子时,此组件无法再使用#external
引用检索div。我怀疑发生这种情况是因为该钩子函数在Angular完全渲染fullcalendar.app.component
在第一个工作示例中,之所以起作用是因为
app-people-list是一个子组件,因此此函数的执行发生在所有子组件都已呈现之后。但是现在我的app-people-list不再是子组件,并且我怀疑此ngAfterViewInit()
不再是正确的解决方案。这个想法正确吗?我该怎么做才能解决这种情况并使我的示例正确运行?我可以使用另一个挂钩还是有其他好的解决方法?我在尝试使用@ViewChild()装饰器时发现了一些问题。我将尝试详细解释我要做什么。我的项目有一个正常工作的版本(目前只是一个...
我认为您可以使用
内容投影
<!-- ... -->
<app-header></app-header>
<app-fullcalendar></app-fullcalendar>
<div #external>
<!-- ... -->
</div>
如果是这种情况,我认为您可以这样做:
app.component.html<app-fullcalendar>
<div #external>
<!-- ... -->
</div>
</app-fullcalendar>
@ContentChild('external') external: ElementRef<HTMLDivElement>;
ngAfterContentInit () {
/* this.external... */
}
编辑
app.component.html
<!-- Left column -->
<!-- <app-test2 #external></app-test2> -->
<div #external>
test23112
</div>
<!-- Right column -->
<app-test1>
<ng-container [share-comp]="external"></ng-container>
</app-test1>
share-comp.directive.ts
@Directive({ selector: '[share-comp]' }) export class ShareCompDirective { @Input('share-comp') shared: any constructor() { } }
app-test1.component.ts
(在您的情况下为app.calendar)
@ContentChild(ShareCompDirective) private external: ShareCompDirective;
constructor() { }
ngOnInit() {
}
ngAfterContentInit () {
// If the `external` part is a component
// const nativeElem = (this.external.shared.elem as ElementRef).nativeElement;
// If the shared part is a DOM element
const nativeElem = this.external.shared
console.log(nativeElem)
}
ng-run demo
所以您可以毫无问题地将模板引用作为输入传递...
<app-fullcalendar [externals]="externals"></app-fullcalendar>
ngOnInit
或更高版本的钩子。但是,如果我正确地阅读了这篇文章,那似乎就不需要了,而您要做的就是移动这部分:
@ViewChild('external') external: ElementRef;
ngAfterViewInit() {
//console.log("ngAfterViewInit() START !!!")
new Draggable(this.external.nativeElement, {
itemSelector: '.fc-event',
eventData: function(eventEl) {
console.log("DRAG !!!");
return {
title: eventEl.innerText
};
}
});
}
从fullcalendar.component.ts
中取出,并进入app.component.ts
控制器中。 ViewChild
仅适用于在该组件的模板中声明的内容,而不适用于整个应用程序的模板。但是,由于您对view child所做的所有事情都将其声明为可拖动,因此在应用程序组件中应该可以正常工作。