AngularJS with Closure Compiler - Simple Optimizations Fail - javascript

Can someone please shed light on why simple optimizations are failing for me in AngularJS? More importantly, how can I get them to work? (best practice/clarification for defining controllers is welcome too).
Here's my scenario, greatly simplified.
I'm using this HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html ng-app="">
<head>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/excite-bike/jquery-ui.css" type="text/css" rel="stylesheet" />
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js" type="text/javascript"></script>
<script src="simple_script.js" type="text/javascript"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js" type="text/javascript"></script>
<script>
//inline JS here
$(function() {
var spinner = $( "#qtySpinner" ).spinner({
spin: function( event, ui ) {
scope.qty = ui.value;
scope.$digest();
//console.log( event );
}
}); //end spinner
var scope = angular.element(spinner).scope();
});
</script>
<title>Angular Testing</title>
</head>
<body>
<div ng-controller="InvoiceCntl">
<b>Invoice:</b><br>
<br>
<table>
<tr>
<td>
Quantity
</td>
<td>
Cost
</td>
</tr>
<tr>
<td>
<input id="qtySpinner" type="integer" min="0" ng-model="qty" required="">
</td>
<td>
<input type="number" ng-model="cost" required="">
</td>
</tr>
</table>
<hr>
<b>Total:</b> {{calculate(qty,cost)}}
</div>
<br>
</body>
</html>
And I'm using this highly minification proof (I thought) JS file as "simple_script.js", which actually works as is:
//this works
window["InvoiceCntl"] = function ($scope) {
$scope["qty"] = 1;
$scope["cost"] = 19.95;
$scope["calculate"] = function (xval, yval) {
return xval * yval;
};
}
Minified using Google Closure Compiler (http://closure-compiler.appspot.com/home) with SIMPLE_OPTIMIZATIONS, I get this, which breaks:
//this breaks, seemingly because "a" replaces "$scope"?
window.InvoiceCntl=function(a){a.qty=1;a.cost=19.95;a.calculate=function(a,b){return a*b}};
I presume it's because $scope is a key word Angular looks for (Dependency Injection?), because when I add the extra step, manually, of passing $scope and assigning it to a in the first line of the function, it works. Like so:
//manually passing "$scope" and immediately assigning it to "a" works
window.InvoiceCntl=function($scope){var a=$scope;a.qty=1;a.cost=19.95;a.calculate=function(a,b){return a*b}};
Why doesn't $scope behave like a normal function parameter in this situation?
Is there a way to minify (simple or advanced) angular code using Closure compiler (or something else) without a manual step like this?
Is $scope configurable or is it a fixed key word, i.e., could I changed the key word to "$myscope" when I'm defining the controller? (not sure that helps me anyway)
Thanks.

You should read http://docs.angularjs.org/tutorial/step_05
I think your concern about injecting '$scope' is correct.
You can inject like following.
var module = angular.module('youApp', []);
module.controller('yourCtrl', ['$scope', function($scope) {
$scope["something"] = "somevalue";
})];
Edit: The minification renames $scope, you can prevent this by adding:
InvoiceCntl.$inject = ['$scope'];

Related

angular expressions do not work inside the controller scope

I am facing a strange problem with the below code..
whenever I remove the ng-controller="page" from the body tag, the expressions start getting evaluated. But on applying this controller on body tag, the expressions tend to get printed as text rather than being evaluated.
Below is my relevant code (Snippet):
<html ng-app="app">
<head>
<!-- links removed for brevity -->
<script>
var app = angular.module('app',[]);
app.controller('page',function($scope){
$scope.segment.name = 'asdf';
});
</script>
</head>
<body ng-controller="page" style="padding:0px;">
<!-- additional markup removed for brevity -->
<form class="navbar-form navbar-right" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Enter Portal ID" ng-model="page.segment.name"/>
</div>
<button class="btn btn-default">Search {{page.segment.name}}</button>
</form>
</body>
</html>
I am possibly making some blunder in the above code as the below code which I wrote as proof of concept works well.
POC code (Snippet):
<html ng-app="app">
<head>
<!-- links removed for brevity -->
</head>
<body ng-controller="page">
<a>Name : {{page.segment.name}}</a>
<input type = "text" ng-model="page.segment.name"/>
</body>
<!-- links removed for brevity -->
<script>
var app = angular.module('app',[]);
app.controller('page',['$scope',function($scope){}]);
</script>
</html>
Kindly help
Thanks in advance..
You are probably getting an error in the console. I would guess it's something similar to "Cannot set property 'name' of undefined." What you are doing here is not valid:
$scope.segment.name = 'asdf';
You need to either do this:
$scope.segment = {};
$scope.segment.name = 'asdf';
Or this:
$scope.segment = { name: 'asdf' };
You have to create the segment object explicitly before you attempt to set properties on it.

Angularjs: Html render as text

I have an Angularjs script to create a navigation Bar using users parameters.
The problem is that, when i try to render the html structure, it always displays as plain text
This is the script:
var app = angular.module('userData', []);
app.controller('showUserData', ['$scope', function($scope){
$scope.userPar = JSON.parse(sessionStorage.userPar);
$scope.navBar = createNavBar($scope.userPar);
$scope.logOut = function(){
alert('Out you go!');
}
}]);
app.directive("otcNavbar", function(){
return {
template:"{{navBar}}"
};
});
function createNavBar(userPar){
var navBar = "<ul id=\"nav\">";
if(userPar.isDir == 1){
navBar +=
"<li class=\"seccion\">Director</li>";
}
if(userPar.isSys == 1){
navBar +=
"<li class=\"seccion\">System</li>";
}
navBar +=
"<li class=\"seccion\">Log Out</li></ul>";
return navBar;
}
This is the template:
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link id="css" rel="stylesheet" href="main.css" type="text/css" />
<script src="angular.min.js"></script>
<script src="mainApp.js"></script>
</head>
<body>
<div id="userPar" ng-app="userData" ng-controller="showUserData">
<table id="sessionData">'.
<tr><th colspan="6">User Data</th></tr>'.
<tr><td class="R B">User No.:</td><td class="L">{{userPar.suID}}</td>.
<td class="R B">Name:</td><td class="L">{{userPar.sulName}}, {{userPar.sufName}}</td>.
<td class="R B">Access:</td><td class="L">{{userPar.utName}}</td></tr>
</table>
<!--<div id="navBar" otc-navbar></div>-->
<div id="navBar" ng-bind="navBar"></div>
</div>
</body>
</html>
I tried a directive (otc-navbar) and ng-bind, but in both cases i get plain text instead of html.
How can I make it work? Thanks in advance!
Use ng-bind-html with angular sanitize
<div id="navBar" ng-bind-html="navBar"></div>
ng-bind-html will evaluates your expression and inserts the resulting HTML into the element. By default, the resulting HTML content will be sanitized using the $sanitize service. In order to use ngSanitize, you need to include "angular-sanitize.js" in your application.
So include angular-sanitize.js in your page and inject the dependency to your angular module.
var app= angular.module('userData', ['ngSanitize']);
Here is a working sample on jsbin. :)
EDIT : As per the comment you wish ng-sanitize to not strip out the html attributes.
By default, ng-sanitize strips certain attributes out. If you really want that to not happen, You can use the $sce service's trustAsHtml method to explicitly tell angular that the markup you are passing is safe.
Here is a working sample

