简体   繁体   English

Angular2滚动到具有* ngIf的元素

[英]Angular2 scroll to element that has *ngIf

I am writing a form in Angular 2 where the user submits the form, it is validated and if there are any errors with the inputs, I want to scroll the user's browser to the first element with the class "error" 我正在Angular 2中编写一个表单,用户提交表单,验证它,如果输入有任何错误,我想将用户的浏览器滚动到第一个带有“错误”类的元素

The problem is, all of my errors use *ngIf like so: 问题是,我的所有错误都使用* ngIf,如下所示:

<input type="text" [(ngModel)]="model.first_name">
<div class="error" *ngIf="errors.first_name">
    {{errors.first_name}}
</div>

In my submit function 在我的提交功能

submit(){
   this.errors = this.validate();
   if(this.errors.any()){
      var errorDivs = document.getElementsByClassName("error");
      if(errorDivs.length > 0){
         errorDivs[0].scrollIntoView();
      }
   }
}

I realize this is because *ngIf removes the div from the DOM completely and the Angular check for changes hasn't been given a chance to run yet. 我意识到这是因为* ngIf完全从DOM中删除div并且Angular检查更改还没有机会运行。 Is there a clever and clean way to do this? 有一个聪明而干净的方法吗?

Not sure I fully understand your question. 不确定我完全理解你的问题。

Using a directive like below would make the element scroll into view when errors.first_name becomes truthy: errors.first_name变为真实时,使用如下所示的指令会使元素滚动到视图中:

<div class="error" *ngIf="errors.first_name" scrollTo>
    {{errors.first_name}}
</div>
@Directive({ selector: '[scrollTo]'})
class ScrollToDirective implements AfterViewInit {
  constructor(private elRef:ElementRef) {}
  ngAfterViewInit() {
    this.elRef.nativeElement.scrollIntoView();
  }
}

Here's a way I figured out: 这是我想出的一种方式:

Create a helper class 创建一个帮助器类

export class ScrollHelper {
    private classToScrollTo: string = null;

    scrollToFirst(className: string) {
        this.classToScrollTo = className;
    }

    doScroll() {
        if (!this.classToScrollTo) {
            return;
        }
        try {
            var elements = document.getElementsByClassName(this.classToScrollTo);
            if (elements.length == 0) {
                return;
            }
            elements[0].scrollIntoView();
        }
        finally{
            this.classToScrollTo = null;
        }
    }
}

Then create one in the component you wish to use it in 然后在要使用它的组件中创建一个

private scrollHelper : ScrollHelper = new ScrollHelper();

then when you find out you have errors 然后当你发现你有错误

submit(){
   this.errors = this.validate();
   if(this.errors.any()){
        this.scrollHelper.scrollToFirst("error");
   }
}

then postpone the actual scroll until ngAfterViewChecked after *ngIf has been evaluated 然后在评估*ngIf之后将实际滚动推迟到ngAfterViewChecked

ngAfterViewChecked(){
    this.scrollHelper.doScroll();
}

This may not be the most elegant solution, but you can try wrapping your error focusing code in a setTimeout 这可能不是最优雅的解决方案,但您可以尝试将错误聚焦代码包装在setTimeout中

submit(){
   this.erroors = this.validate();
   setTimeout(() => {
     if(this.errors.any()){
        var errorDivs = document.getElementsByClassName("error");
        if(errorDivs.length > 0){
          errorDivs[0].scrollIntoView();
        }
     }
   }, 50);
}

The key isn't so much to delay the focusing code as much as it is about ensuring that the code runs after a tick of the event loop. 关键在于延迟聚焦代码,而不是确保代码在事件循环的滴答之后运行。 That will give the change detection time to run, thus ensuring your error divs have time to be added back to the DOM by Angular. 这将使更改检测时间运行,从而确保您的错误div有时间由Angular添加回DOM。

I've created a package that handles scrolling to element in Angular 4+ . 我创建了一个包,用于处理Angular 4+中的元素滚动。 Everything works for AoT and server-side rendering, in case you're using Angular Universal. 如果您使用的是Angular Universal,一切都适用于AoT和服务器端渲染。

NPM NPM
@nicky-lenaers/ngx-scroll-to @吴奇隆-lenaers / NGX滚动到

GitHub GitHub上
@nicky-lenaers/ngx-scroll-to @吴奇隆-lenaers / NGX滚动到

Also, I've recently created a Plunker that shows scrolling to elements that are initially hidden by *ngIf , which solves your problem. 此外,我最近创建了一个Plunker,显示滚动到最初由*ngIf隐藏的*ngIf ,这解决了您的问题。

Check out the Plunker for a working ngIf example 查看Plunker以获取有效的ngIf示例

My solution, which is quite simple. 我的解决方案,非常简单。

Steps 脚步

create a member : 创建一个成员

scrollAnchor: string;

create a method that will scroll to anchor if the anchor is set. 如果设置了锚,则创建一个将滚动到锚点的方法

ngAfterViewChecked(){
  if (this.scrollAnchor) {
    this.scrollToAnchor(this.scrollAnchor);
    this.scrollAnchor = null;
  }
}

and then when you want to scroll to somewhere that is in a div that has a ngIf, simply set the scrollAnchor 然后当你想滚动到具有ngIf的div中的某个地方时,只需设置scrollAnchor即可

this.scrollAnchor = "results";

Very simple! 非常简单! Note you have to write the scroll to anchor method yourself... or scroll to element or whatever. 请注意,您必须自己将滚动写入锚方法...或滚动到元素或其他任何内容。 Here is mine 这是我的

 private scrollToAnchor(anchor: string): boolean {
    const element = document.querySelector("#" + anchor);
    if (element) {
      element.scrollIntoView({block: "start", behavior: "smooth"});
      return true;
    }
    return false;
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM