Clarification in using $location object in AngularJS - javascript

I'm very new to Angular JS and am running into a slight snag. I'm sure this is probably easy to answer but my research so far has been unsuccessful.
I'm using Visual Studio as my IDE and have followed the tutorial here to start a project using AngularJS.
The initial output of the tutorial works great. What I'm trying to do now is add in support for reading a parameter from the url. Something like index.html?target=bob
I found this answer which shows the general methods for using $location to grab the paramters from the url and display the results on the page.
I am having issues applying this logic to my current structure. I assume it's something basic that I've missed so far so apologies if this question is too simplistic.
Here's my current code.
index.html
<!DOCTYPE html>
<html ng-app="app">
<head>
<title></title>
</head>
<body ng-controller="Main as vm">
<input type="text" ng-model="vm.food" placeholder="Enter food" />
<p>Sriracha sauce is great with {{vm.food}}!</p>
Target: {{vm.test}}<br />
<script src="Scripts/angular.min.js"></script>
<script src="app/app.module.js"></script>
<script src="app/main.js"></script>
</body>
</html>
app.module.js
(function () {
'use strict';
angular.module('app', [], function ($locationProvider) {
$locationProvider.html5Mode(true);
});
})();
main.js
(function () {
'use strict';
angular
.module('app')
.controller('Main', main($location));
function main($location) {
var vm = this;
vm.food = 'pizza';
vm.test = $location.search()['target']
}
})();
The page renders as such:
I am getting the following errors in the console which seem obvious but I'm at a loss as to what my next step should be.
Clearly the issue lies in main not understanding $location. But why? What do?!
Thanks!
SOLVED
Both Claise and squiroid provided excellent answers to this below.
Since squiroid's solution is what I'm currently using I'll accept that answer but Claise's answer provides a bit more in depth information on this issue for future reference.
I'd like to note also that the second error listed above was solved by
adding <base href="/"> to the head section of index.html
Thanks everyone!

You are using a derivative style of angular declarations here, which allows you to declare all the controllers in a single block, with all the functions following, rather than the traditional inline method. This works, but you have made a few errors in your use of this form.
First, the .controller('Main', main($location)); line is incorrect. the function .controller() is accepting the name of the function as an argument, it is not calling the function. Therefore, the (), and the list of arguments is unnecessary. controller('Main', main); is what is expected here.
Moving to your function declaration next, function main($location) {...} will work, as long as your code is not minified. As soon as you minify your code, angular will no longer be able to identify which dependency $location represents, and the code will break.
Again, you can refer to inline injections, where you use
.controller('Main',['$inject', main]);
but you also have an option to provide another separation of the dependencies from the declarations.
Angular provides an alternative method, $inject. You can assign an array to the $inject property of your function, to provide the correct dependencies. main.$inject = ['$location'];.
In summary, here is the corrected main.js file:
(function () {
'use strict';
angular
.module('app')
.controller('Main', main);
function main($location) {
var vm = this;
vm.food = 'pizza';
vm.test = $location.search()['target']
}
main.$inject = ['$location'];
})();
And a Plunker showing the changes: http://plnkr.co/edit/j9FnhiFPjkSrY5muhb0J?p=preview

Here is the working plunker for you.
https://plnkr.co/edit/GUDPceYFLNldiqd94vZ1?p=preview
Some code needs to be changed in controller.
(function () {
'use strict';
angular
.module('app')
.controller('Main', ['$location', main]);
function main($location) {
var vm = this;
vm.food = 'pizza';
vm.test = $location.search()['target']
}
})();
There were two problems:
Problem with you code is that you have called the main method instead of passing it as callback function
You haven't passed the $location from controller to the callback method.
['$location', main]
EDIT: Updated the plunker and give the appropriate answer.

