0

我用 Angular 制作了一个 Ionic 应用程序,我需要用物理条形码阅读器读取条形码。
这个阅读器通常就像一个物理键盘,它在读取条形码后发送键盘事件。
所以我制作了一个脚本来捕获窗口对象上的键盘事件,因为用户可以在输入之外读取条形码,并且我将键盘事件转换为可观察的,这将“发出”完整的条形码。该脚本按预期工作,但我谈论它是因为我认为存在与我的问题的链接......<br>在我页面的 TypeScript 文件(离子页面,一个角度组件)中,我订阅了我的 observable(我谈到的那个)关于以前……)。
订阅很简单,我只是在做了一些检查步骤后将条形码添加到 Set<string> 中。或者,这些步骤返回一个 Promise,当 Promise 解决时我添加条形码……<br> 条形码集显示在 ngFor 循环中的 html 文件中。
当条形码阅读器读取条形码时,它被添加到集合中,但 UI 没有刷新……<br> 我很确定我错过了一些东西,也许是关于 NgZone,我真的不知道……</p>

如果我添加条形码而不调用检查步骤(未调用异步代码),则 UI 将刷新。
我还尝试使用模拟条形码扫描仪的硬编码按钮调用检查步骤,并且它可以工作......<br>所以问题是在承诺解决后添加条形码并且条形码来自窗口键盘事件制作的观察者…</p>

观察码条阅读器:

export namespace LecteurCodebarrePhysique {
    // L'évènement est-il dans un input ?
    const inInput = (event) => {return event.target instanceof Element && event.target.nodeName.toLowerCase() === 'input'};
    // La touche relachée est-elle un caractère ?
    const isTextKey = (event) => {return !inInput(event) && event.key.length === 1};
    // La touche relachée est-elle la touche entrée ?
    const isEnter = (event) => {return !inInput(event) && event.keyCode === 13};

    /**
     * Observable émettant le codebarre lu par un lecteur physique
     */
    export function codebarreLu(): Observable<{text: string, format: string}> {
        // Observable initiale : évèrement clavier
        const keyup: Observable<KeyboardEvent> = fromEvent(window, 'keyup');
        return keyup.pipe(
            // On ne garde que les touches représentant un caractère
            filter(ev => isTextKey(ev)),
            // On ne garde que la valeur du caractère
            map(ev => ev.key),
            // On «bufferise» en attendant la touche entrée
            buffer(keyup.pipe(filter(ev => {
                const enter = isEnter(ev);
                if (enter) {
                    ev.preventDefault();
                    ev.stopPropagation();
                }
                return enter;
            }))),
            // Quand la touche entrée est relachée, on concatène les caractères
            // Et on essaye de déterminer si c'es un EAN13 (13 caractères numériques)
            map(chars => {
                const codebarre = chars.reduce((code, char) => code + char, '');
                const isEan13 = /\d{13}/.test(codebarre);
                return {text: codebarre, format: isEan13 ? 'EAN_13' : 'INCONNU'};
            })
        );
    }
}  

页面的 TypeScript 文件(MArticle 是一个对 Article 对象有不同方法的服务。在这个类中,我使用它来检查 DB 中的 Article 对象上是否已经知道条形码):

export class ArticlesNouveauPage {
    codebarres = new Set<string>();
    codebarreLuSub: Subscription;
    article = new Article();

    constructor(private mArticle: MArticle) {}

    ionViewWillEnter() {
        // On souscrit à la letcure de codebarre physique
        this.codebarreLuSub = LecteurCodebarrePhysique.codebarreLu().subscribe(resultat => this.ajouterCodebarre(resultat));
    }

    ionViewWillLeave() {
        // Quand on quitte l'écran on ne souscrit plus à la lecture des codebarres physiques
        this.codebarreLuSub.unsubscribe();
    }

    /**
     * Ajout d'un codebarre
     * @param resultat
     */
    private ajouterCodebarre(resultat: {text: string}) {
        // If an «Article» object is found with the barcode, we show an error message
        return this.mArticle.getInstanceByGtin(resultat.text)
            .then(article => {
                    this.tools.afficherMessage(`Le codebarre ${resultat.text} est déjà assigné à l'article "${article.libelle}" !`);
            })
            .catch(() => {
                // If the promise is rejected, the barcode is unknown, we can add it to the list
                this.addCodebarreToList(resultat.text);
            });
    }

    private addCodebarreToList(codebarre: string) {
        this.codebarres.add(codebarre);
    }

    testAddBarcode() {
        this.ajouterCodebarre({text: `1234567890123`});
    }
}

页面的 HTML 代码:

<ion-content >
    <form #f="ngForm">
        <ion-item-group>
            <ion-item-divider color="secondary">Article</ion-item-divider>
            <ion-item>
                <ion-label color="primary" fixed>Libellé</ion-label>
                <ion-input type="text" [(ngModel)]="article.libelle" name="libelle" required></ion-input>
            </ion-item>
            <ion-item>
                <ion-label color="primary" fixed>Prix</ion-label>
                <ion-input type="number" [(ngModel)]="article.prix" name="prix" required></ion-input>
            </ion-item>
            <ion-item>
                <ion-label color="primary" fixed>Code</ion-label>
                <ion-input type="text" [(ngModel)]="article.code" name="code"></ion-input>
            </ion-item>
        </ion-item-group>
    </form>

    <ion-item-group>
        <ion-item-divider color="secondary">
            Codebarres associés
        </ion-item-divider>
        <ion-item *ngFor="let codebarre of codebarres">
            <ion-icon name="barcode" item-start color="secondary"></ion-icon>
            <h2>{{codebarre}}</h2>
        </ion-item>
    </ion-item-group>

    <ion-fab left bottom>
        <button ion-fab color="danger" (click)="testAddBarcode()"><ion-icon name="add"></ion-icon></button>
    </ion-fab>
</ion-content>

当我单击«加号»按钮时,条形码被添加到列表中并且 UI 被刷新。
当我使用物理条码扫描仪扫描条码时,会添加条码,但不会刷新 UI。
我确实期望两种模式之间的行为相同......<br>我认为这可能是一个 NgZone 问题,但我不是这方面的专家......<br>我想我错过了一些东西,但是什么......</p>

4

2 回答 2

2

您可以使用以角度构建的 ChangeDetectorRef,将其放入构造函数中private CD: ChangeDetectorRef ,然后您可以使用它来更改视图,当您执行代码以更改数组时,ngFor您可以键入this.CD.detectChanges()将检测更改并刷新您的方法UI 在 Angular 和 ionic 应用程序中

于 2019-01-14T12:32:09.897 回答
0

Angular 只会监听其框架内定义的更改。由于 Window 是一个原生对象,Angular 不会监听它的变化并更新视图。要么使用 ChangeDetectorRef 手动更新视图,要么在窗口外创建一个角度服务,如下所示:https ://stackoverflow.com/a/37176929/5108158 。

编辑:以下应该有效,并正确更新更改:

import { Component, Renderer } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
  constructor(private renderer: Renderer) {};

  ngAfterViewInit() {
    this.renderer.listenGlobal('body', 'keypress', (event) => {
      console.log(event);
    })
  }
}

这适用于任何组件,但最好在顶级应用程序组件中使用。

于 2019-01-14T13:19:59.223 回答