should UI state be stored in model or controller? - javascript

So let's take that simple example:
data --> projection --> view
What I call projection here can be any UI state resulting in filtering, sorting, highlighting, etc...
In an MVC paradigm, data lives in the model, view is the view, where does state belong?
I used to store state in the controllers but I've heard it's bad, controllers should be kept "lean" and everything should be stuffed into the model. I can see the advantages of having stateless controllers for testing but it seems counter intuitive to couple the data and its projection in the model.
What if different views want different projections of the data? How does it break down?
[EDIT] Found some related questions here and here but they don't answer the question directly. I know to put the logic for projecting the data inside the model but it doesn't say where the state of these projections should be maintained.

The "projection" you are talking about is one of two things. Either it is actual model data, at which point it belongs to the model layer, or it is a ViewModel in MVVM. As such, it lives in it's own layer between view and controller.
I would really need to see more details to understand which of these is a better representation of what you are trying to accomplish. For example, if this is storing the state of the checkboxes, and the text fields, then it is MVVM. If you were recording a list of entities that were "selected", then it probably belongs down in the domain layer.
If it is it's own model, it might not live in the same model as the original. It could be completely separate, but still depending on the other model.

Related

Are models (as in Backbone or Ampersand models) really necessary with React?

I come from a Backbone / Marionette background and have been working in React for a few weeks now. A lot of people say you should pair React with something like Backbone or Ampersand.js, using React as the V and Backbone / Ampersand as the "M" and "C" in the classic MVC model.
However, the more I think about this, the more I wonder if it's really necessary. In Backbone / Ampersand, the model's main purpose is to track state, and "tell" views to update when the model's state changes. Of course in React, the view takes care of this responsibility via the view's props and state, which seems to make a full blown Backbone / Ampersand model unnecessary and duplicative.
What am I missing?
First, let's define model in MVC terms. The following is from Wikipedia.
[...] the model, captures the behavior of the application [...] independent of the user interface.
The model directly manages the data, logic and rules of the application. A view can be any output representation of information [...] multiple views of the same information are possible [...].
The third part, the controller, accepts input and converts it to commands for the model or view.
In React you inevitably will create View+Controller components; much like angular, knockout, and most other JS application frameworks.
Why models?
While you could also throw the model into the component at this level, it turns out to not work well in practice. You have problems like overfetching (and other optimization limitations), difficulty testing, no separation of concerns, and it's difficult to see what the component actually is until you mentally separate the controller behavior from the model behavior.
So, backbone?
If you have this code, and you want to make it better, you'll eventually end up with models. It could be backbone models, or it could be flux stores, or it could just be simple objects with functions that call $.ajax.
It makes little difference what kind of models you use, but you do need them.
MODEL ALL THE THINGS
Woah! Hold on there. Models aren't free. Every time you use a model you're crossing the abstraction boundary, and leaving component land. It's an imperative action in a declarative system, so we need to keep things predictable.
Most of your components are pretty dumb. Props, and maybe some UI state. You have controller components ("View Controllers"), which are 100% tied to your model layer, and you have the rest of the components which are (ideally) 0% tied to your model layer.
What you seem to be describing in the original question is a small application where you have one of these controller components. However as these grow, you need to coordinate between them (not fetching the same user twice, for example). You nest these controllers inside other controllers to build an application. The model is the glue.
You can think of a React component as a functionally pure function that takes props and state as input arguments. Its render() method's job is to produce and returns a (virtual) DOM element or JSX (syntactic sugar) based on props and state. Backbone still "owns" the model. When the Backbone model changes via user inputs or socket events or whatever, setState() can be called (add some magic here) which causes React components to render again. The point is React component does not hold the state. This is NOT to say that one must uses Backbone with React as React is simply a rendering library.
Update: In react-future, it's very clear that render() should be treated as a pure function. It takes props and state as input arguments and its job is to produce a JXS, so no need to refer to the this keyword.
https://github.com/reactjs/react-future/blob/master/01%20-%20Core/01%20-%20Classes.js

Separating Model and View State in AngularJS

