Как обновить список ngFor, когда он был изменен событием окна? - PullRequest
0 голосов
/ 14 января 2019

Я создаю приложение Ionic для Angular, и мне нужно читать штрих-коды с помощью физического считывателя штрих-кодов.
Этот считыватель обычно похож на физическую клавиатуру, которая отправляет событие клавиатуры после считывания штрих-кода.
Поэтому я создал скрипт для захвата события Keyboard на объекте window, потому что пользователь может читать штрих-код за пределами входных данных, а я преобразую события клавиатуры в наблюдаемые, которые будут «излучать» полный штрих-код. Этот скрипт работает, как и ожидалось, но я говорю об этом, потому что я думаю, что есть связь с моей проблемой ...
В файле TypeScript моей страницы (Ionic Page, угловой компонент) я подписываюсь на свою наблюдаемую (ту, о которой я говорил ранее…).
Подписка очень простая, я просто добавляю штрих-код в Set после того, как сделал несколько шагов проверки. Или эти шаги возвращают обещание, и я добавляю штрих-код, когда обещание выполнено…
Набор штрих-кодов отображается в html-файле в цикле ngFor.
Когда считыватель штрих-кода считывает штрих-код, он добавляется в набор, но пользовательский интерфейс не обновляется ...
Я почти уверен, что что-то пропустил, и, может быть, речь идет о NgZone, которого я не знаю ...

Если я добавляю штрих-код без вызова шагов проверки (асинхронный код не вызывается), пользовательский интерфейс обновляется.
Я также попытался вызвать шаги проверки с помощью жестко запрограммированной кнопки, которая имитирует сканер штрих-кода, и это сработало ...
Таким образом, проблема заключается в том, что штрих-код добавляется после того, как обещание выполнено И штрих-код получен от Наблюдателя, созданного окном События клавиатуры…

Для наблюдения за читателем кодовой строки:

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. В этом классе я использую его, чтобы проверить, известен ли штрих-код для объекта 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>

Когда я нажимаю кнопку «плюс», штрих-код добавляется в список и обновляется пользовательский интерфейс.
Когда я сканирую штрих-код с помощью физического сканера штрих-кода, штрих-код добавляется, но пользовательский интерфейс не обновляется.
Я ожидал одинакового поведения в обоих режимах ...
Я думаю, что это может быть проблема NgZone, но я не эксперт по этому поводу ...
Я думаю, что что-то пропустил, но что ...

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Angular будет прослушивать только изменения, определенные в его рамках. Поскольку Window является нативным объектом, Angular не прослушивает его изменения и не обновляет представление. Либо используйте ChangeDetectorRef, чтобы вручную обновить представление, либо создайте угловой сервис из окна, например так: https://stackoverflow.com/a/37176929/5108158.

Edit: Должно работать следующее и корректно обновлять изменения:

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);
    })
  }
}

Это будет работать в любом компоненте, но лучше всего в компоненте приложения верхнего уровня.

0 голосов
/ 14 января 2019

Вы можете использовать ChangeDetectorRef, встроенный в angular, поместить его в конструктор, скажем, private CD: ChangeDetectorRef, и затем вы можете использовать его для изменения представления, когда вы выполняете код для изменения массива в ngFor, вы можете ввести метод this.CD.detectChanges() это обнаружит изменения и обновит ваш пользовательский интерфейс в угловых и ионных приложениях

...