AngularJS: help registering custom service the right way - javascript

Newbie Question
Have been watching this great Angular Beginners course but got stuck in the register process.
Code project (plnkr.co)
index.html
<!DOCTYPE html>
<html ng-app="githubViewer">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.0/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script src="github.js"></script>
</head>
<body ng-controller="MainController">
<h1>{{message}}</h1>
{{ countdown }}
<form name="searchUser" ng-submit="search(username)">
<input type="search" required placeholder="Username to find" ng-model="username"/>
<input type="submit" value="Search">
</form>
<div ng-include="'userdetails.html'" ng-show="user"> </div>
</body>
</html>
github.js
(function() {
var github = function($http) { // requires the service $http
// Private implementation details //
var gettingUser = function(username) {
return $http.get("https://api.github.com/users/" + username)
.then(function(response) {
return response.data;
});
/* returning the promise that already comes with
the function to perform the data extration
*/
};
var gettingRepos = function(user) {
return $http.get(user.repos_url)
.then(function(response) {
return response.data;
});
/* returning a promise that it will return the data
- so the controller doesn't have to. */
};
// Public API //
return {
gettingUser: gettingUser,
gettingRepos: gettingRepos
};
// returns an object (github service)
};
var module = angular.module("githubViewer");
/* Not creating a module, just getting the reference
to the one created in script.js So no need to list
the dependencies in a list after githubViewer*/
// Register the Service
module.factory("$github", github);
}());
script.js
(function() {
var app = angular.module("githubViewer", []);
var MainController = function(
$scope, $github, $interval, $log,
$anchorScroll, $location) {
var onUserComplete = function(data) {
$scope.user = data;
$github.gettingRepos($scope.user)
.then(onRepos, onError);
};
var onRepos = function(data){
$scope.repos = data;
$anchorScroll( $location.hash("userDetails") );
}
var onError = function(reason) {
$scope.error = "Could not fetch data";
};
var decrementCountdown = function(){
$scope.countdown -= 1;
if($scope.countdown < 1){
$scope.search($scope.username);
}
}
$scope.search = function(username) {
$log.info("Searching for "+ username);
$github.gettingUser(username).then(onUserComplete, onError);
if(countdownIntervalObj){
$interval.cancel(countdownIntervalObj);
$scope.countdown = null;
}
};
var countdownInterval = null;
var startCountdown = function(){
countdownIntervalObj = $interval(decrementCountdown, 1000, $scope.countdown);
}
$scope.username = "angular";
$scope.message = "GitHub Viewer";
$scope.repoSortOrder = "-stargazers_count";
$scope.countdown = 5;
startCountdown();
};
app.controller("MainController",
["$scope", "$http", "$interval", "$log", "$anchorScroll", "$location", "$github", MainController]);
}());
The console keeps saying that the $github.gettingUser is not a function. What am I doing wrong?

Watch out for the order when you inject your dependencies as you are injecting seven but just passing six to the controller in the wrong order. You need to pass $http and put $github at the end.
var MainController = function($scope, $http, $interval, $log, $anchorScroll, $location, $github)
app.controller("MainController", ["$scope", "$http", "$interval", "$log", "$anchorScroll", "$location", "$github", MainController]);

When you inject resources into your controller
app.controller("MainController", ["$scope", "$http", "$interval", "$log", "$anchorScroll", "$location", "$github", MainController]);
order et type must match your controller function declaration
var MainController = function(
$scope, $github, $interval, $log,
$anchorScroll, $location) {
So here what $github contains is the $http module :)
Here is a corrected version of your plunkr
http://plnkr.co/edit/9UyNHDKiXDZAZt8PPEPy?p=preview
However I prefer this syntax, I find it more clear: http://plnkr.co/edit/byhQ7ST8AZlQ6oMYIMeV?p=preview
You should take a look to https://github.com/johnpapa/angular-styleguide
A styleguide were using at work, filled with best practices.
Have fun with angular

because the order of providers are not the same in the array ["scope", "github", etc] with the controller's. your service corresponds to another provider which is minified, even if it is not, it does not matter. you have to pass the injectors in the same order you define in the provider array