(function () {
'use strict';
angular.module('app', [])
.config(function ($locationProvider) {
$locationProvider.html5Mode(true);
}));
})();
Try placing the locationProvider under config in your app.module.js as above. Also in your main.js, if 'target' is the key value of the parameter you can write vm.test = $location.search('target');
(function () {
'use strict';
angular
.module('app')
.controller('Main', function($location) {
var vm = this;
vm.food = 'pizza';
vm.test = $location.search('target');
})();

Related

Scope variable not visible within ng-click handler

I'm pretty new to Angular and am trying to figure out what's wrong here. There is a controller defined like this:
(function(){
function myController($scope, CommsFactory) {
$scope.doSomething = function() {
var x = $scope; // <- Doesn't work because $scope is not defined
}
}
angular
.module('aModule')
.controller('myController', myController);
})();
The doSomething() method is then called by a button click like:
<input type="button" ng-click="doSomething()" class="btn--link" value="do it"/>
This seems straightforward to me but the problem is that, when I break within the method, $scope is not defined. This is different from most of the examples I've seen, and I can't figure out why. Shouldn't it be visible here? Obviously a lot of code is missing - I've tried to show only the relevant bits - could I be missing something somewhere else?
You're declaring a module then you need to add [].
Something like this:
angular.module('aModule', [])
.controller('myController', myController);
Usage
angular.module(name, [requires], [configFn]);
Arguments
name.- The name of the module to create or retrieve.
requires (optional).- If specified then new module is being created. If unspecified then the module is being retrieved for further
configuration.
configFn (optional).- Optional configuration function for the module. Same as Module#config().
Please, I would to recommend to read this guide about Angular Module:
angular.module
(function() {
function myController($scope) {
$scope.doSomething = function() {
var x = $scope;
console.log(x);
}
}
angular
.module('aModule', [])
.controller('myController', myController);
})();
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div data-ng-app="aModule">
<div data-ng-controller="myController">
<input type="button" ng-click="doSomething()" class="btn--link" value="do it" />
</div>
</div>
Your code is generally working fine as demonstrated in this fiddle.
Your main problem seems to be in the usage of $scope. $scope is an object containing all variables and methods which should be available in the corresponding template. For this reason, you would always reference a member of $scope, instead of the whole object.
Furthermore, John Papas AngularJS style guide recommends the usage of controllerAs in favor of $scope for multiple reasons as stated in Y030
By convention, you should also give your controllers uppercase names and use explicit Dependency Injection
A typical use case would rather look like:
(function(){
angular
.module('aModule', [])
.controller('myController', MyController);
MyController.$inject = ['$scope', 'CommsFactory'];
function MyController($scope, CommsFactory) {
var vm = this;
vm.doSomething = doSomething;
function doSomething() {
var $scope.x = "Did it!";
}
}
})();
SOLVED: It turns out that what I was experiencing had something to do with the way in which the Chrome debugger works. It appears to do some kind of lazy loading of variables defined outside of the function in which you break (or at least this as far as I've characterized it). What this means, at least in my case, is that if I break inside of the method, and $scope isn't actually used within that method (which, unfortunately, I was doing a lot because I was trying to verify that $scope was visible), then the debugger will report that $scope is unavailable.

Error: "angular was used before it was defined" but online editors able to output the result

I couldn't get an output from this code when I ran it my local machine but the weird part is online editors able to give me an output: http://codepen.io/anon/pen/waXGom
*Im using Adobe Brackets editor
Here is my HTML code:
<!DOCTYPE html >
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js"></script>
<script src= "app.js"></script>
<body ng-app="MyApp">
<div ng-controller="Controller2">
<button ng-click="add()">Add Object</button>
<button ng-click="reset()">Reset</button>
<div remove="remove($index)" ng-repeat="name in names">
{{name}}
</div>
</div>
</body>
</html>
Here is my JS code:
var myApp = angular.module('myApp', []);
myApp.controller('Controller2', function ($scope) {
$scope.names = [];
var data = ['Dean', 'Andy', 'Dan', 'Wizek', 'Pete', 'Pawel', 'Chris', 'Misko'];
$scope.add = function() {
if (data.length)
$scope.names.push(data.pop());
};
$scope.reset = function(index) {
data.push($scope.names.splice(index, 1)[0]);
};
});
Please help me to figure it out...
Do you mean you see the angular used before it was defined error from JSLint? That'd be because you're using angular, a global, in your JavaScript code without letting JSLint know it's in scope first.
JSLint operates on a page-by-page level, and can't tell what's in the global context from outside includes. You've loaded Angular into the global context in another include, but JSLint can't tell that when it looks at this single JavaScript file by itself.
To be clear, then, just this line should already cause JSLint to report the error:
var myApp = angular.module('myApp', []);
You have to add a global directive to let JSLint know what's in the global context.
/*global angular */
var myApp = angular.module('myApp', []);
Now you're golden.
Just for fun, here's a fully JSLinted version of your JavaScript file snippet. Didn't take much. Note that I had to add squiggly brackets around your if block and the jslint directive white:true for what it considers non-standard whitespace. More on all that here.
/*jslint white:true */
/*global angular */
var myApp = angular.module('myApp', []);
myApp.controller('Controller2', function ($scope) {
"use strict";
$scope.names = [];
var data = ['Dean', 'Andy', 'Dan', 'Wizek', 'Pete', 'Pawel', 'Chris', 'Misko'];
$scope.add = function() {
if (data.length)
{
$scope.names.push(data.pop());
}
};
$scope.reset = function(index) {
data.push($scope.names.splice(index, 1)[0]);
};
});
Just above your app declaration, add the following comment...
/* global angular */
It should look like this...
/ * global angular */
var myApp = angular.module('myApp', []);
The comment can contain a comma separated list of variables which lets JSLint know they (can/may have been) be declared in other files.
There is a typo I am getting. In your HTML you're using
<body ng-app="MyApp">
and in controller
angular.module('myApp', []);
You should use
<body ng-app="myApp">
You're getting console error because your page had included AngularJS. Its not consider a good practice to load scripts in head or start of the page. You should load all scripts at the bottom of the page or I can say in footer.

How to organize angular js code?

I am working on a project using Angular as front end framework. I put all my code into abc.js under this format:
(function(){
var globalVariable1;
var globalVariable2;
var globalVariable3;
var globalVariable4;
var myApp = angular.module('coolapp',[]);
myApp.controller('Controller1', [$scope, $rootScope, function(){
//blah blah
}]);
myApp.factory('Factory1', function(){
//blah blah
});
//....
})();
Now it is more than 500 lines with quite a few global variables.
I plan to separate them into different files. What should I do? Let's say I created
main.js
(function(){
var globalVariable1;
var globalVariable2;
var globalVariable3;
var globalVariable4;
var myApp = angular.module('coolapp',[]);
})();
Controller1.js
(function(){
var myApp = angular.module('coolapp');
myApp.controller('Controller1', [$scope, $rootScope, function(){
//blah blah
}]);
})();
What should I write in main.js to require or include or inject(not sure what is the difference between them) Controller1.js?
You just need to create another module which handle your all controllers and provide dependency to your main module:-
main.js
var myApp = angular.module('coolapp',['cntrlApp']);
Controller1.js
var myApp = angular.module('cntrlApp');
myApp.controller('Controller1', [$scope, $rootScope, function(){
//blah blah
}]);
Yes. Your way is the right way. You also can look on RequireJS
Assuming your project is going to continue to grow, I would suggest looking in to using require.js with Typescript. We've implemented that and it has greatly increased the robustness of our code, and it's much easier to manage and refactor our code.
Then depending on how complex your application is going to be you might look at structuring it in to a domain model, and then views, services, etc.... Obviously a lot of this depends on how complex of a project you're building and where you think you're project is going.
There are a lot of opinions of how to best structure an Angular app. You seem to be headed in a good direction. Here's how I prefer to do it:
In your index.html, include your scripts individually. The self-invoking anonymous functions will set themselves up with Angular. Use a build process like Grunt or Gulp to minify and concatenate these later.
<script src="main.js"></script>
<script src="Controller1.js"></script>
<script src="Factory1.js"></script>
In your main.js (I call it app.js typically, I think it's user prefererence).
(function() {
'use strict';
angular
.module('MyApp', [
// any module dependencies
]);
})();
In your Controller1.js, set up your controller, define it as an Angular controller and part of the application.
(function() {
'use strict';
angular
.module('MyApp')
.controller('Controller1', Controller1);
function Controller1($scope) {
// do something
}
})();
In your Factory1.js, set up your factory function and define it as an Angular factory and part of the application.
(function() {
'use strict';
angular
.module('MyApp')
.controller('Factory1', Factory1);
function Factory1() {
return function() {
// return something
};
}
})();
After that, there isn't much to do. My main.js usually has a config function in it and handles the routing. You can initialize the app by adding the ng-app="MyApp" to your html or body. You can initialize your Controller1 simply by adding the attribute ng-controller="Controller1" to an element in your DOM. If you need Factory1 to be accessible by Controller1, then when you define the function Controller1 in Controller1.js, do it like so function Controller1($scope, Factory1) {}.
That's all you really need to get it up and running with separate files.

When I reference local angular lib I get error: Argument is not a function, got undefined and CDN works ok? [duplicate]

I am writing a sample application using angularjs. i got an error mentioned below on chrome browser.
Error is
Error: [ng:areq] http://errors.angularjs.org/1.3.0-beta.17/ng/areq?p0=ContactController&p1=not%20a%20function%2C%20got%20undefined
Which renders as
Argument 'ContactController' is not a function, got undefined
Code
<!DOCTYPE html>
<html ng-app>
<head>
<script src="../angular.min.js"></script>
<script type="text/javascript">
function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}
</script>
</head>
<body>
<h1> modules sample </h1>
<div ng-controller="ContactController">
Email:<input type="text" ng-model="newcontact">
<button ng-click="add()">Add</button>
<h2> Contacts </h2>
<ul>
<li ng-repeat="contact in contacts"> {{contact}} </li>
</ul>
</div>
</body>
</html>
With Angular 1.3+ you can no longer use global controller declaration on the global scope (Without explicit registration). You would need to register the controller using module.controller syntax.
Example:-
angular.module('app', [])
.controller('ContactController', ['$scope', function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}]);
or
function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}
ContactController.$inject = ['$scope'];
angular.module('app', []).controller('ContactController', ContactController);
It is a breaking change but it can be turned off to use globals by using allowGlobals.
Example:-
angular.module('app')
.config(['$controllerProvider', function($controllerProvider) {
$controllerProvider.allowGlobals();
}]);
Here is the comment from Angular source:-
check if a controller with given name is registered via $controllerProvider
check if evaluating the string on the current scope returns a constructor
if $controllerProvider#allowGlobals, check window[constructor] on the global window object (not recommended)
.....
expression = controllers.hasOwnProperty(constructor)
? controllers[constructor]
: getter(locals.$scope, constructor, true) ||
(globals ? getter($window, constructor, true) : undefined);
Some additional checks:-
Do Make sure to put the appname in ng-app directive on your angular root element (eg:- html) as well. Example:- ng-app="myApp"
If everything is fine and you are still getting the issue do remember to make sure you have the right file included in the scripts.
You have not defined the same module twice in different places which results in any entities defined previously on the same module to be cleared out, Example angular.module('app',[]).controller(.. and again in another place angular.module('app',[]).service(.. (with both the scripts included of course) can cause the previously registered controller on the module app to be cleared out with the second recreation of module.
I got this problem because I had wrapped a controller-definition file in a closure:
(function() {
...stuff...
});
But I had forgotten to actually invoke that closure to execute that definition code and actually tell Javascript my controller existed. I.e., the above needs to be:
(function() {
...stuff...
})();
Note the () at the end.
I am a beginner with Angular and I did the basic mistake of not including the app name in the angular root element. So, changing the code from
<html data-ng-app>
to
<html data-ng-app="myApp">
worked for me. #PSL, has covered this already in his answer above.
I had this error because I didn't understand the difference between angular.module('myApp', []) and angular.module('myApp').
This creates the module 'myApp' and overwrites any existing module named 'myApp':
angular.module('myApp', [])
This retrieves an existing module 'myApp':
angular.module('myApp')
I had been overwriting my module in another file, using the first call above which created another module instead of retrieving as I expected.
More detail here: https://docs.angularjs.org/guide/module
I just migrate to angular 1.3.3 and I found that If I had multiple controllers in different files when app is override and I lost first declared containers.
I don't know if is a good practise, but maybe can be helpful for another one.
var app = app;
if(!app) {
app = angular.module('web', ['ui.bootstrap']);
}
app.controller('SearchCtrl', SearchCtrl);
I had this problem when I accidentally redeclared myApp:
var myApp = angular.module('myApp',[...]);
myApp.controller('Controller1', ...);
var myApp = angular.module('myApp',[...]);
myApp.controller('Controller2', ...);
After the redeclare, Controller1 stops working and raises the OP error.
Really great advise, except that the SAME error CAN occur simply by missing the critical script include on your root page
example:
page: index.html
np-app="saleApp"
Missing
<script src="./ordersController.js"></script>
When a Route is told what controller and view to serve up:
.when('/orders/:customerId', {
controller: 'OrdersController',
templateUrl: 'views/orders.html'
})
So essential the undefined controller issue CAN occur in this accidental mistake of not even referencing the controller!
This error might also occur when you have a large project with many modules.
Make sure that the app (module) used in you angular file is the same that you use in your template, in this example "thisApp".
app.js
angular
.module('thisApp', [])
.controller('ContactController', ['$scope', function ContactController($scope) {
$scope.contacts = ["abcd#gmail.com", "abcd#yahoo.co.in"];
$scope.add = function() {
$scope.contacts.push($scope.newcontact);
$scope.newcontact = "";
};
}]);
index.html
<html>
<body ng-app='thisApp' ng-controller='ContactController>
...
<script type="text/javascript" src="assets/js/angular.js"></script>
<script src="app.js"></script>
</body>
</html>
If all else fails and you are using Gulp or something similar...just rerun it!
I wasted 30mins quadruple checking everything when all it needed was a swift kick in the pants.
If you're using routes (high probability) and your config has a reference to a controller in a module that's not declared as dependency then initialisation might fail too.
E.g assuming you've configured ngRoute for your app, like
angular.module('yourModule',['ngRoute'])
.config(function($routeProvider, $httpProvider) { ... });
Be careful in the block that declares the routes,
.when('/resourcePath', {
templateUrl: 'resource.html',
controller: 'secondModuleController' //lives in secondModule
});
Declare secondModule as a dependency after 'ngRoute' should resolve the issue. I know I had this problem.
I was getting this error because I was using an older version of angular that wasn't compatible with my code.
These errors occurred, in my case, preceeded by syntax errors at list.find() fuction; 'find' method of a list not recognized by IE11, so has to replace by Filter method, which works for both IE11 and chrome.
refer https://github.com/flrs/visavail/issues/19
This error, in my case, preceded by syntax error at find method of a list in IE11. so replaced find method by filter method as suggested https://github.com/flrs/visavail/issues/19
then above controller not defined error resolved.
I got the same error while following an old tutorial with (not old enough) AngularJS 1.4.3. By far the simplest solution is to edit angular.js source from
function $ControllerProvider() {
var controllers = {},
globals = false;
to
function $ControllerProvider() {
var controllers = {},
globals = true;
and just follow the tutorial as-is, and the deprecated global functions just work as controllers.

Angular.js, equivalent controller writing that fails

I'm starting with Angular.js and have a question; What's wrong with the second way to express the controller? Take a look at the jsfiddle below
http://jsfiddle.net/yDhv8/
function HelloCtrl($scope, testFactory, testFactory2)
{
$scope.fromFactory = testFactory.sayHello("World");
$scope.fromFactory2 = testFactory2.sayHello("World");
}
myApp.controller('GoodbyeCtrl', ['$scope', 'testFactory', 'testFactory2', function($scope, testFactory, testFactory2) {
$scope.fromFactory = testFactory.sayGoodbye("World");
$scope.fromFactory2 = testFactory2.sayGoodbye("World");
}]);
Any references that may be useful to understand what's going on will be appreciated,
Cheers,
Everything is fine. you just got confused. use app.controller
myApp is the module name, not a variable name.
app.controller(......)
If you run this in a javascript debugger, you'll find that the variable 'myApp' is not defined. You can either use the 'app' reference that you assigned to the original module call, or use the following syntax:
angular.module('myApp').controller(...)

Categories

Resources