I've got a big complicated SPA that I'm coding in angular. In it, I need to keep track of actual Model data - operationalized as "stuff that I get from and send to the backend API server". I also have viewstate data - "stuff I keep track of, but don't send to the API server". Model data is stuff like user nicknames or forum post bodies, whereas viewstate data is "this particular UI element is expanded" - it's application state basically.
What I'm wondering is: is there a best-practices approach to this? In my more complicated controllers, I've got an ng-repeat of elements, each element has expando-UI elements. The temptation is to put those expandos inside an ng-if and attach a .showing variable to the object. For example one could imagine this kind of text: ng-repeat="user in users", ng-click="user.showing = !user.showing", ng-if="user.showing" in a few divs. This is really handy because all the information we need about the view state of a repeated item is right there on the repeat.
This also smells terrible - it potentially overwrites fields that were fetched from the API server, and since I cache data models to save on API roundtrips (and handle realtime updates), it also means that view state gets preserved when the user navigates away and then back to the same place in the SPA, or that the view state would be linked in the event the same data is displayed twice on the screen (say, the same profile is showing in three places on the screen as three replies to a post).
I've got some ideas about what to do here. I could encapsulate all the view state variables in their own place in the controller, which has the side-effect of making ng-repeats a lot harder to pull off (you have to look up the view state variable using the $index property probably).
I could encapsulate this one problem in a directive, calling ng-repeat on the directive, which would itself have isolate scope and only has one set of state variables to track (so it therefore doesn't need to worry about tracking complex view state).
I could do something else entirely that the SO community suggests.
I'm leaning toward the directive route. This seems like the Angular Way of doing things. Each UI element doesn't really need to share its state outside of itself, I am currently only really doing this because my initial web mockups were big HTML templates that I got and then attached ng-repeat directives to. I can break the Post, Comment, Author, and so on elements into directives, then they just keep their state on their own.
Yeah, for what it's worth, I ended up breaking everything into directives. Basically, any item inside an ng-repeat that needs to track view state gets its own directive (and therefore its own isolate scope). I also got into the habit of stashing view-related variables on scope.view, so scope.view.showingExpandedReply = true, or whatever. That way I didn't have to pollute the data model with view state information.

Is AngularJS really MVC?

I'm looking through examples on the AngularJS home page — specifically “Add Some Control”. I don’t quite understand how it maps to MVC pattern.
It’s more or less clear that the template (index.html) can be thought of as the view, objects constructed by TodoCtrl from todo.js as the controller, but where is a model? Attributes like ng-model map to some internal pieces of the framework and don’t directly expose an object which we could call a model.
Is it correct to call AngularJS an MVC framework?
The core idea behind MVC is that you have clear separation in your code between
managing its data (model), the application logic (controller), and presenting the data
to the user (view).
The view gets data from the model to display to the user. When a user interacts with the
application by clicking or typing, the controller responds by changing data in the model.
Finally, the model notifies the view that a change has occurred so that it can update what
it displays.
In Angular applications, the view is the Document Object Model (DOM), the controllers
are JavaScript classes, and the model data is stored in object properties.
I havent read up on Angular. However, keep in mind that "true" MVC means that the Model contains business Logic, the Controller takes care of communicating user input to the Model, and the View gets its own data from the Model. Many so called MVC frameworks are actually not implementing MVC the right way: They are overloading the Controller With to much responsibility, as in making the Controller responsible also to update the View. That is not what the Controller was intended to do, and in such cases you get what is called a "fat Controller". There is a misconseption among many webdevelopers that the View is simply a "template". This is both true and false; It is a template but it get its own data directly from the Model - not via the Controller.
But as I started out saying: I havent yet read up on Angular, but you might have so yourself and you should be able to find out if Angular is truly a MVC framework by looking at what the different Components in the Framework does.
The attributes map into the scope variable as defined in the controller. For example, if you have some JS objects which represent your business logic, you can put those in the scope variable, and then the attributes can be mapped into the model itself instead of some arbitrary values in the scope variable.
If anything that can store data in a object is considered a model than every front end framework (view) could be considered to have a model. They can all store data in objects and present them in the view. I think calling angular a mvc is really a marketing ploy, trying to make angular seem like it is more than it is.
See:
https://www.pluralsight.com/blog/software-development/tutorial-angularjs-mvc-implementation
In the controller, is where they give their example of angular's as a model. If it can't assist in the storage of persistent data it isn't much of a model.

Javascript MVVM design patterns - how to track dirty state and who should do Ajaxing?

I'm part way through a pretty complex Knockout.js app using the mapping plugin to work with a deep object graph which closely mirrors server side domain objects. I've been refining my patterns as I go to something which works pretty well for my own slightly awkward context but I'd like to know if it's a good / bad overall way to approach MVVM Javascript.
Essentially my page pattern is to have a revealing module function which acts a bit like a controller - it owns the hierarchy of view models and is responsible for detecting changes, Ajaxing changes to the server and using the mapping plugin to update its view model graph with any flow-on changes which may come back in the response as JSON. I've done it this way because my domain is such that a small change in one part of the graph, when validated on the server, may result in changes / removals in distant parts of the graph. When this happens I need a common point at which to re-map the changes, present message dialogs to the user, etc.
All of my view models are instantiable functions and I've designed it so that they know nothing about the page they're used in, or the server (i.e. they don't do their own Ajaxing). The constructor of each view model creates its children via mapping options and each level is passed a reference to its parent. I've implemented a generic dirty flag which most of the view models use, and when a change occurs they pass a reference to themselves up the chain to a "dirty view model" observable at the top, which the module is subscribed to. I know this sounds a bit odd but it seemed the best way to approach it because items at each level are constantly being added and removed so I can't statically subscribe to properties at initialization time. And I don't want to keep removing and re-adding subscriptions each time I re-map my graph (which can get quite big).
From a pure efficiency point of view this isn't the best. The simplest way would be that each view model directly calls a function in the module when it needs to, but that type of coupling has to be wrong. Or I could pass in a reference to the module (or its relevant function) to each view model constructor, and the view model calls that, a bit like Javascript dependency injection. But that just seems too abstract. This is complex enough as it is.
Should I be looking at a higher level framework such as Backbone to sit on top of all this? Is injecting the module reference really too abstract? Or does this way of structuring things basically make sense as it is? I'm keen to hear from anyone who has worked on similarly challenging scenarios as to how you progressed and refined your patterns.
EDIT: I should have clarified that for various reasons, this app works in "save as you go" mode, whereby a change at a given level causes an immediate discrete Ajax post of just that one view model (not including its children) to be sent to the server (which may return a result which represents a change to just about anything else). Despite this annoying need for constant Ajaxing as opposed to pure client side action, Knockout.js has still made my app WAY more elegant, maintainable and scalable than my MVC apps of Olde.
Decoupling your viewmodels and reducing references can be achieved with a pub/sub model, like the one Ryan Niemeyer discusses here.
Ryan also made a Dirty flag for viewmodels, which can be found here.

Backbone: Are Views really Controllers?

I'm building my first Backbone.js app and I'm confused about how much responsibility I'm supposed to give to or hide from my Views.
In my example, I'm building a Rich UI Table (similar to YUI's datagrid) that's dynamically generated from a Collection. In my app I call this an "AppTable". In my understanding of MVC, I would imagine that there'd be some kind of AppTable controller which finds the correct Collection, grabs a "dumb" View and passes to the View whatever information from the Collection it needs to render. In this senario, the View would do little more than take the data provided to it and modify the DOM accordingly, maybe even populating a template or attaching event listeners.
Backbone seems to do away with the idea of having a controller mediate between the View and Collection. Instead a View gets initialized with a reference to a Collection and it is View's responsibility to update itself.
Am I understanding this architecture correctly?
Assuming I do, my question then becomes, what happens when my View needs to do more and more? For example, I want column sorting, drag-and-drop for rows, pagination, searching, table control links (like new, copy, delete row... etc), and more. If we stick with a "smart" View paradigm where the View is connected directly to a Collection, do the above functions become attached to View object?
Thinking through this, I could see the View growing from a simple table wrapper to pretty messy beast with lot of functionality attached to it. So, is the View really a controller in this case?
Your understanding of the architecture is correct. Backbone does not recognize the concept of a "controller" in the traditional MVC sense. (In fact, Backbone used to actually have an object called a Controller, but it has been renamed Router to more accurately describe what it does.)
The functions you list (drag-drop, delete rows, sorting, etc.) would all belong in a View. A view describes what you see and responds to user input. Anything involving an event (a click, a keypress, a submit, etc.) all go inside of a view. But your view should never actually manipulate the data; that should be done by its model. You are correct in thinking that a view acts like a controller, because it packages data and sends it to the model, which will then validate/set/save appropriately. Once those actions have occurred, the view re-renders itself to represent the new version of the data inside the model.
One note of caution: your view should not be too strenuously tied to the DOM. It is Backbone convention to have a top-level DOM element that your view is tied to (e.g., a form or a div) and then deal only with its sub-elements. That is appropriate; in general, things like "remove this link from this div" inside your view are not. If you find your view growing unwieldy, you most likely need to break it into subviews, each with their respective behaviors as components of their whole.
My thoughts on this updated below:
I think Josh gave a good answer, however, in my experience, building a few Backbone apps, even medium-complexity apps do need a separate controller class.
To clarify what I mean about a controller: The functionality between the model (or router) and the view that creates and instantiates the new view class and kills (and unregisters events) on the old one. This functionality might be the same for many views (so a direct one-to-one relationship between views and controllers probably isn't needed) but sometimes one needs to pass in a model or other additional extra values.
Right now, I just have one controller with a few if statements for adding some unique data to certain views for most apps I've built but I'm looking at setting up an architecture where it will check to see if a unique controller exists for that view else it falls back to the standard controller. Nothing special, but should do the job.
Update: After six months of building Backbone apps I realized that routers can be split up and extended just like views. (duh?)
Right off the bat, I knew to make a base view of functionality I know that all my views would need. Similarly, I would make base views for each section, like "profile" pages or "inbox" pages that I know would all use the same functionality. This wasn't so clear to me in the beginning with routers, but the previous name of "Controller" hinted at this.
Most people (as in every example of Backbone I've ever seen on the web) just use one monolithic router instantiation to handle all routes but you can actually have 1-to-1 parity of routers to views, or in my case, a base router for checking user auth and such and then one for each major section. That way if you need to pass in certain models or collections to a router on page load, you don't need to add code to one monolithic router, but instead pull up the unique router for that view. I find this is currently better than creating a separate controller class. The base router can be in charge of last instantiated view, etc, so you can kill the last view before instantiating the new one.
TLDR: Use multiple Routers as controllers. I believe that's what they were meant for and it works well.
I've struggled with the same semantic issues when trying to map out a single-page app. In the end I decided that Backbone is using the wrong name.
When you look at a Backbone app in the browser, the View is not actually a view at all, its el member is the view. Backbone.View is either a view controller or, probably more correctly, a presenter.
Some supporting evidence:
you never see a Backbone.View on the screen, its always the el or $el that is applied to the DOM
a Backbone.View does not receive user input, the DOM element receives input and the events are delegated via the events hash of the "view"
a BackBone.View manages model or collection changes and translates these changes to dumb-view (DOM) elements, then applies them to the actual view, e.g. this.$el.append('<p>Cats!')
I think Backbone.Presenter would be a better name, but I can also see the historical issues with there being a former Backbone.Controller and the amount of work renaming introduces.
I have settled on the following structure for my latest project:
an app controller, extended from Backbone.View, tied to the body element
several model collections to cache data retrieved from the server
a Backbone.Router that translates route changes into Backbone events and triggers them on itself
many app controller methods that handle the router events the app controller listens to
an app controller method prepares any needed models, then initiates a presenter (extended from Backbone.View) and attaches it to the body element
All these parts are initiated and owned by the app controller. The presenters do not know why or where they are on the page and only care for their own DOM elements and the changes they receive from this.model.
Have a look at this part of backbone documentation
http://documentcloud.github.com/backbone/#FAQ-tim-toady
References between Models and Views can be handled several ways. Some
people like to have direct pointers, where views correspond 1:1 with
models (model.view and view.model). Others prefer to have intermediate
"controller" objects that orchestrate the creation and organization of
views into a hierarchy. Others still prefer the evented approach, and
always fire events instead of calling methods directly. All of these
styles work well.
So, backbone does not take that decision for you.
I have a very similar use case (table grid with pagination, ordering, live filtering, and forms with client-side validation, master-details relations, etc.)
In my case, I first started with a Router behaving just like a controller, and quite quickly my code got a bit messy.
So I completely removed Routers (I'll add them back later, but just as an addition) and created my own controller (that in fact works as a presenter). It's just a javascript class, with Backbone.extend backed in to handle inheritance.
The idea is that the view recieves all the data it needs to display itself (model, collection, and the el in which it should be parsed), set up listener on dom events, and then executes controller methods. It never directly modifies the data nor it interacts with other views, it tells the controller to do it.
A view can have subviews, and in that case the subview only interacts with the parent view, or directly with the controller.
So far now it seems to work, but anyway things are not so simple as I expected them to be...
I hope to publish it in the next few days.
A different perspective from the other answers here is that, just because you are using the Backbone framework, that doesn't mean that your entire codebase must be wrapped in Backbone classes.
Personally, my controller is an amalgamation of "raw" Javascript and Backbone routes, and I never use Views for control logic at all. IMHO views are for ... well, view logic, and specifically for wrapping elements. If you're using a view for anything that doesn't directly connect to an HTML element you are (again, IMHO) doing something wrong.
Backbone is awesome, but that doesn't mean that it's a silver bullet that can be applied to everything.

Categories

Resources