未在 Router.navigate() 上触发角度变化检测

Posted

技术标签:

【中文标题】未在 Router.navigate() 上触发角度变化检测【英文标题】:Angular Change Detection not triggering on Router.navigate() 【发布时间】:2018-06-28 16:39:01 【问题描述】:

我在 Angular 中有一个名为 NewEventComponent 的组件,它只包含一个用于提交新事件的表单。在它的提交方法上有这个代码:

onSubmit() 
  this.isSendingRequest = true;

  this.geolocationService.geocodeAddress(this.location.value).subscribe(
    (results: google.maps.GeocoderResult[]) => 
      if (results && results.length) 
        const eventData: EventData = 
          name: this.name.value,
          location: [results[0].geometry.location.lat(), results[0].geometry.location.lng()],
          startDate: this.datetimeService.convertDateAndTimeToISOString(this.startDate.value, this.startTime.value),
          endingDate: this.datetimeService.convertDateAndTimeToISOString(this.endingDate.value, this.endingTime.value),
          description: this.description.value,
          sport: this.sport.value,
          intensity: this.intensity.value,
          fee: this.fee.value,
          maxPlayers: this.maxPlayers.value
        ;

        this.eventService.createEvent(eventData).subscribe(
          (res: any) => 
            this.alertService.createAlert( message: EVENT_CREATED_MESSAGE, type: AlertType.Success );
            this.router.navigate(['events', res.data.event.id]);
          
        );
       else 
        this.handleInvalidLocationError();
      
    
  );

它只将事件发送到服务器并重定向到事件页面,即EventDetails 组件。该组件仅向服务器请求请求的事件并将其作为输入传递给子组件。这是代码:

TS:

@Component(
  selector: 'app-event-details',
  templateUrl: './event-details.component.html',
  styleUrls: ['./event-details.component.scss']
)
export class EventDetailsComponent implements OnInit 

  private event: EventResponse;

  constructor(
    private activatedRoute: ActivatedRoute,
    private eventService: EventService
  )  

  ngOnInit() 
    this.getEvent();
  

  getEvent(): void 
    this.activatedRoute.params.subscribe(
      (params: Params) => 
        this.eventService.getEvent(params.id).subscribe(
          (res: any) => 
            this.event = res.data.event;
          
        );
      
    );
  

HTML:

<div class="row">
  <div class="col">
    <div class="info-wrapper">
      <app-event-details-info [event]="event">
      </app-event-details-info>
    </div>
  </div>
</div>

子组件EventDetailsInfo只接收事件作为像这样@Input() event: EventResponse;这样的输入,并在html中显示这样的信息:&lt;h5&gt; event?.name &lt;/h5&gt;

这里的问题是,当从NewEvent 组件导航到EventDetails 组件时,不会触发更改检测,因此直到我单击 DOM 中的任何位置,例如,更改不会显示。但是当我通过 url 直接导航到 EventDetails 组件时,页面正在正确呈现。

我在变更检测方面遗漏了什么?任何想法?谢谢!

PS:EventService 只执行 HTTP 调用并将它们作为 Observables 返回,例如:

createEvent(eventData: EventData): Observable<any> 
  return this.http.post(`$environment.apiUrl/events/`, eventData);


getEvent(eventId: string): Observable<any> 
  return this.http.get(`$environment.apiUrl/events/$eventId`);

PS2:这是“onSubmit()”方法中使用的GeolocationService 方法:

geocodeAddress(address: string): Observable<google.maps.GeocoderResult[]> 
  const response: Subject<google.maps.GeocoderResult[]>
    = new Subject<google.maps.GeocoderResult[]>();
  const request: google.maps.GeocoderRequest =  address ;

  this.geocoder.geocode(request,
    (results: google.maps.GeocoderResult[]) => 
      response.next(results);
    
  );

  return response;

【问题讨论】:

什么是EventService?也许该事件是在 Angulars 区域之外发出的。 我已经用服务方法更新了答案,它只有http调用 可能是路由配置有问题。请尝试在stackblitz.com中创建一个最小的复制品 好的,这是一个复杂的应用程序,但我稍后会尝试创建一个简单的复制品并在此处发布。我对这个问题很迷茫...... 只添加绝对必要的复制。其他一切都只是噪音,难以追查问题。 【参考方案1】:

更新

onSubmit() 
  this.isSendingRequest = true;

  this.geolocationService.geocodeAddress(this.location.value).subscribe(
    (results: google.maps.GeocoderResult[]) => 
      this.zone.run(() => π
        if (results && results.length) 
            const eventData: EventData = 
              name: this.name.value,
              location: [results[0].geometry.location.lat(), results[0].geometry.location.lng()],
              startDate: this.datetimeService.convertDateAndTimeToISOString(this.startDate.value, this.startTime.value),
              endingDate: this.datetimeService.convertDateAndTimeToISOString(this.endingDate.value, this.endingTime.value),
              description: this.description.value,
              sport: this.sport.value,
              intensity: this.intensity.value,
              fee: this.fee.value,
              maxPlayers: this.maxPlayers.value
            ;

            this.eventService.createEvent(eventData).subscribe(
              (res: any) => 
                this.alertService.createAlert( message: EVENT_CREATED_MESSAGE, type: AlertType.Success );
                this.router.navigate(['events', res.data.event.id]);
              
            );
           else 
            this.handleInvalidLocationError();
          
        
     );
  );

原创

也许事件是在 Angular 区域之外发出的。

试试:

constructor(private zone: NgZone)  

和:

this.zone.run(() => this.router.navigate(['events', res.data.event.id]));

如果这解决了问题,你应该修复EventService

【讨论】:

我试过这个,它解决了问题,但我不明白为什么......那是服务问题? 这可能是服务,但很难说。您分享的代码看起来不错。 我好像遇到过这个问题……不过我不是很懂。我想发布一个最少的代码,以便清楚地理解,但我错过了在提交表单时使用GeolocationService...我已经用GeolocationService 的完整代码更新了我的答案和onSubmit() 方法。所以现在,我认为问题出在GeolocationService 对吗?如果你能更新你的答案会让我很开心! 感谢您的解释!最后我所做的是在GeolocationService 方法中调用zone.run(...) 方法。因此,当服务发出 Subject 的下一个值时,它位于 Angular 区域内,因此在对该方法的任何订阅后,都会触发更改检测。 是的!问题是Google JS library 使用JSONP 来获取Google APIS,所以它在Angular Zone 之外运行。【参考方案2】:

根据订阅发生的顺序,您可能会完全错过该事件。考虑使用shareReplay(1) 并公开一个事件。

或者,将事件的订阅移动到构造函数:

  constructor(
    private activatedRoute: ActivatedRoute,
    private eventService: EventService
  )  
     this.getEvent();
  

  ngOnInit() 
  

【讨论】:

以上是关于未在 Router.navigate() 上触发角度变化检测的主要内容,如果未能解决你的问题,请参考以下文章

防止在Angular中的单个router.navigate调用上滚动到顶部

Backbone.history.navigate和this.router.navigate之间有什么区别

Angular router.navigate w/ route guard 在组件上时不起作用

角度 2 router.navigate 未定义

为啥 routerLink 和 router.navigate() 行为不同?

带有查询参数Angular 5的router.navigate