6

I am still learning angularjs and I have problem with understanding difference between $scope and model object and this currently block me to organize (use some best practice) my app.
As I understand $scope should be read only (watched some tutorials where I heard this).

So when I load app I should use service to get some data from database and store it in model.

UPDATE

Right now all data that I get from server are stored in controller $scope and I am trying to move it to services and make controller dumber.
I also check this article and I am trying to use second or third option but still can't find best way to implement it.
This is my service and controller:

function dataService($http) {
        var service = {
            getToDoList: getToDoList,
            getToDoListByType: getToDoListByType,
            getToDoById: getToDoById
        };

        return service;

        function getToDoList() { }
        function getToDoListByType() { }
        function getToDoById() { }
    }
function toDoController($location) {       
        var vm = this;

        vm.todos = [];
        vm.title = 'toDoController';

        activate();

        function activate() {
            return getToDos().then(function () {
                console.log("ToDos loaded");
            });
        }

        function getToDos() {
            return dataservice.getToDoList()
                .then(function (data) {
                    vm.todos = data;
                    return vm.todos;
                });
        }
    }

But in this implementation to do list is again in the controller.

Where should I store this list after I get it from server and from where it should be set (from controller or from service) so I can manipulate this list in a cached way (keep it local and update it occasionally)?

I am coming from C# world and there I always used entity objects (e.g. User, Product, Item etc.) populate those object in a loop and store it in a list. I can't find a way how should I use this approach in angular too and if yes should that be service with properties only?

I use one service to keep the list and one service to contain CRUD functions. If I load data in $scope from my model how to update that scope later if some other part of code change data in my model?

Change can come from another controller or be updated via SignalR for example. Also as I heard when I update data on view as $scope should be readonly I need to update service and again how and when to update $scope then?

I am sorry if my question is too noob but I would be thankful if someone can help me to understand where to keep what in angular?

1110
  • 7,829
  • 55
  • 176
  • 334
  • At some point the ToDo list has to be in the controller or you won't be able to bind to it. What you want to abstract away is the operations that reach out to the service, so that if the server implementation changes you just need to change the data service, and not the controller. It also helps for unit testing because you can make a mock data service to return fake data. I also do the same for prototyping before writing any server code. – nbering May 03 '16 at 13:30
  • In example from question I created a service that abstract server calls. I will give another example: `Service gets the list` > `return list to controller $scope` > `display it on view` => but now on the view I manipulate that array in items by adding some properties to each item (grouping, coloring etc.) that I need to add in order to display those data. And let's say that I wan't to change all those properties again (run loop again) but I need original data that I got from server. But as I returned list to controller I don't have original array anymore. – 1110 May 03 '16 at 13:42
  • And as I don't want to make server call again I don't know what is good practice - where to keep that model. – 1110 May 03 '16 at 13:42
  • To keep your model there is : @C.C. answer. Module like angularCache to have some cache that handle expiration of data and so on. sessionStorage for recent browsers. All of those elements must be used at **Service** layer. – Walfrat May 04 '16 at 07:41

4 Answers4

4

The model is used more often in a software architectural pattern named Model–view–controller (MVC). You can't understand how the model works without having a knowledge of the full pattern. In this pattern, the web application is broken into components with the purpose to separate responsibilities. I will direct you with an example of full TODO code to see the real use of MVC. enter image description here

Model: Take/manipulate all the domain data (more often you take this from the server). Here you create a clear API that gives access to the data, that happen with services. In a service you get data from the server, you keep them inside and next you deliver some functions that give access and when someone needs these data he just use the injection to take access to the service. Think of this kind of service as something like a singleton class with data, get/set and other methods. A rule is: if you don't know where something is going, more likely is going to the service.(FULL CODE)

.factory('api', function ($resource) {
    'use strict';

    var store = {
        //HERE IS THE API
        todos: [],

        api: $resource('/api/todos/:id', null,
            {
                update: { method:'PUT' }
            }
        ),
        clearCompleted: function ()[..]
        delete: function (todo)[..]
        get: function () [..]
        insert: function (todo)[..] 
        put: function (todo)[..]
    };
    return store;
})

Controller: In the images above, you can see easy the controller only get and not give from user interaction. Controllers don't manipulate the Dom. The data here is going from view (user) to the controller by using the scope (or using this inside the controller) and later manipulate the model using the functions we get by inject the service (model). Many times we make the controller act as a mediator, which break the rule of MVC, by querying the model and passing the result to the view, that is a different pattern name MVP. A rule is: Your controllers must always be as leaner as possible.(FULL CODE)

