Angular2:更改自动滚动指令的检测时间 [英] Angular2: Change detection timing for an auto-scroll directive

查看:70
本文介绍了Angular2:更改自动滚动指令的检测时间的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在研究用于聊天显示的简单自动滚动指令:

@Directive({
    selector: "[autoScroll]"
})
export class AutoScroll {
    @Input() inScrollHeight;
    @Input() inClientHeight;

    @HostBinding("scrollTop") outScrollTop;

    ngOnChanges(changes: {[propName: string]: SimpleChange}) {
        if (changes["inScrollHeight"] || changes["inClientHeight"]) {
            this.scroll();
        }
    };

    scroll() {
        this.outScrollTop = this.inScrollHeight - this.inClientHeight;
    };
}

当我设置了enableProdMode()并将ChangeDetectionStrategy设置为默认值时,此指令将起作用,但是在开发模式"下会出现异常.我可以将ChangeDetectionStrategy设置为onPush,在这种情况下不会发生异常,但是滚动会滞后.

是否有一种更好的代码结构,以便可以更新Dom然后调用Scroll函数?我已经尝试过setTimeout(),但是这会使延迟更糟,尝试使用ChangeDetectorRef并订阅可观察到的触发markForCheck()的方法.使用ngAfterViewChecked()会导致浏览器崩溃.

@Component({
    selector: "chat-display",
    template: `
            <div class="chat-box" #this [inScrollHeight]="this.scrollHeight" [inClientHeight]="this.clientHeight" autoScroll>
                <p *ngFor="#msg of messages | async | messageFilter:username:inSelectedTarget:inTargetFilter:inDirectionFilter" [ngClass]="msg.type">{{msg.message}}</p>
            </div>
       `,
    styles: [`.whisper {
            color: rosybrown;
        }`],
    directives: [NgClass, AutoScroll],
    pipes: [AsyncPipe, MessageFilterPipe],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChatDisplay implements OnInit {

    username: string;
    @Input() inSelectedTarget: string;
    @Input() inTargetFilter: boolean;
    @Input() inDirectionFilter: boolean;

    messages: Observable<ChatType[]>;

    constructor(private socketService_: SocketService, private authService_: AuthService) {
        this.username = this.authService_.username;
    };

    ngOnInit() {
    }

}

这是在开发人员模式下触发的异常:

例外:检查后,表达式'ChatDisplay @ 1:40中的this.scrollHeight'已更改.上一个值:"417".当前值:[Display.1:40中的[this.scrollHeight]中的"420" angular2.dev.js(23083,9)

我找到了解决此问题的一种方法,该方法涉及将聊天显示分为两个单独的组件并使用内容投影.因此,从父级到子级都有变化的流程,并且同一组件中没有两项功能,而另一项则触发更改.我可以使用默认的changeDetectionStrategy,而无需在开发人员模式下获取异常.

@Component({
    selector: "chat-display",
    template: `
    <auto-scroll-display>
        <chat-message *ngFor="#chat of chats | async | messageFilter:username:inSelectedTarget:inTargetFilter:inDirectionFilter" [message]="chat.message" [type]="chat.type"></chat-message>
    </auto-scroll-display>
    `,
    directives: [NgClass, AutoScrollComponent, ChatMessageComponent],
    pipes: [AsyncPipe, MessageFilterPipe]
})
export class ChatDisplay implements OnInit { /* unchanged code */ }

auto-scroll指令与原始帖子相同,试图找出是否有一种方法可以将指令功能组合到组件中.现在只是充当容器.

@Component({
    selector: "auto-scroll-display",
    template: `
    <div #this class="chat-box" [inScrollHeight]="this.scrollHeight" [inClientHeight]="this.clientHeight" autoScroll>
        <ng-content></ng-content>
    </div>
    `,
    directives: [AutoscrollDirective]
})
export class AutoScrollComponent{ }

这是带有工作代码的github链接,链接.

I've been working on a simple auto-scroll directive for chat-display:

@Directive({
    selector: "[autoScroll]"
})
export class AutoScroll {
    @Input() inScrollHeight;
    @Input() inClientHeight;

    @HostBinding("scrollTop") outScrollTop;

    ngOnChanges(changes: {[propName: string]: SimpleChange}) {
        if (changes["inScrollHeight"] || changes["inClientHeight"]) {
            this.scroll();
        }
    };

    scroll() {
        this.outScrollTop = this.inScrollHeight - this.inClientHeight;
    };
}

This directive will work when I've set enableProdMode() and when the ChangeDetectionStrategy is set to default, but when in "dev mode" I get an exception. I can set the ChangeDetectionStrategy to onPush, in that case the exception doesn't occur but the scroll will lag behind.

Is there a way to better structure this code so that Dom will be updated then the Scroll function can be called? I've tried setTimeout() but that makes the delay worse, tried using ChangeDetectorRef and subscribing to the observable to trigger markForCheck(). Using ngAfterViewChecked() causes browser crashes.

@Component({
    selector: "chat-display",
    template: `
            <div class="chat-box" #this [inScrollHeight]="this.scrollHeight" [inClientHeight]="this.clientHeight" autoScroll>
                <p *ngFor="#msg of messages | async | messageFilter:username:inSelectedTarget:inTargetFilter:inDirectionFilter" [ngClass]="msg.type">{{msg.message}}</p>
            </div>
       `,
    styles: [`.whisper {
            color: rosybrown;
        }`],
    directives: [NgClass, AutoScroll],
    pipes: [AsyncPipe, MessageFilterPipe],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChatDisplay implements OnInit {

    username: string;
    @Input() inSelectedTarget: string;
    @Input() inTargetFilter: boolean;
    @Input() inDirectionFilter: boolean;

    messages: Observable<ChatType[]>;

    constructor(private socketService_: SocketService, private authService_: AuthService) {
        this.username = this.authService_.username;
    };

    ngOnInit() {
    }

}

This is the the exception that is triggered when in dev mode:

EXCEPTION: Expression 'this.scrollHeight in ChatDisplay@1:40' has changed after it was checked. Previous value: '417'. Current value: '420' in [this.scrollHeight in ChatDisplay@1:40] angular2.dev.js (23083,9)

解决方案

I found one way to solve this, it involves dividing the chat display into two separate components and use content projection. So there is a flow of changes from parent to child, and not having two functionalities in the same component with one triggering changes in the other. I can used the default changeDetectionStrategy without getting exceptions in dev mode.

@Component({
    selector: "chat-display",
    template: `
    <auto-scroll-display>
        <chat-message *ngFor="#chat of chats | async | messageFilter:username:inSelectedTarget:inTargetFilter:inDirectionFilter" [message]="chat.message" [type]="chat.type"></chat-message>
    </auto-scroll-display>
    `,
    directives: [NgClass, AutoScrollComponent, ChatMessageComponent],
    pipes: [AsyncPipe, MessageFilterPipe]
})
export class ChatDisplay implements OnInit { /* unchanged code */ }

The auto-scroll directive is identical to original post, was trying to figure out if there was a way to combine the directive functionality into the component. It's just acting as a container now.

@Component({
    selector: "auto-scroll-display",
    template: `
    <div #this class="chat-box" [inScrollHeight]="this.scrollHeight" [inClientHeight]="this.clientHeight" autoScroll>
        <ng-content></ng-content>
    </div>
    `,
    directives: [AutoscrollDirective]
})
export class AutoScrollComponent{ }

Here's a github link with working code, link.

这篇关于Angular2:更改自动滚动指令的检测时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