Die Frage, kurz gesagt:
Wie unterscheidet man in MVC zwischen einem Klick auf ein Kontrollkästchen (oder eine Änderung in einem Auswahl- oder Listenfeld), der von einem Menschen ausgeht und bedeutet "Controller, ändere das Modell", und einem Klick auf ein Kontrollkästchen (oder eine Änderung in einem Auswahl- oder Listenfeld), der vom Controller ausgeht und bedeutet "Ich aktualisiere die Ansicht, weil sich das Modell geändert hat"?
Das Beispiel:
Ich habe eine JS-App (alle eine große HTML + JS-Seite; es gibt einen Server dahinter, und AJAX geht auf, aber es ist nicht wichtig für das Beispiel), die die Vorstellung von "Vertices" durch "Edges" verbunden hat. Die Benutzeroberfläche ermöglicht das Hinzufügen und Entfernen von Vertices auf einer Karte und das Aktivieren oder Deaktivieren von Edges zwischen Paaren von Vertices.
Es gibt zwei Möglichkeiten, eine Kante von Scheitelpunkt A zu Scheitelpunkt B zu deaktivieren:
- Klicken Sie auf die Kante, damit das Fenster "Kantendetails" die Schaltfläche "Diese Kante deaktivieren" anzeigt; oder
- Klicken Sie auf Scheitelpunkt A (oder B), um im Fenster "Scheitelpunktdetails" eine Checkliste der nahegelegenen Scheitelpunkte anzuzeigen, in der Sie die Markierung für Scheitelpunkt B (oder A) aufheben können.
So funktioniert das unter der Haube von MVC ( aber siehe das Ende dieses Beitrags für ein Update, in dem ich Probleme in meinem Verständnis korrigiere ):
- Modell: eine Liste von Vertex-Objekten und eine Liste von Edge-Objekten.
- Ansicht: eine GMaps-Benutzeroberfläche mit Markern und Polylinien sowie Kontrollkästchen und Schaltflächen und DIVs "Vertex Details" und "Edge Details".
- Controller:
- JS-Funktionen, die das Modell aktualisieren, wenn Ereignisse auf den Kontrollkästchen und Schaltflächen ausgelöst werden; und
- JS-Funktionen, die die Ansicht aktualisieren, wenn Ereignisse auf den Modellen ausgelöst werden.
Hier ist die spezifische Unzulänglichkeit :
- Der Benutzer hat das Scheitelpunkt-Detailfenster auf Scheitelpunkt A und das Kanten-Detailfenster auf die Kante von Scheitelpunkt A zu Scheitelpunkt B fokussiert.
- Der Benutzer klickt auf "Diesen Rand deaktivieren" im Fenster Randdetails.
- Controller-Funktion 1 empfängt das Click-Ereignis und ruft disable() für das Edge-Modellobjekt auf.
- Das Edge-Modellobjekt löst das Ereignis "Ich wurde gerade deaktiviert" aus.
- Controller-Funktion 2 empfängt das Ereignis "Ich wurde gerade deaktiviert", und
- zeichnet das Edge-Detailfenster neu und sagt: "Ich bin deaktiviert!" und
- hebt die Markierung von Scheitelpunkt B im Fenster Scheitelpunktdetails auf.
- Verdammt! Dadurch wird die Controller-Funktion 1 erneut ausgelöst, die auf UI-Ereignisse wartet, die bedeuten, dass eine Kante deaktiviert wurde!
Es kommt also zu einer unnötigen Neuaktualisierung des Modells und einer Neuaktualisierung der Ansicht. In einer komplexeren Ansicht mit Ereignissen, die Ereignisse auslösen, die wiederum Ereignisse auslösen, kann dies zu Dutzenden von überflüssigen Aktualisierungen führen!
Update: eine großartige Antwort.
Ich habe MVC ein wenig missverstanden. Ich habe nicht nur eine Ansicht, wie ich oben beschrieben: Ich habe mehrere Views in mehreren Models. Insbesondere habe ich eine Checkbox-Liste Ansicht der Kanten zu einem bestimmten Knoten, und eine separate, "detaillierte Fenster-Stil" Ansicht einer Kante.
Außerdem sollte ich nicht eine Controller-Funktion haben, die alle Ansichten aktualisiert, wenn sich das Modell ändert: jede Ansicht sollte die selbst wenn sich das Modell ändert.
Wenn sich also jede Ansicht für "Statusaktualisierungs"-Ereignisse auf dem Modell registriert und jede Ansicht sich selbst beim Empfang dieser Ereignisse aktualisiert, dann ist die Antwort auf meine Frage nach zirkulären Ereignissen einfach die folgende:
Die Checkbox-Listenansicht deaktiviert die Checkbox-Ereignisse für den Moment, in dem sie die Checkboxen nach einer Modellstatusänderung aktualisiert.
Wenn nun ein Benutzer eine Kante über das Detailfenster der Kante deaktiviert, aktualisiert der Controller das Kantenmodell, die Ansicht der Kontrollkästchenliste erhält eine Benachrichtigung über die Aktualisierung, und die Ansicht der Kontrollkästchenliste ist intelligent genug, um Kontrollkästchenereignisse zu unterdrücken, während sie den Status des entsprechenden Kontrollkästchens ändert.
Dies ist viel schmackhafter als meine ursprüngliche Lösung, bei der ein Controller ALLE Ansichten aktualisiert - und daher wissen muss, welche Ansichten besondere Pflege und Fütterung benötigen, um Schleifen zu vermeiden. Stattdessen muss nur die einzelne Ansicht mit störenden UI-Elementen mit dem Problem umgehen.
Vielen Dank an diejenigen, die meine Frage beantwortet haben!