0

我有一个 Observable 给我一些日志更改(有时每毫秒数百)。 我订阅了这个 Observable,并且可以很好地控制所有的变化。
ngOnInit()

当我pipe async将此 Observable 转换为子组件的 HTML时,问题就出现@Input()了 - 如果父组件上的 Observable 看到它的标量值在几毫秒内发生变化……事实上,在子组件上没有发生变化检测.

我的这个 Observable 是一个简单的字符串,时间戳为 1/1000(作为浏览器的 console.log)。因此,我的这种情况与可以相同的 s 或s
无关,实际上可能不会触发任何更改...ArrayObject

有什么建议吗?太感谢了!

顺便说一句,这是一个 Angular 应用程序试图记录在重新渲染和重新安装3D 组件 Scene时发生的所有事情,与库对话ThreeJS
你可以想象速度...

一些编码的代码。

logger.service.ts

@Injectable({
    providedIn: 'root'
})
export class LoggerService {

    public newEvtLogged$ = new Subject<string>();

    constructor() { }

    addLog(log: string) {
        const timeStamp = this.returnNowsTime();
        const loggedEvt = `@ ${timeStamp}: ${log}`;

        this.newEvtLogged$.next(loggedEvt);
    }

...

    /**
     * We want to simulate the broser'd Inspector timestamp.
     * A kind of an unique Id could be '#${new Date().getTime()}'
     * But we want user to see when the event happened...
     */
    private returnNowsTime(): string {
        const now = new Date();
        return now.toLocaleTimeString() + '.' + now.getMilliseconds();

    }

}

parent-component.ts

@Component({
    selector: 'app-component-menu',
    templateUrl: './component-menu.component.html',
    styleUrls: ['./component-menu.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ComponentMenuComponent implements OnInit, OnChanges, AfterViewInit, AfterViewChecked, OnDestroy {

...

    // NgRxJS stream of added logg events, through LoggerService, all over the App:
    loggEvent$: Observable<string>;
    loggSubscription: Subscription;
    loggEvent: string;

...

    ngOnInit() {

        this.loggEvent$ = this.THreeJSloggerService.newEvtLogged$;

        // Check we're subscribing from the beginning:
        this.loggSubscription = this.loggEvent$.subscribe((logEvt: string) => {
            console.log('$$$$$$$$ NEW LOGG EVENT: ', logEvt);
            this.loggEvent = logEvt;
        });
    }

parent-component.html

<ng3d-monitor-debug [goToTopPosition]="monitorMarginTop" [dataToPrint]="(loggEvent$ | async)"></ng3d-monitor-debug>

child-component.ts

@Component({
    selector: 'ng3d-monitor-debug',
    templateUrl: './monitor-debug.component.html',
    styleUrls: ['./monitor-debug.component.scss'],
    changeDetection: ChangeDetectionStrategy.OnPush,
    encapsulation: ViewEncapsulation.None
})
export class MonitorDebugComponent implements OnInit, OnChanges, AfterViewInit {

    ...

    @Input() dataToPrint: string;

    ...

    constructor(
        private hostElement: ElementRef,
        private renderer: Renderer2
    ) { }

    ngOnInit() {
        ...
    }

    ngOnChanges(propsChanged: SimpleChanges) {
        // console.error(propsChanged);

        if (propsChanged.dataToPrint && !propsChanged.dataToPrint.firstChange) {
            // console.error('=============== dataToPrint has CHANGED:', this.dataToPrint);

            this.doPrintData(this.dataToPrint, null);
        }

    }

    ...

    private doPrintData(str: string, jsonData: any) {
        this.contentHTMLmsg =  jsonData ? str + this.prettyPrintJson(JSON.stringify(jsonData, undefined, 4)) : str;

        const contentOrigStripped = str.replace(/(<([^>]+)>)/ig, '');               // <= strips from ALL HTML tags...
        const contentStripped = this.contentHTMLmsg.replace(/(<([^>]+)>)/ig, '');

        console.error('SHOULD PRINT (resumed):', contentOrigStripped.slice(contentOrigStripped.length - 40));
    }

}

在这里,控制台结果(5 缺失,虽然订阅$$$$$$$$得到它们):

在此处输入图像描述

谢谢!

4

1 回答 1

0

事情并没有那么简单,当我们有几毫秒 的变化时......

我按照你的想法做了很多测试,我认为 Angular 在组件的生命周期钩子及其@Input()s 上的更改方面存在问题。

关于parent-component.html

尝试1:

<ng3d-monitor-debug [goToTopPosition]="monitorMarginTop"></ng3d-monitor-debug>

尝试2:

<ng3d-monitor-debug [goToTopPosition]="monitorMarginTop" [dataToPrint]="loggEvent"></ng3d-monitor-debug>

尝试 3,也是最后一个,证明有效:

<ng3d-monitor-debug [goToTopPosition]="monitorMarginTop" [dataToPrint]="(loggEvent$ | async)"></ng3d-monitor-debug>

所以:
尝试 1时,子组件直接从this.THreeJSloggerService.newEvtLogged$; subject. 没有@Input()
结果:在安装时,子组件显然完成了它的工作:记录的消息在 Template var 上呈现contentHTMLmsg。但是,安装后,记录的字符串开始消失......

尝试 2时,我们传入了一个简单的量值,来自父母对this.THreeJSloggerService.newEvtLogged$; subject.
结果:在安装 Child 时没有完整呈现 Template var contentHTMLmsg(屏幕刚刚冻结!),并且在 之后ngOnChanges(),仍然会发生缺失。

尝试 3中,我尝试了一件疯狂的事情,这种想法永远不会奏效——但它确实奏效了!
在安装子组件时,通过订阅它,从 Service 获取记录的字符串,Subject然后,一旦子组件的安装完成,由于父组件的订阅Subject仍然处于活动状态,管道 [dataToPrint]="(loggEvent$ | async)"被捕获,这一次,由孩子的ngOnChanges()
并且完美地呼应了 Child's Template 变量contentHTMLmsg。;-)

一个明显的问题出现了:安装后,我看到重复的消息......孩子订阅发送的消息和父母订阅发送的第二个消息。

“所以……现在它起作用了?在两个周期中……?” - 是我的想法
是的!

因此,保留父级的 HTML(即保留子级的@Input()管道记录的 msgs subscription),我只是评论了子级的 ngOnChanges() 代码,它与幽灵消息相呼应 - 并且记录的消息的重复呈现不再存在。

一切都很好!

我的理论是:只要你声明一个@Input() 的注入,即使你不在 Child 上关心它ngOnChanges(),仅仅因为它是一个声明的入口,并且对于非常快速的变化,Angular 仍然会寻找变化, 在同一个变量上,即使它们来自其他来源 - 而不是来自@Input().

啊! 还有一件事。当我们和他一起玩这个游戏时,
Angular 会感到某种痛苦……得到安慰的错误并不令人意外:
ERROR Error: "ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: '@ 03:31:44.353: ====== RENDERing of an Object done!Orbit Control initialization'. It seems like the view has been created after its parent and its children have been dirty checked. Has it been created in a change detection hook?"

它在所有安装完成之前被抛出。

真正的现场证明可以在这里看到:http:
//ng3d.miles-net.com/obj
一件事:
这是我仍在努力的事情......到目前为止,没有“加载轮”,你可以看到'3 楼办公室'' 3D 建筑,您必须等待 30 秒到 1 分钟 - 很大程度上取决于机器。

但重要的是看到每个记录的消息都打印在监视器上,并且还可以检查时间。
毕竟......这是一个角度问题!我再说一遍,当我们处理毫秒变化时。

你对此有什么看法……?
非常感谢@Random!

于 2020-05-12T03:28:54.977 回答