I'm finding Angular's use of models confusing. Angular seems to take the approach that a model can be anything you like - I.E. Angular does not include an explicit model class and you can use vanilla JavaScript objects as models.
In almost every Angular example I've seen, the model is effectively an object, either created by hand, or returned from an API call via a Resource. Because almost every Angular example I've looked at is simple, usually the model data stored on $scope in a controller and any state related to the model, for example selection, is also stored on the $scope in the controller. This works fine for simple apps/examples, but this seems like an oversimplification when apps become more complex. Model state stored in a controller is at risk of becoming contextual and being lost if the context changes, for example; A Controller storing selectedGallery
and selectedPhoto
can only store global selectedImage
, not a selectedPhoto
per gallery. In such a situation, using a controller per gallery might negate this problem, but would seem wasteful and probably inappropriate and unnecessary from a UI perspective.
Angular's definition of models seems closer to what I would consider a VO/DTO that is a dumb object passed between server and client. My instinct is to wrap such an object in what I would consider a Model - a class that maintains state relating to the DTO/VO (such as selection), offers mutators as needed to manipulate the DTO/VO, and notifies the rest of the application of changes to the underlying data. Obviously this last part is nicely taken care of by Angular's bindings, but I still see a strong use-case for the first two responsibilities.
However I haven't really seen this pattern used in the examples I've looked at, but neither have I seen what I would consider a scalable alternative. Angular seems to implicitly discourage using Services as models by enforcing Singletons (I know there are ways to get around this, but they don't seem widely used or approved of).
So how should I be keeping state on Model data?
[Edit] The second answer in this question is interesting and close to what I'm currently using.
You can maintain a state, either using "$scope", or "$rootScope" in AngularJS. In above example, "$scope" is used for page level or local variable and "$rootScope" is used for maintaining the state globally.
Methods using Stateprovider in AngularJS$stateProvider is used to define different states of one route. You can give the state a name, different controller, different view without having to use a direct href to a route. There are different methods that use the concept of $stateprovider in AngularJS.
State management is the process of managing the states of user controls. It helps developers build large-scale applications with heavy data communications while sustaining high application performance.
The AngularJS application is defined by ng-app="myApp". The application runs inside the <div>. The ng-controller="myCtrl" attribute is an AngularJS directive. It defines a controller.
$scope is Angular's data storage object. It's analogous to a database. $scope itself is not the model, but you can store models in $scope.
Each $scope has a parent $scope, all the way up to $rootScope forming a tree structure that loosely mirrors your DOM. When you call a directive which requires a new $scope, such as ng-controller, a new $scope object will be created and added to the tree.
$scope objects are connected using prototypical inheritance. This means that if you add a model at a higher level in the tree, it will be available to all the lower levels. This is a phenomenally powerful feature which makes the $scope hierarchy almost transparent to the template author.
The purpose of the controller is to initialise $scope. The same controller can initialize many $scope objects in different parts of the page. The controller is instantiated, sets up the $scope object and then exits. You can use the same controller to initialize many $scopes in different parts of the page.
In the case of your image gallery, you would have an imageGallery controller which you would then apply to every portion of the DOM which you want to be a gallery using the ng-controller directive. That portion of the page would get it's own $scope, which you would use to store the selectedPhoto attribute.
$scope inherits from its parent using plain old prototypical inheritance all the way up to $rootScope, so you can store your objects anywhere on the hierarchy that makes sense. You get a tree of $scope objects that roughly relates to your current DOM. If your DOM changes, new $scope objects are created for you as required.
$scope is just a plain JavaScript object. It's no more wasteful to create multiple $scope objects than it would be to create an array with multiple currentImage objects. It's a sensible way to organise your code.
In this way Angular does away with the old "where do I store my data" problem that we often find in JavaScript. It's the source of one of the really big productivity gains that we get from Angular.
Got global data (eg. a userId)? store it on $rootScope. Got local data (eg. a currentImage in a gallery where there are multiple gallery instances)? Store it on the $scope object that belongs to that gallery.
$scope is automatically available to you in the correct portion of the template.
Coming from a Rails background where we emphasise fat models and skinny controllers, I found Angular's 'barely there' models surprising. In fact, putting a lot of business logic in your model often leads to problems down the line, as we sometimes see with the User model in Rails which, if you're not careful, will grow until it becomes unmaintainable.
Any object can be a model. Models are typically defined using JSON in the controller, or AJAXed in from a server. A model might be a JSON object, or might be just a string, array, or even a number.
Of course, there's nothing to stop you adding additional functions to your model and storing them in the JSON object if you want to, but this would be porting in a paradigm that doesn't really fit with Angular.
Angular objects are typically repositories of data, not functions.
Of course the model that you hold on the client is not the real model. Your actual model, your single source of truth lives on the server. We synchronise it using an API, but if there's a conflict between the two the model in your database is obviously the ultimate victor.
This gives you privacy for things like discount codes, etc. The model you find in your front end is a synchronised version of the public properties of the real model, which is remote.
Say you want to write a method to do something to your model, synchronise it, or validate it for example. In other frameworks you might be tempted to extend your model with a method to do this. In Angular you would be more likely to write a service.
Services are singleton objects. Like any other JavaScript object you can put functions or data in them. Angular comes with a bunch of built in services, such as $http. You can build your own, and use dependency injection to automatically provide them to your controllers.
A service might contain methods to talk to a RESTful API for example, or to validate your data, or any other work you might need to do.
Of course you shouldn't use services as models. Use them as objects which can do stuff. Sometimes they do stuff to your model. It's a different way of thinking, but a workable one.
First of all, let's not forget that Angular is a web based framework and if you "keep your state" solely in an object, it will not survive user hitting refresh on their browser. Therefore, figuring out how to keep state of Model data in a web based application means figuring out how you are going to persist it so that your code will function in a browser environment.
Angular makes it really easy for you to persist your state using:
In your simple example, the storing of user actions such as selectedGallery
and selectedPhoto
can be represented using URL with something like:
// List of galleries .../gallery // List of photos in a gallery .../gallery/23 // A specific photo .../gallery/23/photo/2
The URL is critical because it allow your user to navigate the browser history using back
and forward
buttons. If you wish to share this state with other part of your application, web application provide wealth of methods for you accomplish that using cookie/localStorage, hidden frame/fields or even storing it in your server.
Once you defined your strategy on how to persist different state of your application, it should be easier to decide if you wish to access these persisted info using a singleton object as provided by .service
or an instance via .factory
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With