Search code examples
javascriptangularjsweb-servicescontrollerng-bind

AngularJS move part of Controller to Service


Consider the following code.

HTML:

<!doctype html>
<html ng-app="todoApp">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link href="/css/bootstrap.min.css" rel="stylesheet">
        <link href="/css/overlay-control.css" rel="stylesheet">
        <link href="/css/list-control.css" rel="stylesheet">

        <script src="http://code.jquery.com/jquery-1.10.2.js"></script>
        <script src="http://code.jquery.com/ui/1.11.2/jquery-ui.js"></script>
        <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
        <!--<script src="/js/Services/UserService.js"></script>-->
        <script src="/js/Controllers/MainController.js"></script>
        <!--<script src="/js/Controllers/UserController.js"></script>-->
        <script src="/js/bootstrap.min.js"></script>
    </head>
    <body ng-controller="MainController as myControl">
        <div id="overlaycover" ng-click="myControl.showUpd(0)"></div>

        <div id="overlaybox">
            <div class="col-md-12">
            <h4>Update:</h4>
            <form ng-submit="myControl.updTodo()">
                <div class="form-group">
                    <label for="updContent">Note:</label>
                    <textarea rows="5" cols="30" class="form-control" id="updContent" name="updContent" ng-model="noteupd.content"></textarea>
                </div>
                <div class="form-group">
                    <label for="updDeadline">Deadline (format YYYY-MM-DD HH:MM:SS):</label>
                    <input type="text" class="form-control" id="updDeadline" name="updDeadline" ng-model="noteupd.deadline" />
                </div>
                <div class="checkbox">
                    <label>
                        <input type="checkbox" id="updCompleted" name="updCompleted" ng-model="noteupd.completed" /> - Completed
                    </label>
                </div>
                <div class="form-group">
                    <input type="hidden" id="updID" ng-model="noteupd.id" /><br/>
                    <button type="submit" class="btn btn-info">Update</button>
                </div>
            </form>
            Click utside the square to close.
            </div>
        </div>

        <div class="container">
            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12" id="listDiv">
                <h1>Todo-list:</h1>
                <p>
                    <img src="/images/legend-normal.png"> - Unfinished&emsp;
                    <img src="/images/legend-completed.png"> - Finished&emsp;
                    <img src="/images/legend-late.png"> - Late  
                </p>
                <table class="table" id="list-table">
                    <tr>
                        <th>Note:</th>
                        <th>Author:</th>
                        <th>Project:</th>
                        <th>Created:</th>
                        <th>Deadline:</th>
                        <th colspan="2">Modify:</th>
                    </tr>
                    <tr ng-repeat="todo in myControl.todos" ng-class="rowClass(todo.completed, todo.deadline)">
                        <td> {{ todo.content }} </td>
                        <td> {{ todo.user }} </td>
                        <td> {{ todo.project }} </td>
                        <td> {{ todo.created }} </td>
                        <td> {{ todo.deadline }} </td>
                        <td><button type="button" class="btn btn-info" ng-click="myControl.showUpd(todo.id)">Update</button></td>
                        <td><button type="button" class="btn btn-danger" ng-click="myControl.delTodo(todo.id)">Delete</button></td>
                    </tr>
                </table>
            </div>
            <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12" id="formDiv">
                <h3>Add new note:</h3>
                <form ng-submit="myControl.addTodo()">
                    <div class="form-group">
                        <label for="newUser">User:</label>
                        <select ng-model="noteadd.user" class="form-control" id="newUser" name="newUser">
                            <option ng-repeat="user in myControl.users" value="{{ user.id }}">{{ user.name }}</option>
                        </select><br/>
                    </div>
                    <div class="form-group">
                        <label for="newProject">Project:</label>
                        <select ng-model="noteadd.project" class="form-control" id="newProject" name="newProject">
                            <option ng-repeat="project in myControl.projects" value="{{ project.id }}">{{ project.name }}</option>
                        </select><br/>
                    </div>
                    <div class="form-group">
                        <label for="newContent">Note:</label>
                        <textarea rows="5" cols="30" ng-model="noteadd.content" class="form-control" id="newContent" name="newContent"></textarea><br/>
                    </div>
                    <div class="form-group">
                        <label for="newDeadline">Deadline (format YYYY-MM-DD HH:MM:SS):</label>
                        <input type="text" ng-model="noteadd.deadline" class="form-control" id="newDeadline" name="newDeadline" /><br/>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-info">Add</button>
                    </div>
                </form>
            </div>
        </div>
    </body>
</html>

AngularJS Controller:

