AngularJS - Templates not working

aBhijit picture aBhijit · May 25, 2014 · Viewed 7k times · Source

I am using Restangular to create a simple API using MEAN stack.

Here is my code:

index.html

<!DOCTYPE html>

<html data-ng-app="scotchTodo">
<head>
    <!-- META -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1"><!-- Optimize mobile viewport -->

    <title>Node/Angular Todo App</title>

    <!-- SCROLLS -->
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"><!-- load bootstrap -->
    <style>
        html{
            overflow-y:scroll;
        }
        body{
            padding-top:50px; 
        }
    </style>    

    <!-- SPELLS -->
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular.min.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.15/angular-route.min.js"></script>

    <script src="//cdnjs.cloudflare.com/ajax/libs/restangular/1.4.0/restangular.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>

    <script src="app.js"></script>
</head>
<body>
    <div data-ng-view>

    </div>
</body>
</html>

app.js

var scotchTodo = angular.module('scotchTodo', ['restangular','ngRoute']);

//config
scotchTodo.config(['$routeProvider','$locationProvider', function($routeProvider, $locationProvider) {

    $routeProvider
    .when('/',{
        templateUrl: 'list.html',
        controller: 'ListController'
    })
    .when('api/todos/:todo_id',{
        templateUrl: 'edit.html',
        controller: 'EditController'
    });

    $locationProvider.html5Mode(true);

}]);


//controllers
scotchTodo.controller('ListController', ['$scope', 'Restangular',
    function($scope, Restangular) {

        //GET ALL
        var baseTodo = Restangular.all('api/todos');
        baseTodo.getList().then(function(todos) {
            $scope.todos = todos;
        });

        //POST -> Save new
        $scope.save = function() {
            var baseTodo = Restangular.all('api/todos');
            var newTodo = {'text': $scope.text};
            baseTodo.post(newTodo).then(function(todos) {
                $scope.todos = todos;
                $scope.text = '';
            });
        };

        //DELETE
        $scope.delete = function(id) {
            var baseTodo = Restangular.one('api/todos', id);
            baseTodo.remove().then(function(todos) {
                $scope.todos = todos;
            });
        };

    }]);

scotchTodo.controller('EditController', ['$scope', 'Restangular','$routeParams',
    function($scope, Restangular, $routeParams) {   

        var baseTodo = Restangular.one('api/todos', id);

        baseTodo.getList().then(function(todo) {
            $scope.todo = todo[0];
            window.test = "dev";
        });

        //PUT -> Edit
        $scope.update = function(id){
            var baseTodo = Restangular.one('api/todos', id);
            baseTodo.text = "Edited";
            baseTodo.put().then(function(todos) {
                $scope.todos = todos;
            });
        };

    }]);

list.html

<div>
    <div data-ng-repeat="todo in todos">
        {{todo.text}}<a href="api/todos/{{todo._id}}">Edit</a><button data-ng-click="delete(todo._id)">X</button>
    </div>
    <input type="text" data-ng-model="text"/>
    <button data-ng-click="save()">Add</button>
</div>

edit.html

<div>
    <input type="text" data-ng-model="text" value="{{todo.text}}" />
    <button data-ng-click="update(todo._id)">Save</button>
</div>

server.js

// setup ========================
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bodyParser = require('body-parser');

//configuration =================
mongoose.connect('mongodb://127.0.0.1:27017/sl', function(err, db) {
    if (!err) {
        console.log("We are connected to " + db);
    }
});

app.use(express.static(__dirname + '/public'));
app.use(bodyParser());

// application -------------------------------------------------------------
app.get('/', function(req, res) {
    res.sendfile('./public/index.html'); // load the single view file (angular will handle the page changes on the front-end)
});

//listen ========================
app.listen(8080);
console.log('App started on the port 8080');

//define model ==================
var Todo = mongoose.model('Todo', {
    text: String
});

// routes ======================================================================

// api ---------------------------------------------------------------------

//get one todo
app.get('/api/todos/:todo_id', function(req, res) {

    // use mongoose to get all todos in the database
    Todo.find({
        _id: req.params.todo_id
    },function(err, todos) {

        // if there is an error retrieving, send the error. nothing after res.send(err) will execute
        if (err){
            res.send(err);
        }

        res.json(todos); // return all todos in JSON format
    });
});

// get all todos
app.get('/api/todos', function(req, res) {

    // use mongoose to get all todos in the database
    Todo.find(function(err, todos) {

        // if there is an error retrieving, send the error. nothing after res.send(err) will execute
        if (err){
            res.send(err);
        }

        res.json(todos); // return all todos in JSON format
    });
});

// create todo and send back all todos after creation
app.post('/api/todos', function(req, res) {

    // create a todo, information comes from AJAX request from Angular
    Todo.create({
        text: req.body.text,
        done: false
    }, function(err, todo) {
        if (err){
            res.send(err);
        }

        // get and return all the todos after you create another
        Todo.find(function(err, todos) {
            if (err)
                res.send(err);
            res.json(todos);
        });
    });

});

// update todo and send back all todos after creation
app.put('/api/todos/:todo_id', function(req, res) {

    // create a todo, information comes from AJAX request from Angular
    Todo.update({
        _id: req.params.todo_id
    }, {
        text:req.body.text
    }, function(err, todo) {
        if (err){
            res.send(err);
        }

        // get and return all the todos after you create another
        Todo.find(function(err, todos) {
            if (err)
                res.send(err);
            res.json(todos);
        });
    });

});


// delete a todo
app.delete('/api/todos/:todo_id', function(req, res) {
    Todo.remove({
        _id: req.params.todo_id
    }, function(err, todo) {
        if (err){
            res.send(err);
        }

        // get and return all the todos after you create another
        Todo.find(function(err, todos) {
            if (err){
                res.send(err);
            }
            res.json(todos);
        });
    });
});

The first page of my app loads perfectly fine. Here is the screenshot.

list.html

But when I click on either of the edit link it is supposed to load edit.html template. But it shows a blank page with no errors in console. Here is the screenshot. edit.html

I am unable to figure out what's wrong. Please help. Please ask if any other piece of code is needed. I added almost everything that I did. I know it is annoying and not recommended but I am not sure what part of my code is causing this issue.

EDIT 1:

My farthest guess is that the url for edit.html might not be getting resolved correctly. But I am not sure how to test that! Any help will be appriciated.

EDIT 2: Directory structure

Directory Structure

SOLUTION : Courtesy @ashu

The issue was this line in index.html

<script src="app.js"></script>

It should be:

<script src="/app.js"></script>

However, I am not clear why! Page was including app.js either way. It is weird.

Answer

ashu picture ashu · Jun 7, 2014

You have same routes for angular and express.

.when('api/todos/:todo_id',{
    templateUrl: 'edit.html',
    controller: 'EditController'
});

and in express

app.get('/api/todos/:todo_id', function(req, res) {

Hence, there is ambiguity. You can remove the 'api' part from angular urls.

.when('/todos/:todo_id', {
     templateUrl: 'edit.html',
     controller: 'EditController'
 })

And in server, you can add a catchall route which will handle all the non-api urls. For doing that you can move your app.get('/', function(req,res) {..}) call at the bottom after defining your api routes.

// < Define APi Routes here >

//catch all route for serving the html template
app.get('/*', function(req, res ) {
      res.sendfile('./public/index.html')
});

Also, in your app.js EditController, you forgot to initialise value of id.

var id = $routeParams.todo_id;