NgFor n'a pas de mise à jour des données avec un Tuyau dans Angular2
Dans ce scénario, j'affiche une liste des étudiants (tableau) à la vue avec ngFor
:
<li *ngFor="#student of students">{{student.name}}</li>
C'est merveilleux qu'il met à jour à chaque fois que j'ajoute d'autres étudiants à la liste.
Cependant, quand je lui donne un pipe
à filter
par le nom de l'étudiant,
<li *ngFor="#student of students | sortByName:queryElem.value ">{{student.name}}</li>
Il ne met pas à jour la liste jusqu'à ce que je tape quelque chose dans le filtrage de l'étudiant nom de domaine.
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;
}
}
- Ajouter
pure:false
dans votre Pipe, etchangeDetection: ChangeDetectionStrategy.OnPush
dans votre Composant. - Merci @EricMartinez. Elle fonctionne. Mais pouvez-vous expliquer un peu?
- Aussi, je suggère de ne PAS utiliser
.test()
dans votre fonction de filtre. Ses parce que, si les entrées de l'utilisateur une chaîne qui comprend la signification particulière des personnages comme:*
ou+
etc. votre code de casser. Je pense que vous devriez utiliser.includes()
ou de l'évasion de la chaîne de requête avec la fonction personnalisée. - L'ajout de
pure:false
et de faire de votre tuyau stateful résoudre le problème. La modification de ChangeDetectionStrategy n'est pas nécessaire. - Pour tous ceux qui lisent ceci, la documentation Angulaire des Tuyaux a obtenu beaucoup mieux et va sur un grand nombre des mêmes choses discutées ici. Check it out.
Vous devez vous connecter pour publier un commentaire.
Pour bien comprendre le problème et les solutions possibles, nous avons besoin de discuter Angulaire de la détection de changement -- pour les tuyaux et les composants.
Tuyau De Détection De Changement De
Apatrides/pure Tuyaux
Par défaut, les tuyaux sont apatrides/pure. Apatrides/pure tuyaux de transformer les données d'entrée en données de sortie. Ils ne me souviens pas de quoi que ce soit, de sorte qu'ils n'ont pas de propriétés – juste une
transform()
méthode. Angulaire peut donc optimiser le traitement des apatrides/pure tuyaux: si leurs entrées ne changent pas, les tuyaux ne doivent pas être exécutées lors d'un changement de cycle de détection. Pour une pipe comme{{power | exponentialStrength: factor}}
,power
etfactor
sont entrées.Pour cette question,
"#student of students | sortByName:queryElem.value"
,students
etqueryElem.value
sont des entrées, et le tuyausortByName
est apatride/pure.students
est un tableau (de référence).students
ne change pas, donc les apatrides/pure pipe n'est pas exécutée.queryElem.value
n'changement, d'où les apatrides/pure pipe est exécutée.Un moyen de corriger le tableau enjeu est de changer le tableau de référence chaque fois qu'un élève est ajoutée, c'est à dire, 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 être mis en œuvre d'une certaine façon, juste parce que nous faisons à l'aide d'un tuyau. Nous voulons utiliserpush()
à ajouter à notre tableau.Stateful Tuyaux
Avec état des tuyaux de l'état est -- ils ont normalement des propriétés, et pas seulement un
transform()
méthode. Ils peuvent avoir besoin d'être évalué même si leurs contributions n'ont pas changé. Quand nous indiquons qu'un canal est dynamique/non-pure –pure: false
– quand Angulaire de détection de changement de système vérifie la présence d'un composant pour les changements et le composant utilise avec un état de la pipe, il va vérifier la sortie de la pipe, que son entrée a changé ou pas.Cela ressemble à ce que nous voulons, même si c'est moins efficace, puisque nous voulons que la pipe à exécuter même si l'
students
de référence n'a pas changé. Si nous nous contentons de faire la conduite dynamique, nous obtenons une erreur:Selon @drewmoore réponse, "cette erreur se produit uniquement en dev mode (qui est activé par défaut sous forme de bêta-0). Si vous appelez
enableProdMode()
quand le démarrage de l'application, l'erreur à ne pas faire jeter." Le docs pourApplicationRef.tick()
état:Dans notre scénario, je crois que l'erreur est fausse ou trompeuse. Nous avons avec un état de la pipe, et la sortie peut changer à chaque fois qu'il est appelé, il peut avoir des effets secondaires, et c'est correct. NgFor est évaluée après la pipe, il devrait fonctionner correctement.
Cependant, nous ne pouvons pas vraiment se développer avec cette erreur est générée, donc une solution de contournement consiste à ajouter un tableau de propriété (c'est à dire, de l'état) pour le tuyau de mise en œuvre et toujours le retour de ce tableau. Voir @pixelbits réponse à cette solution.
Cependant, nous pouvons être plus efficaces, et comme nous allons le voir, nous n'aurons pas besoin de la propriété du tableau dans le tuyau de mise en œuvre, et nous n'avons pas besoin d'une solution de contournement pour le double de la détection de changement.
Composant De Détection De Changement De
Par défaut, sur chaque navigateur d'événements, angle de détection de changement passe par tous les composants pour voir si elle a changé – les entrées et les modèles (et peut-être d'autres choses?) sont vérifiées.
Si nous savons qu'un composant ne dépend que de ses propriétés d'entrée (et le modèle d'événements), et que les propriétés d'entrée sont immuables, nous pouvons utiliser le beaucoup plus efficace
onPush
détection de changement de stratégie. Avec cette stratégie, au lieu de vérifier sur chaque navigateur d'événements, un composant est vérifiée que lorsque les entrées changement et quand le modèle d'événements de déclenchement. Et, apparemment, nous n'avons pas cetteExpression ... has changed after it was checked
erreur avec ce paramètre. C'est parce qu'unonPush
composant n'est pas vérifié à nouveau jusqu'à ce qu'il est "marqué" (ChangeDetectorRef.markForCheck()
) de nouveau. Donc Modèle de liaisons dynamique pipe sorties sont exécutés/évaluées qu'une seule fois. Apatrides/pure tuyaux ne sont pas encore exécutés à moins que leurs entrées changement. Nous avons donc encore besoin d'un stateful pipe ici.C'est la solution @EricMartinez suggéré: stateful pipe avec
onPush
la détection de changement. Voir @caffinatedmonkey réponse à cette solution.Notez qu'avec cette solution, les
transform()
méthode n'a pas besoin de retourner le même tableau à chaque fois. Je trouve cela un peu bizarre mais: avec un état de tuyau sans état. D'y penser un peu plus... une pipe sans doute doit toujours retourner le même tableau. Sinon, il pourrait être utilisé uniquement aveconPush
composants en dev mode.Donc après tout ça, je crois que j'aime une combinaison de @Eric et @pixelbits réponses: stateful tuyau qui renvoie la même référence de tableau, avec
onPush
de la détection de changement si la pièce le permet. Depuis une pipe retourne la même matrice de référence, le tuyau peut toujours être utilisé avec des composants qui ne sont pas configurés aveconPush
.Plunker
Cela va probablement devenir Angulaire à 2 idiome: si un tableau est l'alimentation d'un tuyau, et le tableau peut changer (les éléments du tableau qui est, non pas le tableau de référence), nous devons utiliser avec un état de conduite.
onPush
détection de changement de plnkr.co/modifier/gRl0Pt9oBO6038kCXZPk?p=previewComme Eric Martinez a souligné dans les commentaires, ajout de
pure: false
à votrePipe
décorateur etchangeDetection: ChangeDetectionStrategy.OnPush
à votreComponent
décorateur permettra de résoudre votre problème. Ici, c'est un travail plunkr. Changer deChangeDetectionStrategy.Always
, fonctionne aussi. Voici pourquoi.Selon le angular2 guide sur les tubes:
Comme pour le
ChangeDetectionStrategy
, par défaut, tous les liens sont vérifiés chaque cycle. Lorsqu'unpure: false
pipe est ajouté, je crois que le changement de méthode de détection de changements à partir deCheckAlways
àCheckOnce
pour des raisons de performances. AvecOnPush
, des fixations pour le Composant ne sont vérifiées lors de l'entrée des changements de propriété ou lorsqu'un événement est déclenché. Pour plus d'informations sur la modification des détecteurs, une partie importante deangular2
, consultez les liens suivants:pure: false
pipe est ajouté, je crois que le changement de méthode de détection de changements à partir de CheckAlways à CheckOnce" -- qui n'est pas d'accord avec ce que vous avez cité de la documentation. Ma compréhension est comme suit: un apatride du tuyau entrées sont vérifiées à chaque cycle par défaut. Si il y a un changement, le tuyau detransform()
méthode est appelée à se (re)générer la sortie. Avec un état de la canalisation detransform()
méthode est appelée à chaque cycle, et sa sortie est vérifié pour les changements. Voir aussi stackoverflow.com/a/34477111/215945.OnPush
(c'est à dire, le composant est marqué comme "immuable"), une pipe de sortie n'a pas à "stabiliser" -- c'est à dire, il semble que letransform()
méthode est exécutée qu'une seule fois (probablement tous de la détection de changement pour le composant est exécuté qu'une seule fois). Comparez cela à @pixelbits de réponse, où latransform()
méthode est appelée plusieurs fois, et il a pour stabiliser (selon pixelbits du réponse), 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, à droite?transform()
retour le même tableau de référence (de sorte qu'il peut être utilisé avec n'importe quel composant), l'utilisationonPush
si la pièce le permet. J'ai mis à jour ma réponse à discuter un peu.Démo Plunkr
Vous n'avez pas besoin de changer le ChangeDetectionStrategy. Mise en œuvre d'une dynamique de la Pipe est assez pour faire tout.
C'est avec un état de tuyau (pas d'autres modifications ont été apportées):
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 angulaire de la documentation
Pure et impure tuyaux
Il y a deux catégories de tuyaux: pur et de l'impur. Les tuyaux sont de la pure par défaut. Tous les tuyaux que vous avez vu jusqu'à présent a été pur. Vous faire une pipe impur par la mise en pur drapeau à false. Vous pourriez faire le FlyingHeroesPipe impur comme ceci:
@Pipe({
name: 'flyingHeroesImpure',
pure: false
})
Avant de faire cela, de comprendre la différence entre le pur et l'impur, à commencer par une pure pipe.
Pur tuyaux
Angulaire exécute une pure pipe uniquement lorsqu'il détecte un pur changement de la valeur d'entrée. Un pur changement est un changement à une primitive de la valeur d'entrée (String, Number, Boolean, Symbole) ou un changement de l'objet de référence (la Date, le Tableau, la Fonction de l'Objet).
Angulaire ignore les changements à l'intérieur (composite) des objets. Il ne le demandera pas une pure pipe si vous modifiez une entrée de mois, l'ajouter à un tableau d'entrée, ou de mettre à jour une entrée de propriété de l'objet.
Cela peut paraître restrictif, mais il est aussi très rapide. Une référence d'objet de vérifier rapidement, beaucoup plus vite qu'un profond vérifier les différences de manière Angulaire pouvez rapidement déterminer si elle peut passer à la fois le tuyau d'exécution et d'une vue de mise à jour.
Pour cette raison, une pure pipe est préférable si vous pouvez vivre avec la détection de changement de stratégie. Lorsque vous ne pouvez pas, vous pouvez utiliser l'impur pipe.
Une solution: importer Manuellement des Tuyaux dans le constructeur et l'appel de méthode de transformation à l'aide de cette pipe
Fait, vous n'avez même pas besoin d'un tuyau
Au lieu de faire du pur:c'est faux. Vous pouvez copie en profondeur et de remplacer la valeur du composant par cette.les étudiants = Objet.attribuer([], NEW_ARRAY); où NEW_ARRAY est le tableau modifié.
Il fonctionne angulaire 6 et devrait fonctionner pour les autres angulaire de versions que de bien.
Ajouter à la pipe paramètre supplémentaire, et de le modifier à droite après la matrice de changement, et même avec de la pure pipe, la liste sera actualisée
laisser élément d'éléments | tube:param
Dans ce cas d'utilisation, j'ai utilisé ma Pipe en fichier ts pour le filtrage des données. Il est beaucoup mieux que la performance, de l'aide pur tuyaux. Utilisation en ts comme ceci: