我如何手动重新渲染一个组件?

27

我是Angular2的新手,习惯了Angular 1的digest循环。这意味着如果我更新视图的作用域,可以通过调用$scope.$digest()手动触发digest。然而,在Angular2中,我不确定如何做到这一点,尤其是由于新框架没有旧版本具有的隐式数据绑定。

这是我的问题所在。当命中带有参数的URL时,我有一个路由加载组件:

// Router
export const AppRoutes : RouterConfig = [
    {
    path: 'my/:arg/view',
    component: MyComponent  
    }
]

然后我有这个组件:

// Component
export class MyComponent {
    constructor(private route : ActivatedRoute,
      private r : Router) {
    let id = parseInt(this.route.params['value'].id);
    console.log(id);
    // do stuff with id
    }

    reloadWithNewId(id:number) {
        this.r.navigateByUrl('my/' + id + '/view');
    }
}

假设我导航到url /my/1/view。它将调用构造函数并记录数字1。但是,如果我使用新的ID调用 reloadWithNewId ,例如reloadWithNewIf(2),则构造函数不会再次被调用。我如何手动重新加载组件?


6
已接受的解决方案(在 ngOnInit() 中订阅 params)是首选方法,但并不总是有效。例如,我正在使用 ng2-charts https://github.com/valor-software/ng2-charts,这是一个组件,无法正确响应 x 轴标签的更改(https://github.com/valor-software/ng2-charts/issues/547)。如果第三方组件存在此类错误,是否有一种方法可以重新加载该组件? - Ho Li Fuk
4个回答

20

不应该需要重新加载组件。只需更新模型,视图会自动更新:

export class MyComponent {
    constructor(private route : ActivatedRoute,
      private r : Router) {}

    reloadWithNewId(id:number) {
        this.r.navigateByUrl('my/' + id + '/view');
    }

    ngOnInit() {
      this.sub = this.route.params.subscribe(params => {
         this.paramsChanged(params['id']);
       });
    }

    paramsChanged(id) {
      console.log(id);
      // do stuff with id

    }
}

使用 ngOnInit 钩子与像 @madhu-ranjan 所做的构造函数相比有什么优势? - dopatraman
2
你也可以在构造函数中执行 this.route.params.subscribe(...) 的操作。当构造函数被执行时,@Input() 还不可用,但是在 ngOnInit() 被执行时就可以使用了(在这个实际用例中并不相关)。 - Günter Zöchbauer
1
它正在工作。但是,我使用了Activated router而不是route this.activatedRoute.paramMap.subscribe(params => { this.eci = params.get('id'); }。 - gnganapath

14

你可以通过导航到其他url然后返回到你想要的那个url来强制重新初始化angular2组件。

以下是我使用Router类方法的简单解决方案(参考:https://angular.io/docs/ts/latest/api/router/index/Router-class.html

组件构造函数:

constructor(private router: Router) {}

模板中(示例):

(click)="onTabChange(id)"

然后在组件类内部:

onTabChange() {
  if (this.router.navigated === false) {
    // Case when route was not used yet
    this.router.navigateByUrl(`/module/${id}`);
  } else {
    // Case when route was used once or more
    this.router.navigateByUrl(`/index`).then(
      () => {
        this.router.navigateByUrl(`/module/${id}`);
      });
  }
}

1
有点巧妙,但这是唯一回答问题并比刷新更有效的答案。 - Levi Fuller
5
唯一可行的解决方案是如果有一种方法可以从浏览历史记录中删除假网址。否则,浏览器的后退按钮将不会按预期执行。 - benek
1
@benek 使用 skipLocationChangethis.router.navigateByUrl('/blank', { skipLocationChange: true }) - Thanh Nguyen
1
@ThanhNguyen,这对我非常有效,但是在最新的Angular版本中有没有避免虚拟路由的方法? - Clueless

3

当您仅更新参数时,构造函数不会再次调用。因为组件类已经实例化,

如果您想检测更改,可以像下面这样订阅参数更改:

  constructor(route: ActivatedRoute) {
    route.params.subscribe(param => {
        let id = param['id '];
        // Do your stuff with id change.
    });
  }

为什么不直接使用 ngOnInit 钩子而不是构造函数? - dopatraman
@dopatraman,如果你只是通过触发reloadWithNewId来更新参数,则ngOnInit不会被触发,因为组件已经初始化。这是Plunker,希望能有所帮助! - Madhu Ranjan
我也遇到了这个问题,将route.params.subscribe实现在ngOnInit中确实有效 :) - KCarnaille

1
我认为changeDetectorRef是完成这项任务的合适工具: HTML
<my-component *ngIf="!reloading"
              (loadNext)="onLoadNext()">
</my-component>

TS

  constructor(
    private cdr: ChangeDetectorRef
  ) { }

  onLoadNext() {
      this.reloading = true;
      this.cdr.detectChanges();
      this.reloading = false;
      this.cdr.detectChanges();
  }

我为什么要这样做,而不是像Günther提供的答案中所说的“更新模型和视图自动更新”?这样你可以确保组件被重新初始化,之前的一些值没有留下。这就是为什么我觉得这样做更加清晰。

示例:https://stackblitz.com/edit/commatrainer-v1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接