ngAfterViewInit不起作用,还是我做错了什么?

8

我的ngAfterViewInit函数的效果不如预期。我感觉可能是我漏了什么。

  ngOnInit() {
   this.dataService.getUsers().subscribe((users) => {this.users = users) ;  
  }

  ngAfterViewInit() {
    if (document.getElementById('target')) {
        document.getElementById('target').scrollIntoView({
          behavior: 'auto',
          block: 'center',
          inline: 'center',
       });
   }

基本上,我使用ngAfterViewInint()函数,在视图完全加载后滚动到当前项目。在ngOnInit()中,如果我没有调用subscribe(),而只是放了一个变量,它就可以正常工作。但这不是我要问的问题。
我通过在ngAfterViewInit()中使用setTimeout()来解决这个问题。但我不喜欢它。我不喜欢它依赖于超时。我认为应该有更好的解决方案。有什么好的建议吗?谢谢大家。

你的 target 元素是否依赖于请求返回的数据? - Pytth
1
在 Angular 2+ 中,您不能像这样引用元素。请参考调用子本机元素的此内容:https://dev59.com/TVYM5IYBdhLWcg3w5jRN - Rager
@Pyth 是的,没错。 - daniel8x
@Rager 谢谢。让我试试。 - daniel8x
我认为@ViewChildren是这里的完美解决方案,我已经包含了我的答案,希望有所帮助。 - Muhammed Albarmavi
3个回答

5
更好的解决方案是使用ViewChildren装饰器,因为你可以订阅视图的更改,这意味着你不需要使用setTimeOut
  @ViewChildren("target") target: QueryList<ElementRef>;

  ngOnInit() {
   this.dataService.getUsers().subscribe((users) => {this.users = users) ;  
  }

  ngAfterViewInit() {
    this.target.changes.subscribe(({ first: elm }) => {

      elm.nativeElement.scrollIntoView({
        behavior: "auto",
        block: "center",
        inline: "center"
      });

    });
  }

stackblitz demo


@所有读者,请不要忘记,在这种情况下,target不是像OP问题中的HTML ID,而是一个Angular模板变量<div #target></div>https://angular.io/api/core/ViewChildren - undefined

5

看起来你的代码依赖于dataService.getUsers()调用返回后才会调用ngAfterViewInit()

如果是这样,那么不行,因为时间没有保证。 相反,请按照以下方式操作:

 ngAfterViewInit() {
   this.dataService.getUsers().subscribe((users) => {
      this.users = users;
      this.scrollTargetIntoView();
   )};
 }

  scrollTargetIntoView() {
    if (document.getElementById('target')) {
        document.getElementById('target').scrollIntoView({
          behavior: 'auto',
          block: 'center',
          inline: 'center',
       });
   }

这可能还有点早 - 你可能想在这里加上一个setTimeout()函数,让DOM有机会被渲染,例如:

ngAfterViewInit() {
   this.dataService.getUsers().subscribe((users) => {
      this.users = users;
      setTimeout(() => this.scrollTargetIntoView());
   )};
 }

  scrollTargetIntoView() {
    if (document.getElementById('target')) {
        document.getElementById('target').scrollIntoView({
          behavior: 'auto',
          block: 'center',
          inline: 'center',
       });
   }

我曾经不得不使用这种技巧。 我需要等待DOM加载完成,以便将Angular模板中“select”标签元素的ID传递给外部JS文件进行样式设置。 - Sam Alekseev

2
如果您的Angular应用程序具有路由功能,那么您可以考虑另一个选项Route Resolvers,它可以在导航到特定路由之前预取组件的数据。这样,您的用户界面将拥有所有必要的数据以在ngOnInit()生命周期钩子中呈现。
您可以使用以下代码创建解析器服务:
@Injectable()
export class UserResolverService implements Resolve<Observable<User[]>> {
  constructor(private dataService: DataService) {}

  resolve() {
    return this.dataService.getUsers()
  }
}

然后在您的路由定义中,您可以使用以下方法激活解析器:
const routes: Routes = [
  {
    path: 'my_path',
    component: MyComponent,
    resolve: {
      users: UserResolverService
    }
  }
];

然后最后,在你的组件的ngOnInit()生命周期钩子中,你可以使用以下代码来消费数据:
constructor(private route: ActivatedRoute) {}

ngOnInit() {
  this.users = this.route.snapshot.data.users;
}

ngAfterViewInit() {
  if (document.getElementById('target')) {
    document.getElementById('target').scrollIntoView({
      behavior: 'auto',
      block: 'center',
      inline: 'center',
    });
  }
}

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