Dans ce scénario, j'affiche une liste d'étudiants (tableau) dans la vue avec ngFor
:
<li *ngFor="#student of students">{{student.name}}</li>
C'est merveilleux qu'il se mette à jour chaque fois que j'ajoute un autre étudiant à la liste.
Cependant, lorsque je lui donne un pipe
à filter
par le nom de l'élève,
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
Il ne met pas à jour la liste tant que je n'ai pas tapé quelque chose dans le champ de filtrage du nom de l'élève.
Voici un lien vers plnkr .
Hello_world.html
<h1>Students:</h1>
<label for="newStudentName"></label>
<input type="text" name="newStudentName" placeholder="newStudentName" #newStudentElem>
<button (click)="addNewStudent(newStudentElem.value)">Add New Student</button>
<br>
<input type="text" placeholder="Search" #queryElem (keyup)="0">
<ul>
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
</ul>
sort_by_name_pipe.ts
import {Pipe} from 'angular2/core';
@Pipe({
name: 'sortByName'
})
export class SortByNamePipe {
transform(value, [queryString]) {
// console.log(value, queryString);
return value.filter((student) => new RegExp(queryString).test(student.name))
// return value;
}
}
pure:false
votre Pipe etchangeDetection: ChangeDetectionStrategy.OnPush
votre Component..test()
dans votre fonction de filtre. C'est parce que, si l'utilisateur entre une chaîne qui comprend des caractères spéciaux comme:*
ou+
etc., votre code se cassera. Je pense que vous devriez utiliser.includes()
ou échapper une chaîne de requête avec une fonction personnalisée.pure:false
et rendre votre pipe avec état résoudra le problème. La modification de ChangeDetectionStrategy n'est pas nécessaire.Réponses:
Pour bien comprendre le problème et les solutions possibles, nous devons discuter de la détection de changement angulaire - pour les tuyaux et les composants.
Détection de changement de tuyau
Tuyaux sans état / purs
Par défaut, les tubes sont sans état / purs. Les tuyaux sans état / purs transforment simplement les données d'entrée en données de sortie. Ils ne se souviennent de rien, donc ils n'ont aucune propriété - juste une
transform()
méthode. Angular peut donc optimiser le traitement des tuyaux sans état / purs: si leurs entrées ne changent pas, les tuyaux n'ont pas besoin d'être exécutés lors d'un cycle de détection de changement. Pour un tuyau tel que{{power | exponentialStrength: factor}}
,power
etfactor
sont des entrées.Pour cette question,
"#student of students | sortByName:queryElem.value"
,students
etqueryElem.value
sont entrées, et le tuyausortByName
est sans état / pur.students
est un tableau (référence).students
ne change pas - donc le tube sans état / pur n'est pas exécuté.queryElem.value
change, donc le tube sans état / pur est exécuté.Une façon de résoudre le problème du tableau est de changer la référence du tableau chaque fois qu'un étudiant est ajouté - c'est-à-dire de créer un nouveau tableau chaque fois qu'un étudiant est ajouté. Nous pourrions le faire avec
concat()
:Bien que cela fonctionne, notre
addNewStudent()
méthode ne devrait pas avoir à être implémentée d'une certaine manière simplement parce que nous utilisons un tube. Nous voulons utiliserpush()
pour ajouter à notre tableau.Tuyaux avec état
Les tuyaux avec état ont un état - ils ont normalement des propriétés, pas seulement une
transform()
méthode. Ils peuvent avoir besoin d'être évalués même si leurs entrées n'ont pas changé. Lorsque nous spécifions qu'un tube est avec état / non pur -pure: false
- alors chaque fois que le système de détection de changement d'Angular vérifie un composant pour les modifications et que ce composant utilise un tube avec état, il vérifie la sortie du tube, si son entrée a changé ou non.Cela ressemble à ce que nous voulons, même si c'est moins efficace, car nous voulons que le tube s'exécute même si la
students
référence n'a pas changé. Si nous rendons simplement le tube avec état, nous obtenons une erreur:Selon la réponse de @ drewmoore , "cette erreur se produit uniquement en mode dev (qui est activé par défaut à partir de la version bêta-0). Si vous appelez
enableProdMode()
lors du démarrage de l'application, l'erreur ne sera pas levée." La documentation pour l'ApplicationRef.tick()
état:Dans notre scénario, je pense que l'erreur est fausse / trompeuse. Nous avons un tube avec état, et la sortie peut changer chaque fois qu'elle est appelée - cela peut avoir des effets secondaires et ce n'est pas grave. NgFor est évalué après le tube, donc cela devrait fonctionner correctement.
Cependant, nous ne pouvons pas vraiment développer avec cette erreur lancée, donc une solution de contournement consiste à ajouter une propriété de tableau (c'est-à-dire, un état) à l'implémentation du tube et à toujours renvoyer ce tableau. Voir la réponse de @ pixelbits pour cette solution.
Cependant, nous pouvons être plus efficaces, et comme nous le verrons, nous n'aurons pas besoin de la propriété array dans l'implémentation du tube, et nous n'aurons pas besoin d'une solution de contournement pour la détection de double changement.
Détection de changement de composant
Par défaut, à chaque événement du navigateur, la détection de changement angulaire passe par chaque composant pour voir s'il a changé - les entrées et les modèles (et peut-être d'autres choses?) Sont vérifiés.
Si nous savons qu'un composant ne dépend que de ses propriétés d'entrée (et des événements de modèle), et que les propriétés d'entrée sont immuables, nous pouvons utiliser la
onPush
stratégie de détection des changements beaucoup plus efficace . Avec cette stratégie, au lieu de vérifier chaque événement du navigateur, un composant est vérifié uniquement lorsque les entrées changent et lorsque les événements de modèle se déclenchent. Et, apparemment, nous n'obtenons pas cetteExpression ... has changed after it was checked
erreur avec ce paramètre. Cela est dû au fait qu'unonPush
composant n'est pas vérifié à nouveau tant qu'il n'est pas à nouveau "marqué" (ChangeDetectorRef.markForCheck()
). Ainsi, les liaisons de modèle et les sorties de canal avec état ne sont exécutées / évaluées qu'une seule fois. Les tuyaux sans état / purs ne sont toujours pas exécutés à moins que leurs entrées ne changent. Nous avons donc encore besoin d'un tube avec état ici.C'est la solution proposée par @EricMartinez: pipe avec état avec
onPush
détection de changement. Voir la réponse de @ caffinatedmonkey pour cette solution.Notez qu'avec cette solution, la
transform()
méthode n'a pas besoin de renvoyer le même tableau à chaque fois. Je trouve cela un peu étrange cependant: un tube avec état sans état. En y réfléchissant un peu plus ... le tube avec état devrait probablement toujours renvoyer le même tableau. Sinon, il ne peut être utilisé qu'avec desonPush
composants en mode développement.Donc après tout ça, je pense que j'aime une combinaison des réponses de @ Eric et @ pixelbits: pipe avec état qui renvoie la même référence de tableau, avec
onPush
détection de changement si le composant le permet. Étant donné que le tube avec état renvoie la même référence de tableau, le tube peut toujours être utilisé avec des composants qui ne sont pas configurés aveconPush
.Plunker
Cela deviendra probablement un idiome Angular 2: si un tableau alimente un tube et que le tableau peut changer (les éléments du tableau qui sont, pas la référence du tableau), nous devons utiliser un tube avec état.
la source
onPush
détection de changement plnkr.co/edit/gRl0Pt9oBO6038kCXZPk?p=previewComme l'a souligné Eric Martinez dans les commentaires, l'ajout
pure: false
à votrePipe
décorateur etchangeDetection: ChangeDetectionStrategy.OnPush
à votreComponent
décorateur résoudra votre problème. Voici un plunkr fonctionnel. Changer enChangeDetectionStrategy.Always
, fonctionne également. Voici pourquoi.Selon le guide angular2 sur les tuyaux :
En ce qui concerne le
ChangeDetectionStrategy
, par défaut, toutes les liaisons sont vérifiées à chaque cycle. Lorsqu'unpure: false
tuyau est ajouté, je pense que la méthode de détection des modifications passe deCheckAlways
àCheckOnce
pour des raisons de performances. AvecOnPush
, les liaisons du composant ne sont vérifiées que lorsqu'une propriété d'entrée change ou lorsqu'un événement est déclenché. Pour plus d'informations sur les détecteurs de changement, une partie importante deangular2
, consultez les liens suivants:la source
pure: false
tube est ajouté, je crois que la méthode de détection des modifications passe de CheckAlways à CheckOnce» - cela ne correspond pas à ce que vous avez cité dans la documentation. Ma compréhension est la suivante: les entrées d'un tuyau sans état sont vérifiées à chaque cycle par défaut. S'il y a un changement, latransform()
méthode du tube est appelée pour (re) générer la sortie. Latransform()
méthode d'un tube avec état est appelée à chaque cycle et sa sortie est vérifiée pour les modifications. Voir également stackoverflow.com/a/34477111/215945 .OnPush
(c'est- à -dire que le composant est marqué comme "immuable"), la sortie du tube avec état n'a pas à "stabiliser" - c'est-à-dire qu'il semble que latransform()
méthode ne soit exécutée qu'une seule fois (probablement toutes la détection des modifications pour le composant n'est exécutée qu'une seule fois). Comparez cela à la réponse de @ pixelbits, où latransform()
méthode est appelée plusieurs fois et qu'elle doit se stabiliser (selon l' autre réponse de pixelbits ), d'où la nécessité d'utiliser la même référence de tableau.transform
n'est appelée que lorsque de nouvelles données sont poussées au lieu de plusieurs fois jusqu'à ce que la sortie se stabilise, non?Démo Plunkr
Vous n'avez pas besoin de modifier la ChangeDetectionStrategy. L'implémentation d'un Pipe avec état suffit pour que tout fonctionne.
Ceci est un tube avec état (aucune autre modification n'a été apportée):
la source
Expression 'students | sortByName:queryElem.value in HelloWorld@7:6' has changed after it was checked. Previous value: '[object Object],[object Object],[object Object]'. Current value: '[object Object],[object Object],[object Object]' in [students | sortByName:queryElem.value in HelloWorld@7:6]
De la documentation angulaire
Tuyaux purs et impurs
Il existe deux catégories de tuyaux: purs et impurs. Les tuyaux sont purs par défaut. Chaque pipe que vous avez vue jusqu'à présent est pure. Vous rendez un tuyau impur en définissant son drapeau pur sur false. Vous pourriez rendre le FlyingHeroesPipe impur comme ceci:
@Pipe({ name: 'flyingHeroesImpure', pure: false })
Avant de faire cela, comprenez la différence entre pur et impur, en commençant par une pipe pure.
Pure pipes Angular exécute un pur tube uniquement lorsqu'il détecte un changement pur de la valeur d'entrée. Un changement pur est soit une modification d'une valeur d'entrée primitive (chaîne, nombre, booléen, symbole) ou une référence d'objet modifiée (date, tableau, fonction, objet).
Angular ignore les changements dans les objets (composites). Il n'appellera pas de canal pur si vous modifiez un mois d'entrée, ajoutez à un tableau d'entrée ou mettez à jour une propriété d'objet d'entrée.
Cela peut sembler restrictif mais c'est aussi rapide. Une vérification de référence d'objet est rapide - beaucoup plus rapide qu'une vérification approfondie des différences - donc Angular peut rapidement déterminer s'il peut ignorer à la fois l'exécution du tuyau et une mise à jour de vue.
Pour cette raison, un tuyau pur est préférable lorsque vous pouvez vivre avec la stratégie de détection des changements. Lorsque vous ne pouvez pas, vous pouvez utiliser le tuyau impur.
la source
Au lieu de faire pur: faux. Vous pouvez copier en profondeur et remplacer la valeur du composant par this.students = Object.assign ([], NEW_ARRAY); où NEW_ARRAY est le tableau modifié.
Cela fonctionne pour angular 6 et devrait également fonctionner pour d'autres versions angulaires.
la source
Solution de contournement: importez manuellement le tuyau dans le constructeur et appelez la méthode de transformation à l'aide de ce tuyau
En fait, tu n'as même pas besoin de pipe
la source
Ajoutez un paramètre supplémentaire au tube et modifiez-le juste après le changement de tableau, et même avec un tube pur, la liste sera actualisée
laisser un article | tuyau: paramètre
la source
Dans ce cas d'utilisation, j'ai utilisé mon fichier Pipe in ts pour le filtrage des données. C'est bien meilleur pour les performances que d'utiliser des tuyaux purs. Utilisez dans ts comme ceci:
la source
créer des tuyaux impurs coûte cher en performance. donc ne créez pas de tuyaux impurs, modifiez plutôt la référence de la variable de données en créant une copie des données lors du changement de données et réaffectez la référence de copie dans la variable de données d'origine.
la source