angular.module('todoApp', []).controller('MainController', function($scope, $http) {
    var thisApp = this;
    $scope.noteadd = {};
    var noteadd = $scope.noteadd;
    $scope.noteupd = {};
    var noteupd = $scope.noteupd;

    // Get all notes:
    $http({method : 'GET', url : 'http://localhost:8000/notes'})
        .then (function(response) {
            thisApp.todos = response.data;
        }, function() {
            alert("Error getting todo notes");
        });

    // Get all users:
    $http({method : 'GET',url : 'http://localhost:8000/users'})
        .then(function(response) {
            thisApp.users = response.data;
        }, function() {
            alert("Error getting users");
        });


    // Get all projects
    $http({method : 'GET', url : 'http://localhost:8000/projects'})
        .then(function(response) {
            thisApp.projects = response.data;
        }, function() {
            alert("Error getting projects");
        });

    // Add note to database
    thisApp.addTodo = function(noteadd)
    {
        $http({
            method : 'PUT', 
            url : 'http://localhost:8000/notes',
            data : $.param($scope.noteadd),
            headers : {'Content-Type': 'application/x-www-form-urlencoded'}
        }).then(function(response) {
            location.reload();
        }, function() {
            alert("Couldn't add note. Please try again!");
        });
    };

    // Hide note by setting removed to 1
    thisApp.delTodo = function(noteID)
    {
        var r = confirm("Are you sure?");

        if (r == true) {
            var noteObj = JSON.parse('{"id":' + noteID + '}'); // JSON for req
            $http({
                method : 'DELETE', 
                url : 'http://localhost:8000/notes',
                data : $.param(noteObj),
                headers : {'Content-Type': 'application/x-www-form-urlencoded'}
            }).then(function(response) {
                location.reload();
            }, function() {
                alert("Couldn't delete note. Please try again!");
            });
        }
    };

    // Show overlay with form for updating a note
    thisApp.showUpd = function(noteID)
    {
        var overlaycover = document.getElementById("overlaycover");
        var overlaybox = document.getElementById("overlaybox");
        overlaycover.style.opacity = .65;

        if(overlaycover.style.display == "block" || noteID == 0){ // For toggling overlay
            overlaycover.style.display = "none"; // Hide div overlaycover
            overlaybox.style.display = "none"; // Hide div overlaybox
        } else {
            $http({method : 'GET', url : 'http://localhost:8000/notes/' + noteID})
                .then (function(response) {
                    noteupd.content = response.data.content; // Update fields in form
                    noteupd.deadline = response.data.deadline;
                    noteupd.id = response.data.id;

                    if (response.data.completed == 1) {
                        noteupd.completed = true;
                    } else {
                        noteupd.completed = false;
                    }

                    overlaycover.style.display = "block"; // Show div overlaycover
                    overlaybox.style.display = "block"; // Show div overlaybox
                }, function() {
                    alert("Error getting todo note");
                });
        }
    }

    // Update a note
    thisApp.updTodo = function(noteupd)
    {
        $http({
            method : 'POST', 
            url : 'http://localhost:8000/notes',
            data : $.param($scope.noteupd),
            headers : {'Content-Type': 'application/x-www-form-urlencoded'}
        }).then(function(response) {
            location.reload();
        }, function() {
            alert("Couldn't add note. Please try again!");
        });
    }

    // Check if deadline passed for each note in list
    $scope.rowClass = function(completed, deadline)
    {
        var nowTime = Math.floor(Date.now()/1000);
        var deadTime = new Date(deadline);
        var deadUTC = Math.floor(deadTime/1000);

        if (completed == 1) {
            return "success"; // Note is completed
        } else if (deadUTC < nowTime) {
            return "danger"; // Note is not completed, deadline passed
        } else {
            return "active"; // Note is not completed, still time left
        }
    }
});

What I would like to do is to move all $http-calls to a service instead of having them in the controller like I have it now. I have searched the internet but I don't really understand the solutions i've found there.

Second, in several functions, as you can see, I use location.reload();. I would like to use ng-bind instead, but as the sam, I don't understand how this works.

Can someone please explain how I should do these two things?


Solution

  • Ok , let's for example create a Users factory and put all Users api related stuff inside:

    'use strict';
    
    angular
        .module('todoApp')
        .factory('Users', factory);
    
        function factory($http) {
            var service = {
                get: get,
                //edit: edit ...
            };
    
            return service;
    
            function get() {
                return $http({method : 'GET',url : 'http://localhost:8000/users'})
                .then(function(response) {
                    return response.data;
                });
    
            }
    
            //function edit(user) {
            //    return $http({method : 'PUT...
            //}
    
        }
    

    What you have to do next is inject that factory wherever you want to call it.

    So in your controller you essentially have to do this:

    angular.module('todoApp', [])
        .controller('MainController', function($scope, Users) {
    
        //.....
        function getUsers() {
            Users.get().then(function(data){
                thisApp.users = response.data;
            }, function() {
                alert("Error getting users");
            });
        }      
    
        getUsers();
        //...
    

    Repeat the same by creating the appropriate services for notes and projects.

    To not bloat the main app.js move this services to seperate files, users.service.js etc..
    I would also advise you to move the controllers to seperate files too.

    Just be careful:

    this is a module creation

    angular.module('todoApp', [])
    

    You create a module once.

    to attach a service/factory/controller/anything, when you are in that separate file, to that module you use this:

    angular.module('todoApp').anything
    

    Second, I assume you use location.reload to update the view with the new data.

    Let's say you edit/delete a User. Instead of reloading the page, call getUsers() in the then() of put/delete/create User.

    Hope it makes sense to you!

    PS: This styleguide by John Papas have been very helpful to me, I suggest you give it a read !