Related

Attempting to use controllers with angularjs, error when taking controller code from app.js and putting into a controller file

So I'm trying to play with basic angular code so that I can get a feel for how to create and declare controllers for the project I'm working on. I took some sample code from the angularjs.org controller tutorial and ran into a few issues (turns out I didn't have "./angular.min.js" originally). But I finally got my page to work properly after fixing that.
Then I tried to split the code up so my app.js file could know what controllers to use and I could put the controller code in a separate file (having never made a controller before). After I did this the page gave a console error Uncaught Error: [$injector:modulerr] http://errors.angularjs.org/1.5.8/$injector/modulerr?p0=spicyApp1&p1=Error%…FUsers%2FRDubz%2FDesktop%2Fcontroller%2520test%2Fangular.min.js%3A21%3A179) and I can't figure out what's wrong. I just want to be able to put the controller code into another file.
app.js with controller code:
var myApp = angular.module('spicyApp1', []);
myApp.controller('SpicyController', ['$scope', function($scope) {
$scope.spice = 'very';
$scope.chiliSpicy = function() {
$scope.spice = 'chili';
};
$scope.jalapenoSpicy = function() {
$scope.spice = 'jalapeño';
};
}]);
app.js without controller code:
var spicyApp1 = angular.module('spicyApp1.controllers', []);
testController.js
angular.module('spicyApp1.controllers').controller('SpicyController', ['$scope', '$http', function($scope, $http){
$scope.spice = 'very';
$scope.chiliSpicy = function() {
$scope.spice = 'chili';
};
$scope.jalapenoSpicy = function() {
$scope.spice = 'jalapeño';
};
}]);
blank.html
<title>Example - example-controller-spicy-1-production</title>
<script src="./angular.min.js"></script>
<script src="./app.js"></script>
<script src="./testController.js"></script>
</head>
<body ng-app="spicyApp1">
<div ng-controller="SpicyController">
<button ng-click="chiliSpicy()">Chili</button>
<button ng-click="jalapenoSpicy()">Jalapeño</button>
<p>The food is {{spice}} spicy!</p>
</div>
</body>
</html>
Try this:
File 1:
angular.module('spicyApp1', []);
File 2:
angular.module('spicyApp1').controller('SpicyController', ['$scope', '$http', function($scope, $http){
$scope.spice = 'very';
$scope.chiliSpicy = function() {
$scope.spice = 'chili';
};
$scope.jalapenoSpicy = function() {
$scope.spice = 'jalapeño';
};
}]);
In your html file
call the file 1, file 2 and so on...

Unable to update Child controllers scope valriable

Im new to angular js and im not able to figure out how to change the child controller scope variable from parent controller. Here is the code snippet for that:
var mainApp = angular.module("mainApp", []);
var parentCtrl = function($rootScope, $scope, shareService, $log){
shareService.setDetails($scope.pdetails);
}
var mainCtrl1 = function($rootScope, $scope, shareService, $log){
$scope.msg = "Controller 1";
$scope.details = shareService.details;//shareService.details;
}
var mainCtrl2 = function($rootScope, $scope, shareService){
$scope.msg = "Controller 2";
$scope.details = shareService.details;//shareService.details;
}
parentCtrl.$inject = ["$rootScope", "$scope", "shareService", "$log"];
mainCtrl1.$inject = ["$rootScope", "$scope", "shareService", "$log"];
mainCtrl2.$inject = ["$rootScope", "$scope", "shareService", "$log"];
mainApp.controller("parentController", parentCtrl)
.controller("mainController1", mainCtrl1)
.controller("mainController2", mainCtrl2)
.factory("shareService", function(){
var shareData = {
details : "sadfgs detaisdfadsfasdf..",
setDetails: function(value){
this.details = value;
}
};
return shareData;
});
<html>
<head>
<title>Angular JS Views</title>
<script src='lib/angular.js'></script>
<script src='js/mainApp.js'></script>
<script src='js/studentController.js'></script>
</head>
<body ng-app = 'mainApp' ng-controller='parentController' ng-strict-di>
<div ng-controller='mainController1'>
1. Msg : {{msg}}<br/>
Share Details: {{details}}<br/><br/>
</div>
<div ng-controller='mainController2'>
2. Msg : {{msg}}<br/>
Share Details: {{details}}<br/><br/>
</div>
<input type='text' ng-model='pdetails'/>
</body>
</html>
Here is the Plunker link:
https://plnkr.co/edit/hJypukqMmdHSEZMVnkDO?p=preview
In order to change value of child controller from parent controller you can use $broadcast on $scope.
syntax
$scope.$broadcast(event,data);
$broadcast is used to trigger an event(with data) to the child scope from current scope.
In child controller use $on to receive the event(with data).
Here id the code snippet:
app.controller("parentCtrl",function($scope){
$scope.OnClick=function()
{
$scope.$broadcast("senddownward",$scope.messege);
}
});
app.controller("childCtrl",function($scope){
$scope.$on("senddownward",function(event,data)
{
$scope.messege=data;
});
});
In this example I am broadcasting the event on ng-click,you can use some other custom event.like $watch on $scope.
See this example
https://plnkr.co/edit/efZ9wYS2pukE0v4JsNCC?p=preview
P.S. you can change the name of event from senddownward to whatever you want
You can access the parent's scope properties directly due to the scope inheritance:
<div ng-controller='mainController1'>
Share Details: {{pdetails}}
</div>
Your example does not work because the controllers get executed only once before the view is rendered, pdetails is empty at that moment.
To monitor the changes to pdetails, you can use $watch in the child controller:
$scope.$watch('pdetails', function(newVal) {
$scope.details = newVal;
});

How to change $rootScope value in an ng-click function?

I am trying to change the value of $rootScope.name that I set in the controller by another function in another controller, but when I access the $rootScope.name in another controller the value remains the same as it was set. For example:
app.controller('homectrl', function($scope, $rootScope){
$rootScope.name = "joshua";
})
app.controller('aboutctrl', function($scope, $rootScope){
$scope.send = function(newname)
{
$rootScope.name = newname;
}
})
app.controller('servicectrl', function($scope, $rootScope){
console.log($rootScope.name); // this outputs joshua instead of new name set in send function in about controller
})
You can not push the button which triggers the send() method fast enough to make the servicectrl output the new name. if you really want to see what is in $rootScope.name after you pushed the button you should observe the value or $watch it, eg. by changing the servicectrl like this:
$rootScope.$watch('name', function(newname) {
console.log($rootScope.name);
});
In a case like that I would avoid using $rootScope, but instead I would use a factory service, shared between the two controllers.
Here is a working example:
var app = angular.module('myApp',[]);
app.controller('homectrl', function($scope, NameService){
$scope.name = NameService.name;
$scope.$watch(function(){return NameService.name}, function(newValue, oldValue){
$scope.name = newValue;
});
});
app.controller('aboutctrl', function($scope, NameService){
$scope.name = NameService.name;
$scope.send = function(newname){
NameService.changeName($scope.name);
console.log('New name!', $scope.name);
}
});
app.factory('NameService', function(){
return {
name : 'joshua',
changeName : function(newName){
this.name = newName;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="homectrl">
<h3>Home Controller </h3>
<p>{{name}}</p>
</div>
<hr>
<div ng-controller="aboutctrl">
<h3>About Controller </h3>
<input type="text" ng-model="name" />
<button ng-click="send($scope.name)">Change</button>
</div>
</div>

Reduce dependencies in angular js controllers

How to reduce the dependencies that we give in angular js controllers like
app.controller('sampleController', function($scope, $timeout, $localStorage, $http, $location))
.controller('sample1Controller', function($scope, $timeout, $localStorage, $http, $location))
.controller('sample2Controller', function($scope, $timeout, $localStorage, $http, $location))
.controller('sample3Controller', function($scope, $timeout, $localStorage, $http, $location))
and I'm using the same set of dependencies for multiple controllers.
Can we store all the dependencies in a variable use that to all the controllers.
try to create services for the functionality in the controllers. then your code will be like this, for example,
app.controller('sampleController', function($scope, serviceA, $location))
app.service('serviceA', function($timeout, $localStorage, $http) {
// do something here
});
the more you abstract code out of your controllers, less your injections will be
You can create custom service in angular which returns the dependencies and you can inject that service in your controller and access them. but you will not be able to include $scope in the service as scope is available only for controller.
// angular module implementation
(function(){
'use strict';
angular
.module('app',[]);
})();
// angular controller
(function(){
'use strict';
var controllerId = 'myCtrl';
angular
.module('app')
.controller(controllerId,['common',function(common){
var vm = this;
init();
function init(){
vm.count = 0;
common.interval(function(){
vm.count++;
}, 1000);
}
}]);
})();
// service that returns the depandancies
(function(){
'use strict';
var serviceId = 'common';
angular
.module('app')
.factory(serviceId, ['$timeout','$interval', function($timeout,$interval){
return {
timeout: $timeout,
interval: $interval
};
}]);
})();
<!DOCTYPE html>
<html>
<head>
<script data-require="angularjs#1.5.0" data-semver="1.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="app" ng-controller='myCtrl as vm'>
<h1>My Count is: {{vm.count}}!</h1>
</body>
</html>
To eliminate $scope from your controller go ahead mvvm approach. http://www.johnpapa.net/angularjss-controller-as-and-the-vm-variable/
If you don't want to see all the dependencies statically injected to your controllers and need to do it in a single place, you can use $injector to create an object which will give reference to all your dependencies.
.factory('dependencies', function($injector){
var dependencies;
dependencies.fooDependency = $injector.get('fooDependency');
dependencies.barDependency = $injector.get('barDependency');
return dependencies;
})
Inject this factory to your controller and use it to access your dependencies.

Angular controller is not a function, got undefined

I've been learning Angular recently and in the process of creating a new website referenced I created with a tutorial. Following all the steps I was told, for some reason I am getting this error. And strangely, it is showing up as some sort of url. Here's the "error":
http://errors.angularjs.org/1.4.9/ng/areq?p0=PostCtrl&p1=not%20a%20function%2C%20got%20undefined
Removing all the url gibberish leaves PostCtrl not a function got undefined.
I don't understand why, I've looked all over Stack Overflow and all the common errors such as using global scope, not registering the controller, and other common errors don't seem to be the cause. Here is the controller and jade file.
Controller:
var app = angular.module("app", []);
(function() {
var PostCtrl = function ($scope, $log, $location) {
$scope.posts = [];
$scope.post = function (title, content) {
$scope.title = title;
$scope.content = content;
$log.info("Posting article: " + title + "\n" + content);
$scope.posts.push([title, content]);
};
};
app.controller("PostCtrl", ["$scope", "$log", PostCtrl]);
})();
Jade file: (ng-app="app") is inside the layout file, along with all other scripts involving angular.
extends partials/layout
block scripts
script(src="app/controllers/PostCtrl.js")
block content
.body(ng-controller="PostCtrl")
.row
.col-xs-12
form(class="form-horizontal" ng-submit="post(title, content)")
.form-group
label(for="inputTitle" class="col-sm-2 control-label") Title
.col-sm-10
input(type="text" class="form-control" id="inputTitle" placeholder="Title" ng-model="title")
.form-group
label(for="inputContent" class="col-sm-2 control-label") Content
.col-sm-10
textarea(class="form-control" id="inputContent" placeholder="Content" ng-model="content")
.form-group
.col-sm-offset-2.col-sm-10
button(type="submit" class="btn btn-default") Post
hr
.row
.col-xs-12
#FlorianTopf saved the day. I was not including $location as a dependency. New PostCtrl controller:
(function() {
var PostCtrl = function ($scope, $log, $location) {
$scope.posts = [];
$scope.post = function (title, content) {
$scope.title = title;
$scope.content = content;
$log.info("Posting article: " + title + "\n" + content);
$scope.posts.push([title, content]);
};
};
app.controller("PostCtrl", ["$scope", "$log", "$location", PostCtrl]);
})();
You are initiating twice the controller. Remove it from the html .body(ng-controller="PostCtrl")

Categories

Resources