.controller('TodoCtrl', function TodoCtrl($scope, $routeParams, $filter, store) {
  //store is the model, we make this in app.js
  //is the result of a factory we make up,and we named "api"
  var todos = $scope.todos = store.todos;
  [..]
  //in html we call removeTODO
  //<button class="destroy" ng-click="removeTodo(todo)"></button>
  //We use user interaction to manipulate the model!
  $scope.removeTodo = function (todo) {
    store.delete(todo);//we use the api we make
  };
  [..]

View: As you can see in the image, model updates the view, not the controller. How? With directives and filters. Be careful, the view have only the presentation of the data (data-binding). Don't contain complex logic. I want to be clear, in MVC the view should access the model directly. Directives and filters deliver this function. If you want to do a DOM manipulation you must use a directive (not a controller). Note: we put the dom manipulation inside the compile and link function of the directive, not in the controller.(FULL CODE1 FULL CODE2)

I have problem with understanding the difference between $scope and model object

Scope is just refer to model as we see up, but is not the model! Scope is used for user interaction too, controller depends on scope and controller depends on model.

so I can manipulate this list in a cached way (keep it local and update it occasionally)?

There are many ways to solve this. Regular we use observer pattern, but in angular there is another way to do this that is better in most of the times. Here an example:

angular
  .module("testApp", [])
  .service("myDataService", function(){
    this.dataContainer = {
      valA : "car",
      valB : "bike"
    }
  })
  .controller("testCtrl", [
    "$scope",
    "myDataService",
    function($scope, myDataService){
      $scope.data = function(){
        //you get always the update data and never store tha data
        //to the controller
        return myDataService.dataContainer;
      };
  }]);

For more information check this, has some amazing answers.

Community
  • 1
  • 1
Thomas Karachristos
  • 3,237
  • 18
  • 22
2

The Problem: You have some remote data. You want all your controllers to have access to it. You don't want them each to get it on their own.

One way to do this in Angular: Use a service. Services are all singletons. This means that your app will only have one instance of the service and can be used to share data. I looked at the link you shared and the below is an example of the second suggestion, "Service is a Model and a Service".

function dataService($http) {
    var todos= [];
    this.getTodos = function() { return todos; };

    this.getToDoList= function() {
        // use $http to get remote data and assign it to todos
    };
}

Now you can do TodoService.getData() anywhere you've injected it, say maybe your .run block, and from then on, TodoService.getTodos() will return the same data the service got previously.

Alternatively, you can use the service exclusively to get data and not to store (your link's 3rd suggestion). To do that, you would not store var todos in the service, or have a this.getTodos, you would only have the getData function (and other data get functions). Then from each controller, you would run TodoService.getData() to run the common http get function.

Where should I store this list after I get it from server and from where it should be set (from controller or from service) so I can manipulate this list in a cached way (keep it local and update it occasionally)?

If you want to store and manipulate it in a cached way, you want to keep your data in the service. Your controllers will get the data from the service. There are a bunch of articles on using services to talk between controllers. They talk about using $broadcast to send your own events so that an update to one controller will update other independent controllers.

In either case: you do want to bind the todos list to $scope in your controller. This will allow you to output its contents in your view and use Angular magic like 2-way binding. In your code:

function toDoController($scope, dataService) {      
    $scope.todos = [];

    activate();

    function activate() {
        return getToDos().then(function () {
            console.log("ToDos loaded");
        });
    };

    function getToDos() {
        return dataService.getToDoList()
            .then(function (data) {
                $scope.todos = data;
                return $scope.todos;
            });
    };
}

Then in your view, you can just reference {{todos}}.

C.C.
  • 565
  • 2
  • 7
2

Angular doesn't come with an opinionated way to store data.

Some projects that have addressed this and other related issues:

https://github.com/mgonto/restangular
https://github.com/adrianlee44/ng-backbone
https://github.com/paysavvy/immutable-angular

What I've done in the past is write up a models and collections module that stores data. These are just simple constructors.

angular
  .module('app.models', [])
  .factory('app.models.User', ['$resource', function($resource) {

    function User(name) {
      this.name = name;
    }

    User.prototype.sayName = function() { 
      console.log(this.name) 
    };

    User.prototype.getInfo = function(params) {
      $resource.get(params).then(res => {
        this.info = res.data;
      });
    };

    return User;

  });

And then in your view model you hook up the view...to the model!

['app.models.User', function Controller(User) {
  this.user = new User('bob');
}]

<div>
  <button ng-click="vm.user.sayName()">say name</button>
</div>
Daniel Lizik
  • 3,058
  • 2
  • 20
  • 42
1

I haven't read exactly the same tutorials as you have, but I generally refer to the Angular Style Guide originally published by John Papa with considerable feedback from the Angular community.

If you're using SignalR to update your models in real-time, I think what you're looking for is the concept of Unidirectional Data Flow. I don't have a great resource to point you at for this, but I've seen some examples of SignalR and Angular that you might find just looking around for the basic idea.

Overall, the goal is to have updates from from the server, to your application. So if your controller updates a value, your data model isn't updated by the controller code. Your application sends a write to the server and the server sends the new value back to the AngularJS app.

Angular versions before 1.5 did not have a concept of one-way data binding, so if you're using ng-model 2-way binding is automatic, so you generally need to treat the value you're binding against as temporary and then sync state with a cached value or the server when a user is done editing data.

Your question is pretty broad, so if you'd like a more specific answer or How To, you might want to include some information about what type of application you're writing and sometimes the size of the application (number of controllers/features) will give an idea on what practices will serve you best. Some of the best practices for very large apps seem like anti-patterns for simple weekend project apps.

nbering
  • 2,725
  • 1
  • 23
  • 31
  • Hi I have to reopen question. I updated question to be more specific. I need to setup my app for large app. And don't have any kind of mentor in this. My updated question contain details. – 1110 May 03 '16 at 12:32