Single Object Model for AngularJS
angularjs, angularjs-scope, javascript, model, model-view-controller
Solution
In my day job we use the "state in a single object" pattern in a big enterprise AngularJS application. So far I can only see benefits. Let me address your questions:
Is such approach advisable? What are the downsides and pitfalls I will face when application starts to scale?
I see two main benefits:
1) DEBUGGING. When the application scales, the toughest question to answer is what is happening to my application right now? When I have all the application state in one object, I can just print it on the console at any point in time while the application is running.
That means it's much easier to understand what is happening when an error occurs, and you can even do it with the application on production, without the need to use debugging tools.
Write your code using mostly pure functions that process the state object (or parts of it), inject those functions and the state in the console, and you'll have the best debugging tool available. :)
2) SIMPLICITY. When you use a single object, you know very clearly on your application what changes state and what reads state. And they are completely separate pieces of code. Let me illustrate with an example:
Suppose you have a "checkout" screen, with a summary of the checkout, the freight options, and the payment options. If we implement `Summary`, `Freight` and `PaymentOptions` with separate and internal state, that means that every time the user changes one of the components, you have to explicitly change the others.
So, if the user changes the `Freight` option, you have to call the `Summary` in some way, to tell it to update its values. The same have to happen if the user selects a `PaymentOption` with a discount. Can you see the spaghetti code building?
When you use a centralized state object things get easier, because each component only interacts with the state object. `Summary` is just a pure function of the state. When the user selects a new freight or payment option, the state is updated. Then, the `Summary` is automatically updated, because the state has just changed.
When small portion of object is updated say priority is increased will angular smartly re-render the delta alone or will it consider the object got changed and re-render whole screen? (This will lead to poor performance), If so what are the works around?
We encountered some performance problems when using this architecture with angular. Angular dirty checking works very well when you use watchers on objects, and not so well when you use expensive functions. So, what we usually do when finding a performance bottleneck is saving a function result in a "cache" object set on `$scope`. Every time the state changes we calculate the function again and save it to the cache. Then reference this cache on the view.
Now since the whole DOM got smoothly translated into one JavaScript object the application has to keep manipulating this object. Do we have right tools for complex JavaScript object manipulation like jQuery was king of DOM manipulator?
Yes! :) Since we have a big object as our state, each and every library written to manipulate objects can be used here.
All the benefits you mentioned are true: it makes it easier to take snapshots of the application, serialize it, implement undos...
I've written some more implementation details of the centralized state architecture in my blog.
Also look for information regarding frameworks that are based on the centralized state idea, like Om, Mercury and Morearty.
Problem
I am experimenting few best practices in AngularJS specially on designing model. One true power in my opinion in AngularJS is 'When model changes view gets updated & vice versa' . That leads to the obvious fact 'At any given time the model is the single source of truth for application state' Now, After reading various blog posts on designing the right model structure and I decided to use something like 'Single Object' approach. Meaning the whole app state is maintained in a single JavaScript object. For example of a to-do application ``` $scope.appState = { name: "toDoApp", auth: { userName: "John Doe", email: "john@doe.com", token: "DFRED%$%ErDEFedfereRWE2324deE$%^$%#423", }, toDoLists: [ { name: "Personal List", items: [ { id: 1, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}, { id: 2, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 1}, { id: 3, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}] }, { name: "Work List", items: [ { id: 1, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : -1}, { id: 2, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}, { id: 3, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}] }, { name: "Family List", items: [ { id: 1, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 2}, { id: 2, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 0}, { id: 3, task: "Do something ", dueDate: "2013-01-10 11:56:05", status:"Open", priority : 5}] } ] ``` }; This object will go HUGE depending on the application complexity. Regarding this I have the below worries and marked them as questions. Is such approach advisable? What are the downsides and pitfalls I will face when application starts to scale? When small portion of object is updated say priority is increased will angular smartly re-render the delta alone or will it consider the object got changed and re-render whole screen? (This will lead to poor performance), If so what are the works around? Now since the whole DOM got smoothly translated into one JavaScript object the application has to keep manipulating this object. Do we have right tools for complex JavaScript object manipulation like jQuery was king of DOM manipulator? With the above doubts I strongly find the below advantages. Data has got neatly abstracted & well organized so that anytime it can be serialized to server, firebase or local export to user. Implementing crash recovery will be easy, Think this feature as 'Hibernate' option in desktops. Model & View totally decoupled. For example, company A can write Model to maintain state and few obvious Controllers to change the model and some basic view to interact with users. Now this company A can invite other developer to openly write their own views and requesting the company A for more controllers and REST methods. This will empower LEAN development. What if I start versioning this object to server and I can make a playback to the user in the SAME way he saw the website and can continue to work without hassle. This will work as a true back button for single page apps.