Backbone.js views and models correlation? - javascript

Background: I'm using Backbone for the first time. In my project, I have one "main" model - let's call it Customer. At all times, only one Customer is displayed. I have a CustomerList Collection, which queues the Customers. At a button click, the next Customer is displayed. CustomerView does all the rendering etc.
The problem: should I make a new instance of CustomerView every time we switch to the next Customer? Another possibility (which I started out with) was just one instance of the View and passing the model to the render function of the view (view.render(model)). This way, I can get the next Customer from the List, pass it on to the render function of the view and that's it. I'm wondering though how well this suits the MVC pattern of Backbone? It would seem more "standard" to get the new model, kill the current View and make a new View instance.
Any suggestions from experienced Backbone developers, please? Thanks.

If the only thing that is changing is the actual model then you should definitely keep your CustomerView around and simply provide it a new model to display.
This most certainly pertains to MVC concepts of functional separation. The CustomerView only needs the relevant data (supplied by the model) in order to perform it's task of rendering the view.
Think of it like this - is there any need to reset your CustomerView module? It shouldn't hold and model specific information at all so essentially it shouldn't care that you change the model - only that it needs to be re-rendered once the data changes.
The one instance where it would make sense to me to totally "kill" the current view would be if the user navigates to a different section of your application that has no relation to customers and has no option to view customer details. Only then would it make sense to completely release un-needed modules.

Related

Make a change in an Angular service show up in all of the components that use that service

I am building the clone of a website/app called Kualitee.com. It is a test-management tool used by QA Engineers.
In kualitee, you have multiple projects, with test cases and members. You can add, delete, and change projects and members. There is a header on top which enables you to select a project of which you want the data of i.e test cases and stuff.
the header for changing projects
The approach I used for this is as follow:
1. one service containing all the data
2. one service containing only the project selected...
The whole app uses the data found in the second service. When I make a change in a project, say add a new member, it does display that at that time. After that, if I change the project in the select at the top to another project, the component containing that member does not change, even though the project is a different one now, but as soon as I switch to another component, through routing, and back again, the changes are there.
My point is, is there any way I can add functionality where, if a parent component changes an object in service, the change reflects in a child component also using that same object in that same service, without me needing to change components or refresh.
PS. This is my first time asking something on StackOverflow, Sorry if my question is confusing.
You probably need an observable shared service to share the data between components. Here is how to achieve that: Interaction between components using a service
I think you are referred to Angular life cycle https://angular.io/guide/lifecycle-hooks

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.

Managing Backbone.js Page Transitions

I know this is a very commonly discussed topic but I can't find anything that answers my question exactly :)
I'm working on a Backbone.js project at the moment. Router wise I don't instantiate views, models or collections within the router but instead just use the router as one way of dealing with state - the router calls methods on my custom controller object.
My controller then instantiates the different views, models and collections for index, show etc. This is all fine and dandy.
I'm just having a bit of a struggle with how to deal with page transitions. I've read all of the great posts on managing zombies etc, and know that whatever happens I have to have some cleanup system for old views (I'm currently using the .close() method that Derick Bailey blogged about).
If I'm going from #show to #index, or any other route change, I understand that it makes sense to just instantiate new, fresh views, models, etc - which is what I see in pretty much every tutorial. Making sure to cleanup old ones, of course.
But, if I'm already on #show say, and I route to another #show page, all of the views etc that I want are already instantiated and rendered. All I want to change is the data of the models and collections.
So I guess my question is why do I not see people re-using views very much. In my head I was thinking if you're already on the page that you want, it would make more sense to just update the url or urlRoot of the model / collection that that view is linked to and re-fetch. This would then trigger a reset event, and all views that need to can subscribe to this and re-render themselves.
But, as I say, I don't see people doing this. Is it just because it's a really bad idea? If someone is doing something like this, how do you deal with tracking 'refreshable' models and collections?
Thanks!
I think it depends a lot on how you are using your views and how complicated/large they are.
If your views are very simple then it is often just easier to rerender the entire view and replace the existing HTML with the new markup (it might also be faster then traversing the DOM to change the necessary parts. However if you have a more complicated view and there is only a small amount of information changing it is most likely better to listen to the appropate attributes change events (eg. _bind('change:name', this.nameChanged,this)) and then only update that part of the DOM.
Keep in mind that while the convention is to use a render method which renders the element, you can just as easily apply an additional refresh event to only refresh certain parts, you can then just swap models (like #jackwanders suggested) and call the refresh method.

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.

What can I use Backbone JS's Models for exactly? Is this too far?

I have a question regarding Backbone JS's Models. I've already delved into Backbone JS quite a bit the last few days. I am understanding it now (and thanks to Stackoverflow, I have understood further with a few things).
At present, my web app is using Backbone Models to store dynamic navigation and other such bits. Stuff that is generally used in Backbone Collections. At present, my web app is still needing to store sort of 'global' states... Such as what the user has currently selected in the navigation (which then effects other parts of the app etc.)
I've been keeping all of these stored 'states' in variables like this:
App.data.selectedPage = whatever etc.
But I'm thinking more about Backbone Models... Should I be storing these 'global states' in a Model? Is that something that the Models can be used for?
At present, the webapp doesn't save to the server, or local storage, it's more of a converted Flash presentation I have to code for an iPad. So it still made sense to use something like Backbone for code organisation purposes... I mean, that's ok too right? To use Backbone, even I don't intend on storing the models anywhere?
Anyhow, yes, using models to store this sort of information is ok too? Anything goes? ...please do tell me if I'm approaching this wrong.
Many, many thanks.
James
I think one good rule of thumb for using backbone model is if you need events when some data is changing.
For exemple, you have a calendar with a selected date. Many other parts (views) of your app needs to know and be informed about the selected date. Then it makes sense to store the date in a model and have everyone listen on events from the calendar.
This is more for data related functions. For state it is different. Your selected page is a state to me. Page selection, page state, globals, they should be in your controllers. They (or it) should know what the state of the page is and they can trigger event when it changes.
Model => data centric with events

Categories

Resources