Ich finde Angulars Verwendung von Modellen verwirrend. Angular scheint den Ansatz zu verfolgen, dass ein Modell beliebig sein kann - IE Angular enthält keine explizite Modellklasse und Sie können Vanilla-JavaScript-Objekte als Modelle verwenden.
In fast jedem Angular-Beispiel, das ich gesehen habe, ist das Modell effektiv ein Objekt, das entweder von Hand erstellt oder von einem API-Aufruf über eine Ressource zurückgegeben wird. Da fast jedes Angular-Beispiel, das ich mir angesehen habe, einfach ist, werden normalerweise die im $ scope in einem Controller gespeicherten Modelldaten und alle mit dem Modell verbundenen Zustände, z. B. die Auswahl, auch im $ scope im Controller gespeichert. Dies funktioniert gut für einfache Apps / Beispiele, aber dies scheint eine übermäßige Vereinfachung zu sein, wenn Apps komplexer werden. Der in einem Controller gespeicherte Modellstatus kann kontextabhängig werden und verloren gehen, wenn sich beispielsweise der Kontext ändert. Ein Controller speichert selectedGallery
und selectedPhoto
kann nur global speichern selectedImage
, nicht aselectedPhoto
pro Galerie. In einer solchen Situation könnte die Verwendung eines Controllers pro Galerie dieses Problem beseitigen, erscheint jedoch aus Sicht der Benutzeroberfläche verschwenderisch und wahrscheinlich unangemessen und unnötig.
Angulars Definition von Modellen scheint näher an dem zu liegen, was ich als VO / DTO betrachten würde, das ein dummes Objekt ist, das zwischen Server und Client übertragen wird. Mein Instinkt ist es, ein solches Objekt in ein Modell zu verpacken, das ich als Modell bezeichnen würde - eine Klasse, die den Status in Bezug auf das DTO / VO beibehält (z. B. Auswahl), Mutatoren bietet, die zur Manipulation des DTO / VO erforderlich sind, und den Rest des benachrichtigt Anwendung von Änderungen an den zugrunde liegenden Daten. Natürlich wird dieser letzte Teil von Angulars Bindungen gut erledigt, aber ich sehe immer noch einen starken Anwendungsfall für die ersten beiden Verantwortlichkeiten.
Allerdings habe ich dieses Muster in den Beispielen, die ich mir angesehen habe, nicht wirklich gesehen, aber ich habe auch nicht gesehen, was ich als skalierbare Alternative betrachten würde. Angular scheint implizit davon abzuraten, Services als Modelle zu verwenden, indem Singletons erzwungen werden (ich weiß, dass es Möglichkeiten gibt, dies zu umgehen, aber sie scheinen nicht weit verbreitet oder anerkannt zu sein).
Wie soll ich den Status der Modelldaten beibehalten?
[Bearbeiten] Die zweite Antwort in dieser Frage ist interessant und entspricht in etwa dem, was ich derzeit verwende.
quelle
galleryService
könnte eine Reihe von Galerien speichern.gallery
Dienst über die bekannten Methoden (Abrufen, Aktualisieren, Löschen usw.) verfügen, während er den Status intern verwaltet und Objekte mit einzelnen Aufzeichnungsmethoden zurückgibt, z$resource
.Antworten:
Status (und Modelle) werden in $ scope gespeichert
$ scope ist das Datenspeicherobjekt von Angular. Es ist analog zu einer Datenbank. $ scope selbst ist nicht das Modell, aber Sie können Modelle in $ scope speichern.
Jeder $ scope hat einen übergeordneten $ scope, bis zu $ rootScope, der eine Baumstruktur bildet, die Ihr DOM lose widerspiegelt. Wenn Sie eine Direktive aufrufen, für die ein neuer $ scope erforderlich ist, z. B. ng-controller, wird ein neues $ scope-Objekt erstellt und dem Baum hinzugefügt.
$ scope-Objekte werden durch prototypische Vererbung verbunden. Dies bedeutet, dass wenn Sie ein Modell auf einer höheren Ebene im Baum hinzufügen, es allen niedrigeren Ebenen zur Verfügung steht. Dies ist eine phänomenal leistungsstarke Funktion, die die $ scope-Hierarchie für den Vorlagenautor nahezu transparent macht.
Controller initialisieren $ scope
Der Zweck des Controllers besteht darin, $ scope zu initialisieren . Der gleiche Controller kann viele $ scope-Objekte in verschiedenen Teilen der Seite initialisieren. Der Controller wird instanziiert, richtet das $ scope-Objekt ein und wird dann beendet. Sie können denselben Controller verwenden, um viele $ scopes in verschiedenen Teilen der Seite zu initialisieren.
Im Fall Ihrer Bildergalerie hätten Sie einen imageGallery-Controller, den Sie dann mit der ng-controller-Direktive auf jeden Teil des DOM anwenden würden, für den Sie eine Galerie sein möchten. Dieser Teil der Seite erhält einen eigenen $ -Bereich, in dem Sie das selectedPhoto-Attribut speichern.
Prototypische Bereiche
$ scope erbt von seinem übergeordneten Element mithilfe einer einfachen alten prototypischen Vererbung bis hin zu $ rootScope, sodass Sie Ihre Objekte an einer beliebigen Stelle in der Hierarchie speichern können, die sinnvoll ist. Sie erhalten einen Baum mit $ scope-Objekten, der sich ungefähr auf Ihr aktuelles DOM bezieht. Wenn sich Ihr DOM ändert, werden bei Bedarf neue $ scope-Objekte für Sie erstellt.
$ scope ist nur ein einfaches JavaScript-Objekt. Es ist nicht verschwenderischer, mehrere $ scope-Objekte zu erstellen, als ein Array mit mehreren currentImage-Objekten zu erstellen. Es ist eine sinnvolle Möglichkeit, Ihren Code zu organisieren.
Auf diese Weise beseitigt Angular das alte Problem "Wo speichere ich meine Daten?", Das wir häufig in JavaScript finden. Dies ist die Quelle eines der wirklich großen Produktivitätsgewinne, die wir durch Angular erzielen.
Haben Sie globale Daten (z. B. eine Benutzer-ID)? Speichern Sie es auf $ rootScope. Haben Sie lokale Daten (z. B. ein aktuelles Bild in einer Galerie, in der mehrere Galerieinstanzen vorhanden sind)? Speichern Sie es auf dem $ scope-Objekt, das zu dieser Galerie gehört.
$ scope steht Ihnen automatisch im richtigen Teil der Vorlage zur Verfügung.
Winkelmodelle sind dünn
Aus einem Rails-Hintergrund stammend, in dem wir uns auf fette Modelle und dünne Controller konzentrieren, fand ich Angulars "kaum da" -Modelle überraschend. Tatsächlich führt das Einfügen einer Menge Geschäftslogik in Ihr Modell häufig zu Problemen auf der ganzen Linie, wie wir manchmal beim Benutzermodell in Rails sehen, das, wenn Sie nicht vorsichtig sind, wächst, bis es nicht mehr wartbar ist.
Ein Winkelmodell ist einfach ein JavaScript-Objekt oder ein Grundelement.
Jedes Objekt kann ein Modell sein. Modelle werden normalerweise mit JSON im Controller oder AJAXed von einem Server definiert. Ein Modell kann ein JSON-Objekt sein oder nur eine Zeichenfolge, ein Array oder sogar eine Zahl.
Natürlich hindert Sie nichts daran, Ihrem Modell zusätzliche Funktionen hinzuzufügen und diese im JSON-Objekt zu speichern, wenn Sie möchten, aber dies würde in einem Paradigma portieren, das nicht wirklich zu Angular passt.
Winkelobjekte sind normalerweise Datenrepositorys, keine Funktionen.
Das Modell am Frontend ist nicht das echte Modell
Natürlich ist das Modell, das Sie auf dem Client halten, nicht das echte Modell. Ihr eigentliches Modell, Ihre einzige Quelle der Wahrheit, lebt auf dem Server. Wir synchronisieren es mithilfe einer API, aber wenn es einen Konflikt zwischen beiden gibt, ist das Modell in Ihrer Datenbank offensichtlich der ultimative Sieger.
Dies gibt Ihnen Privatsphäre für Dinge wie Rabattcodes usw. Das Modell, das Sie in Ihrem Frontend finden, ist eine synchronisierte Version der öffentlichen Eigenschaften des realen Modells, das entfernt ist.
Geschäftslogik kann in Diensten leben.
Angenommen, Sie möchten eine Methode schreiben, um etwas mit Ihrem Modell zu tun, es zu synchronisieren oder beispielsweise zu validieren. In anderen Frameworks könnten Sie versucht sein, Ihr Modell mit einer entsprechenden Methode zu erweitern. In Angular würden Sie eher einen Dienst schreiben.
Services sind Singleton-Objekte. Wie bei jedem anderen JavaScript-Objekt können Sie Funktionen oder Daten in diese einfügen. Angular wird mit einer Reihe integrierter Dienste geliefert, z. B. $ http. Sie können Ihre eigenen erstellen und mithilfe der Abhängigkeitsinjektion diese automatisch für Ihre Controller bereitstellen.
Ein Service kann Methoden enthalten, um beispielsweise mit einer RESTful-API zu kommunizieren, Ihre Daten zu validieren oder andere Arbeiten auszuführen, die Sie möglicherweise ausführen müssen.
Dienstleistungen sind keine Modelle
Natürlich sollten Sie Dienste nicht als Modelle verwenden. Verwenden Sie sie als Objekte, die Dinge tun können. Manchmal machen sie Sachen mit deinem Modell. Es ist eine andere Denkweise, aber eine praktikable.
quelle
Vergessen wir zunächst nicht, dass Angular ein webbasiertes Framework ist. Wenn Sie Ihren Status ausschließlich in einem Objekt beibehalten, überlebt es nicht, dass Benutzer in ihrem Browser auf Aktualisieren klicken. Um herauszufinden, wie der Status von Modelldaten in einer webbasierten Anwendung beibehalten werden kann, müssen Sie herausfinden, wie Sie sie beibehalten, damit Ihr Code in einer Browserumgebung funktioniert.
Angular macht es Ihnen wirklich einfach, Ihren Zustand beizubehalten, indem Sie:
In Ihrem einfachen Beispiel kann das Speichern von Benutzeraktionen wie
selectedGallery
undselectedPhoto
mithilfe einer URL wie folgt dargestellt werden:// List of galleries .../gallery // List of photos in a gallery .../gallery/23 // A specific photo .../gallery/23/photo/2
Die URL ist wichtig, da Ihr Benutzer mit den Schaltflächen
back
und im Browserverlauf navigieren kannforward
. Wenn Sie diesen Status mit anderen Teilen Ihrer Anwendung teilen möchten, bietet die Webanwendung eine Vielzahl von Methoden, mit denen Sie Cookies / localStorage, versteckte Frames / Felder oder sogar das Speichern auf Ihrem Server erreichen können.Nachdem Sie Ihre Strategie definiert haben, wie der unterschiedliche Status Ihrer Anwendung beibehalten werden soll, sollte es einfacher sein, zu entscheiden, ob Sie mit einem Singleton-Objekt, wie es von
.service
oder einer Instanz über bereitgestellt wird, auf diese beibehaltenen Informationen zugreifen möchten.factory
.quelle
new
eine Instanz erstellen. Dies bedeutet, dass Sie diese Instanz mit der normalen Abhängigkeitsinjektionssyntax nicht für andere Entitäten freigeben können. Ich denke, dies wäre verwirrend für andere Angular-Entwickler, die Ihren Code verwenden könnten.new
Schlüsselwort angibt, dass dieser Dienst nicht mit anderen Entitäten geteilt werden soll. Zum Beispiel habe ich einen Wrapper um ui.bootstrap.alert, den ich instanziiere, indem ich $ scope in meinem Controller übergebe. Wenn ich später einen Alarm anzeigen muss, rufe ich einfach analert.success("Ok")
. Trotzdem stimme ich zu, dass nicht klar ist, wann der Anrufer new verwenden soll, aber es liegt wirklich am Benutzer des Dienstes, zu verstehen, wie der Dienst verwendet werden soll.new
verwirrend ist, aber es hat mich daran erinnert, dass der Dienst, den ich anrufe , nicht geteilt werden darf. Wenn die Bedenken jedoch nur auf der Syntaxseite liegen, können Sie auch ein Objekt zurückgeben, dh: new in the aufrufen.factory
. Ich habe den Plunker aktualisiert, um zu veranschaulichen, wie dies getan werden kann.Angular hat keine Meinung dazu, wie Sie sogenannte "Modellobjekte" speichern. Der Angular-Controller dient
$scope
ausschließlich als "Ansichtsmodell" für die Verwaltung Ihrer Benutzeroberfläche. Ich schlage vor, diese beiden Konzepte in Ihrem Code zu trennen.Wenn Sie möchten, dass Angular Scope Change Notification (
$watch
) verwendet wird, können Sie ein Scope-Objekt verwenden, um Ihre Modelldaten zu speichern, wenn Sie dies wünschen (var myScope = $rootScope.$new()
). Verwenden Sie nur nicht dasselbe Bereichsobjekt, an das Ihre Benutzeroberfläche gebunden ist.Ich empfehle, zu diesem Zweck benutzerdefinierte Dienste zu schreiben. Der Datenfluss sieht also folgendermaßen aus:
AJAX -> Benutzerdefinierter Dienst -> Modellbereichsobjekt -> Controller -> UI-Bereichsobjekt -> DOM
Oder dieses:
AJAX -> Benutzerdefinierte Dienste -> Einfache alte JavaScript-Objekte -> Controller -> UI-Bereichsobjekt -> DOM
quelle