Calling an AngularJS controller function from outside of it

I have the following code:
app.controller('MatrixExpertCtrl', function($scope,$http){
$scope.PassedMatrix=[];
$scope.GetMatrixFromServer=function(){
$http.get("http://localhost:3000/getmatrixfromdb").success(function(resp){
alert("The matrix grabbed from the server is: " + resp[0].ans);
$scope.PassedMatrix.push(resp[0].ans);
});
};
$scope.DispSize=function(){
alert("testing");
alert("The size is "+$scope.PassedMatrix[0].size) ;
};
//$scope.GetMatrixFromServer();
});
Now, suppose, in HTML, I have something like this:
<div class="col-sm-3 col-md-3 col-lg-3">
<div class="text-center">
<h3>Example Survey</h3>
<p>example paragrah</p>
<p>More random text</p>
<p>ending the paragraphs</p>
<button id="updmat" ng-click="DispSize();" type="button" class="btn btn-default">Updates</button>
</div>
//Much more code
<div id="body2">
<div class="col-sm-6 col-md-6 col-lg-6" style="background-color:#ecf0f1;">
<div ng-controller="MatrixExpertCtrl" ng-app="app" data-ng-init="GetMatrixFromServer()">
<div class="text-center">
Meaning with this:
Is it possible to call a function that is defined inside a controller, from outside of the scope of that same controller?
I need this because the function is manipulating a shared object, owned by the controller in a very very simple fashion (for example, clicking on the button changes the color of a given element).
I am having trouble to make this work, any help will be appreciated.
I think that declaring some data structures as global would help me solving this problem, but, I would like to avoid doing that because, besides it being bad practice, it might bring me more problems in the future.
If i understand your problem correctly than what you basically do have is one utility function which will work on your shared object and do your useful things (i.e. clicking on the button changes the color of a given element) and now you do require the same behaviour in another controller outside of it's scope. You can achieve the same thing in 2 different ways :
1).Create a service and make it available in your controllers like this :
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.factory('myService', function() {
return {
changeColour: function() {
alert("Changing the colour to green!");
}
};
});
myApp.controller('MainCtrl', ['$scope', 'myService', function($scope,
myService) {
$scope.callChangeColour = function() {
myService.changeColour();
}
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="callChangeColour()">Call ChangeColour</button>
</body>
</html>
Pros&Cons: More angularistic way, but overhead to add dependency in every different controllers and adding methods accordingly.
2).Access it via rootscope
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.angularjs.org/1.1.2/angular.min.js"></script>
<script type="text/javascript">
var myApp = angular.module('myApp', []);
myApp.run(function($rootScope) {
$rootScope.globalChangeColour = function() {
alert("Changing the colour to green!");
};
});
myApp.controller('MainCtrl', ['$scope', function($scope){
}]);
</script>
</head>
<body ng-controller="MainCtrl">
<button ng-click="globalChangeColour()">Call global changing colour</button>
</body>
</html>
Pros&Cons: In this way, all of your templates can call your method without having to pass it to the template from the controller. polluting Root scope if there are lots of such methods.
try removing semicolon
ng-click="DispSize()"
because it binds ng-click directive to the function.

In angularjs, calling controller as constructor is not working

i am new to angularjs and i tried practicing some examples. In the below stated example I am trying to initialize a value and setting it in scope. it is working fine, if i initialize the controller and scope values as given in the commented lines . but instead of those commented lines of code, if i try to set a controller and initialize scope value , like a regular javascript constructor function, then my example is not working.. Please anyone kindly explain why it is not working ?
<html>
<head>
<title>My Practices</title>
<script type="text/javascript" src="lib/angular.min.js"></script>
<script type="text/javascript">
/*var myApp = angular.module('myApp', []);
myApp.controller('HelloController', ['$scope', function($scope){
$scope.value = "World";
}]);*/
var HelloController = function ($scope) {
$scope.value = 'World';
}
</script>
</head>
<body ng-app>
<div ng-controller="HelloController">
<input type="text" ng-model="value" placeholder="enter your name">
<div>Hi {{value}} !! </div> </div>
</body>
</html>

How to retrieve the ng-model value to a controller in AngularJS

I have a simple view with an input text field in which the user enter his name and a controller must retrieve that value an assign to employeeDescription variable. The problem is that the ng-model value (from the input) doesn't come to the controller, I just tried using $watch like Radim Köhler explains Cannot get model value in controller method in angular js but doesn't work. I just think it must be simple.
Also a just try by retrieving the name variable (ng-model) from the $scope, like $scope.employeeDescription = $scope.name; but doesn't retrieve value and the Google chrome console doesn't give me any output about that. Any solution?
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/bootstrap.min.css">
</head>
<body ng-app='angularApp'>
<div ng-controller='nameController'>
<div>
Name <input type="text" ng-model='name' />
</div>
Welcome {{employeeDescription}}
</div>
<script type="text/javascript" src="angular.min.js"></script>
<script type="text/javascript" src="app.js"></script>
</body>
</html>
app.js
(function(angular) {
var myAppModule = angular.module('angularApp', []);
myAppModule.controller('nameController', function($scope) {
$scope.employeeDescription = name;
});
})(window.angular);
Your code should fail, cause the 'name' variable in the controller is never defined. You have two variables defined in your code: $scope.employeeDescription and $scope.name (defined in the ng-model of the input). Note that '$scope.name' is being defined, not 'name'.
My suggestion is to leave only one variable:
<div>
Name <input type="text" ng-model='employeeDescription' />
</div>
myAppModule.controller('nameController', function($scope) {
$scope.$watch('employeeDescription', function() {
console.log($scope.employeeDescription);
});
});

Categories