在此示例中,为什么我无法访问在另一个组件(不是子组件)中定义的div的@ViewChild引用?

问题描述 投票:0回答:2

我在尝试使用@ 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()装饰器时发现了一些问题。我将尝试详细解释我要做什么。我的项目有一个正常工作的版本(目前只是一个...

我认为您可以使用

内容投影

angular angular-components angular9 viewchild
2个回答
0
投票
<!-- ... --> <app-header></app-header> <app-fullcalendar></app-fullcalendar> <div #external> <!-- ... --> </div>

如果是这种情况,我认为您可以这样做:

app.component.html

<app-fullcalendar> <div #external> <!-- ... --> </div> </app-fullcalendar>

app-fullcalendar.component.ts
@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>

0
投票
您可以根据需要使用它,以适合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所做的所有事情都将其声明为可拖动,因此在应用程序组件中应该可以正常工作。

© www.soinside.com 2019 - 2024. All rights reserved.