Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Closure Compiler break this AngularJS script?

I'm testing AngularJs with Play Framework 2.0 (Scala). Play uses Closure to minimize Javascript files.

My file is as follows:

// Define a Module 'todoList' for Angular that will load the views. In this example the views are very simple, it's just to show
// the concept
angular.module('todoList', ['taskDoneFilter', 'todoServices']).
    config(['$routeProvider', function($routeProvider) {
        $routeProvider.
            when('/all', {templateUrl: 'assets/angular/all.html',   controller: TodoCtrl}).
            when('/task/:id', {templateUrl: 'assets/angular/task.html', controller: TaskDetailCtrl}).
            otherwise({redirectTo: '/all'});
    }]);


// This filter allows us to convert strings. In this case, it adds an extra tick besides a task indicating if it's done or no
angular.module('taskDoneFilter', []).filter('checkmark', function() {
    return function(input) {
        return input ? '\u2713' : '\u2718';
    };
});


// When running tests with Jasmine the jsRoutes object is not defined, which means we need to use a default route for the http call below
// This kind of defeats the purpose of retrieving the routes via Play instead of hardcoding them, as we need a fallback for the tests
// but I decided to leave the code just to see that we have the possibility, in case I find a way to improve this.
var tasksUrl = '/tasks/all';
if(!(typeof jsRoutes === "undefined")) {
  tasksUrl = jsRoutes.controllers.Application.tasks().url ;
}

// Definition of a Service, that stores all the REST requests independently from the controllers, facilitating change
angular.module('todoServices', ['ngResource']).
    factory('All', function ($resource) {
        return $resource(tasksUrl, {}, {
            //The data model is loaded via a GET request to the app
            query: {method: 'GET', params: {}, isArray: true}
        });
    })
    .factory('Task', function ($resource) {
        return $resource('tasks', {}, {
            add: {method: 'POST'}
        });
    });

/**
 * This is the controller behind the view, as declared by ng-controller
 * All references to methods and data model in the view map to this controller
 * @param $scope model data injected into the controller
 * @constructor
 */
var TodoCtrl = ['$scope', 'All', 'Task', function($scope, All, Task) {
    // We use the service to query for the data
    $scope.todos = All.query();

    //when submitting the form, this is called. Model in the form is referenced (todoText) and we add the task to
    //the data model
    $scope.addTodo = function() {
        var txt = $scope.todoText;
        $scope.todos.push({text: txt, done: false});
        Task.save({msg: txt});
        $scope.todoText = '';  //clear the input!
    };

    // calculates the remaining todos, automatically called when the model changes to update the view
    // notice the use of 'angular' component for functional approach
    $scope.remaining = function() {
        var count = 0;
        angular.forEach($scope.todos, function(todo) {
            count += todo.done ? 0 : 1;
        });
        return count;
    };

    //another acton triggered by click (in this case on an anchor), which archives completed tasks
    $scope.archive = function() {
        var oldTodos = $scope.todos;
        $scope.todos = [];
        angular.forEach(oldTodos, function(todo) {
            if (!todo.done) $scope.todos.push(todo);
        });
    };
}];

// Task details controller, used in the routes to provide a second view for the application
var TaskDetailCtrl = ['$scope', '$routeParams', function($scope, $routeParams) {
    $scope.id = $routeParams.id;
}];

But when minimizing, it becomes:

var module$todo={};angular.module("todoList",["taskDoneFilter","todoServices"]).config(["$routeProvider",function($routeProvider){$routeProvider.when("/all",{templateUrl:"assets/angular/all.html",controller:TodoCtrl$$module$todo}).when("/task/:id",{templateUrl:"assets/angular/task.html",controller:TaskDetailCtrl$$module$todo}).otherwise({redirectTo:"/all"})}]);angular.module("taskDoneFilter",[]).filter("checkmark",function(){return function(input){return input?"\u2713":"\u2718"}});
var tasksUrl$$module$todo="/tasks/all";if(!(typeof jsRoutes==="undefined"))tasksUrl$$module$todo=jsRoutes.controllers.Application.tasks().url;angular.module("todoServices",["ngResource"]).factory("All",function($resource){return $resource(tasksUrl$$module$todo,{},{query:{method:"GET",params:{},isArray:true}})}).factory("Task",function($resource){return $resource("tasks",{},{add:{method:"POST"}})});
var TodoCtrl$$module$todo=["$scope","All","Task",function($scope,All,Task){$scope.todos=All.query();$scope.addTodo=function(){var txt=$scope.todoText;$scope.todos.push({text:txt,done:false});Task.save({msg:txt});$scope.todoText=""};$scope.remaining=function(){var count=0;angular.forEach($scope.todos,function(todo){count+=todo.done?0:1});return count};$scope.archive=function(){var oldTodos=$scope.todos;$scope.todos=[];angular.forEach(oldTodos,function(todo){if(!todo.done)$scope.todos.push(todo)})}}];
var TaskDetailCtrl$$module$todo=["$scope","$routeParams",function($scope,$routeParams){$scope.id=$routeParams.id}];

And then it stops working. Notice the:

var module$todo={};

and

var TodoCtrl$$module$todo=

which break the app.

Anyone knows why may this be happening?

like image 678
Pere Villega Avatar asked Dec 27 '22 11:12

Pere Villega


1 Answers

Your All & Task services are not 'minify safe'. You must use the array notation.

angular.module('todoServices', ['ngResource']).
    factory('All', ['$resource', function ($resource) {
        return $resource(tasksUrl, {}, {
            //The data model is loaded via a GET request to the app
            query: {method: 'GET', params: {}, isArray: true}
        });
    }])
    .factory('Task', ['$resource', function ($resource) {
        return $resource('tasks', {}, {
            add: {method: 'POST'}
        });
    }]);

Also, define your controller with angular.module(...).controller():

angular.module(...).controller('TodoCtrl', ['$scope', 'All', 'Task', function($scope, All, Task) {
}]);
like image 85
asgoth Avatar answered Dec 29 '22 01:12

asgoth