Как обновить список ngFor, когда он был изменен событием окна?
Я создаю приложение 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 ответа
Вы можете использовать ChangeDetectorRef, встроенный в angular, поместив его в конструктор скажем private CD: ChangeDetectorRef
а затем вы можете использовать его для изменения представления, когда ваш код выполняется для изменения массива в ngFor
Вы можете ввести метод this.CD.detectChanges()
это обнаружит изменения и обновит ваш пользовательский интерфейс в угловых и ионных приложениях
Angular будет прослушивать только изменения, определенные в его рамках. Поскольку Window является нативным объектом, Angular не прослушивает его изменения и не обновляет представление. Либо используйте ChangeDetectorRef, чтобы вручную обновить представление, либо создайте угловой сервис из окна, например так: /questions/9439496/angular2-kak-dobavit-okno-v-sluzhbu-angular2/9439512#9439512.
Изменить: следующее должно работать, и обновлять изменения правильно:
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);
})
}
}
Это будет работать с любым компонентом, но лучше всего с компонентом приложения верхнего уровня.