2

私は、チャット表示用の単純な自動スクロール ディレクティブに取り組んできました。

@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() {
    }

}

これは、dev モードのときにトリガーされる例外です。

例外: 式 'this.scrollHeight in ChatDisplay@1:40' は、チェック後に変更されました。以前の値: '417'。現在の値: [ChatDisplay@1:40 の this.scrollHeight] angular2.dev.js の '420' (23083,9)

4

2 に答える 2

3

DOM が更新されてから Scroll 関数を呼び出すことができるように、このコードをより適切に構造化する方法はありますか?

ngAfterViewChecked()が呼び出される前に DOM を更新する必要があります。次のようなものが機能するかどうかを確認します。

ngOnChanges(changes: {[propName: string]: SimpleChange}) {
    // detect the change here
    if (changes["inScrollHeight"] || changes["inClientHeight"]) {
        this.scrollAfterDomUpdates = true;
    }
};
ngAfterViewChecked() {
    // but scroll here, after the DOM was updated
    if(this.scrollAfterDomUpdates) {
       this.scrollAfterDomUpdates = false;
       this.scroll();
    }
}

それでもうまくいかない場合は、setTimeout でスクロールの呼び出しをラップしてみてください。

    if(this.scrollAfterDomUpdates) {
       this.scrollAfterDomUpdates = false;
       this.setTimeout( _ => this.scroll());
    }
于 2016-03-25T21:33:28.837 に答える
2

これを解決する 1 つの方法を見つけました。それには、チャット表示を 2 つの別個のコンポーネントに分割し、コンテンツ プロジェクションを使用することが含まれます。したがって、親から子への変更の流れがあり、同じコンポーネントに 2 つの機能があり、一方が他方の変更をトリガーすることはありません。開発モードで例外を取得することなく、デフォルトの 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 */ }

自動スクロール ディレクティブは元の投稿と同じです。ディレクティブ機能をコンポーネントに組み合わせる方法があるかどうかを調べようとしていました。現在はコンテナとして機能しています。

@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 リンクlinkです。

于 2016-03-25T17:29:08.827 に答える