Executing any one function in controller executes the entire controller function - angularjs

I am a newbie to Angular. I am using a controller and I am initially loading the page using the init function. I have an element on html that activates the incrementPage function. When I activate the function, the init function executes again and I get the same result instead of the next page. How do I solve this issue?
myApp.controller('MusicController', ['$scope', '$resource', function($scope, $resource){
var vm = $scope;
init();
function init(){
vm.pgno = 1;
music = $resource('http://104.197.128.152:8000/v1/tracks/', {"page": vm.pgno}).get({"page": vm.pgno});
vm.tracks = music;
}
vm.incrementPage = function(){
vm.pgno = vm.pgno + 1;
console.log(vm.pgno);
music = $resource('http://104.197.128.152:8000/v1/tracks/', {"page": vm.pgno}).get({"page": vm.pgno});
vm.tracks = music;
console.log(vm.pgno);
};
}]);
Also, I am using ngRoute and two different controllers.
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: "views/listMusic.html",
controller:'MusicController',
})
.when('/genres', {
templateUrl: "views/genres.html",
controller:'MusicGenreController',
})
}]);
myApp.controller('MusicGenreController', ['$scope', 'tracklister', function($scope, tracklister) {
var vm = $scope;
var gpgno = 1;
var incrementGPage = function(){
gpgno += 1;
gen = tracklister.genreList.get({'page': gpgno});
vm.genres = gen;
}
(function(){
gen = tracklister.genreList.get({'page': gpgno});
vm.genres = gen;
})();
}]);
When I click the Genres instead of taking me to the genres view it is getting me back to the same listMusic view inside the first MusicController controller. What to do here?
<!DOCTYPE html>
<html ng-app='ListTracksApp'>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-route.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-resource.js"></script>
<script src="appl.js"></script>
<meta charset="utf-8">
<link rel="stylesheet" href="styles.css">
<title>Music</title>
</head>
<body>
<h1>Music Tracks</h1>
Genres
<div ng-view>
</div>
</body>
</html>

In your controller, change it to this:
myApp.controller('MusicController', ['$scope', '$resource', function($scope, $resource) {
var vm = $scope;
function $onInit() {
// the rest of your code
That'll get called once, when the controller initializes. Also I think your link is incorrect, as georgeawg pointed out but I'm not too familiar with ngRoute, I've used uiRouter for years, it should be #!/genres
One other thing although this isn't related to your question, in your init() function you have a call to your db, which I suspect is going to be async, you should probably use resolve in the ngRoute and inject it into the controller so it's initially resolved:
resolve: {
data: function ($resource) {
var url = 'http://104.197.128.152:8000/v1/tracks/';
return $resource(url, {"page": 1}).get({"page": 1}).$promise;
}
}
Then your controller can use 'data' as the result of the call:
myApp.controller('MusicController', ['$scope', '$resource', 'data', function($scope, $resource, data)

Related

How to bind the data to the template using rxjs and angular 1.6

I am tring to get the data using rxjs from promise method, and once it is success am subscribing it and passing it to my scope.
I can see the response object attached to my scope, but in UI, it is not getting mapped.
This is what I tried.
index html:
<html>
<head></head>
<body>
<my-component></my-component>
<script src="rx.lite.js"></script>
<script src="angular.min.js"></script>
<script src="rx.angular.js"></script>
<script src="app.js"></script>
<script src="service.js"></script>
<script src="component.js"></script>
</body>
</html>
app.js:
(function() {
'use strict';
angular.module('app', ['rx']);
})();
service.js:
;(function (undefined) {
'use strict';
angular.module('app').factory('myService', ['$http', '$q', 'rx',function($http, $q, rx) {
function httpReq(configObj){
var deferred = $http(configObj);
return rx.Observable
.fromPromise(deferred)
.map(function(response){ return response.data; });
}
return {
httpReq : httpReq
}
}]);
}.call(this));
component.js:
;(function (undefined) {
'use strict';
angular.module('app').component('myComponent', {
templateUrl: "myComponent.tpl.html",
controller: ['myService', 'rx', Ctrl]
});
function Ctrl(myService, rx) {
var $ctrl = this;
myService.httpReq({ url: ' http://localhost:3000/posts/1', method: 'GET'})
.subscribe(function(val) {
$ctrl.list = val;
}.bind(this));
}
}.call(this));
myComponent.tpl.html:
<div ng-if="$ctrl.list">{{$ctrl.list}}</div>
You have to call $apply() because $scope is modified outside of the $digest cycle (not sure though).

access angular $cookies from non-angular code

I have a test module that set cookie isTesting to be true
app.controller('test_page',
['$scope', '$window', 'userService', 'flash', '$cookies',
function($scope, $window, userService, flash, $cookies) {
helper.initCommonScope($scope, $window, userService, flash)
$scope.refreshShownHidden = function() {
$scope.showTurnOffTest = $cookies.get('isTesting')
$scope.showTurnOnTest = ! $cookies.get('isTesting')
}
$scope.turnOnTest = function() {
$cookies.put('isTesting', true)
$scope.refreshShownHidden()
helper.turnOnTest()
}
$scope.turnOffTest = function() {
$cookies.put('isTesting', false)
$scope.refreshShownHidden()
helper.turnOffTest()
}
$scope.refreshShownHidden()
}])
And in helper.js, I have
exports.havePermission = function(access, resource, userService, entity) {
//Note: In debugging, we can grant client helper all access, and test robustness of server
if (angular.$cookies.isTesting)
return true
return permission.havePermission(access, resource, userService.isAuthenticated(), entity, userService.user)
}
But $cookies is not available since helper.js is not part of any angular module, thus no DI is available. How can I access the isTesting value?
I have tried using window.isTesting instead but it's not persisted when I refresh the page or go to other pages. So cookie is a better choice
You can use Angular's injector to access Angular modules and services outside of your Angular application.
AngularJS Code
angular.module('app', ['ngCookies']).
controller('ctrl', ['$cookies', function($cookies) {
$cookies.put('isTesting', true);
}]);
Non-Angular Code
helper = {
getCookies: function() {
// Create new injector for ngCookies module
var $injector = angular.injector(['ngCookies']);
// Inject $cookies to some function and invoke it
$injector.invoke(['$cookies', function($cookies) {
alert($cookies.get('isTesting'));
}]);
}
}
HTML
<html ng-app='app'>
<head>
<script data-require="angular.js#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular.js"></script>
<script data-require="angular-cookies.js#1.4.3" data-semver="1.4.3" src="https://code.angularjs.org/1.4.3/angular-cookies.js"></script>
<script src="script.js"></script>
<script src="helper.js"></script>
</head>
<body ng-controller="ctrl">
<button onclick="helper.getCookies()">Click Me</button>
</body>
</html>
Plunker: http://plnkr.co/edit/jur9A6d69ViiJqiFgopD?p=preview
var cookies = angular.injector(['ngCookies']).get('$cookies');
It creates a new instance of $cookies service, so if you're using it across the app, it is better to export it to global.
It's not the AngularJS purpose. You should try to keep all your stuff in your AngularJS code. However you can set any global variable in AngularJS:
app.controller('test_page',
['$scope', '$window', 'userService', 'flash', '$cookies',
function($scope, $window, userService, flash, $cookies) {
angular.$cookies = $cookies;
// or window.$cookies = $cookies;
helper.initCommonScope($scope, $window, userService, flash)
Then you can access $cookies anytime anywhere. It make certain things easier but less safe (any other script can access it too, it can create conflict, etc.)

angular services declaration

I am working on a simple angular SPA. I got the services working, and then wanted to extract them to a separate file. I've done something wrong with syntax and dependencies, and I'm wondering if anyone can help me spot it. I know I am confusing some things, and there might be a couple of additional bugs. I'm open to hearing some ideas about where I'm going wrong here. Please advise.
current angular error
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- FetchProduct
http://errors.angularjs.org/1.3.14/$injector/unpr?p0=<article class="demo ng-scope" ng-view="product-list">copeProvider%20%3C-%20%24scope%20%3C-%20FetchProduct
Error: $injector:unpr Unknown Provider Unknown provider: Description
This error results from the $injector being unable to resolve a
required dependency. To fix this, make sure the dependency is defined
and spelled correctly.
markup
<!doctype html>
<html ng-app="demoApp">
<head>
<title>Kat Chilton | Escalada | Angular Skills Demo App</title>
<link rel="stylesheet" href="demo.css" type="text/css">
<link href='http://fonts.googleapis.com/css?family=Oswald:400,300,700' rel='stylesheet' type='text/css'>
</head>
<body ng-controller="ProductListCtrl">
<article class="demo" ng-view="product-list">
</article>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-route.js"></script>
<script src="controllers.js"></script>
<script src="app.js"></script>
<script src="services.js"></script>
</body>
</html>
app.js
'use strict';
var demoApp = angular.module('demoApp', [
'ngRoute',
'DemoControllers',
'DemoServices'
]);
demoApp.config(['$routeProvider',
function($routeProvider){
$routeProvider.when('/products/', {
templateUrl: 'product-list.html',
controller: 'ProductListCtrl'
})
.when('/products/:product_id', {
templateUrl: 'product-detail.html',
controller: 'ProductDetailCtrl'
})
.otherwise({
redirectTo: '/products/'
});
}]);
controllers.js
var DemoControllers = angular.module('DemoControllers', []);
DemoControllers.controller('ProductListCtrl', ['$scope', '$http', 'FetchList',
function ($scope, $http, FetchList) {
$scope.products = FetchList;
}]);
DemoControllers.controller('ProductDetailCtrl', ['$scope', '$http', '$routeParams', 'FetchProduct',
function ($scope, $http, $routeParams, FetchProduct) {
$scope.selection = FetchProduct;
}]);
services.js:
var DemoServices = angular.module('DemoServices', []);
DemoServices.factory('FetchList', ['$http', function ($http) {
var list;
var FetchList = function ($http) {
$http.get('https://s3-eu-west-1.amazonaws.com/developer-application-test/cart/list')
.success(function (data, status, headers, config) {
// console.log('data :: ', data, 'status :: ', status, 'headers :: ', headers, 'config :: ', config);
list = data.products;
});
};
return {products: new FetchList($http)};
}]);
DemoServices.factory('FetchProduct', ['$http', '$scope', function ($http, $scope) {
var product;
var FetchProduct = function ($http, $scope) {
$http.get('https://s3-eu-west-1.amazonaws.com/developer-application-test/cart/' + $scope.product_id + '/detail')
.success(function (data, status, headers, config) {
console.log('data :: ', data, 'status :: ', status, 'headers :: ', headers, 'config :: ', config);
product = data;
});
};
return {product: new FetchProduct($http, $scope)};
}]);
var demoApp = angular.module('demoApp', [
'ngRoute',
'DemoControllers'
])
.service('DemoServices');
This is incorrect. The service() method is used to add a new service to the module. It takes a service name as argument, and a constructor function for the service. What 'DemoServices' is here is the name of a module. And you want to add this module as a dependency of demoApp. So you just need to add it to the dependencies array, already containing ngRoute and DemoControllers:
var demoApp = angular.module('demoApp', [
'ngRoute',
'DemoControllers',
'DemoServices'
]);
EDIT:
OK, now that I have a clear error message, the error is more obvious. Your service FetchProduct depends on $scope. But $scope is not a service, and thus can't be injected in a service. Controllers have a $scope, and angular creates one every time the controller is instantiated, but services are singletons, and don't have any scope: they're not associated to any DOM element.
Your service should look like this:
DemoServices.factory('productFetcher', ['$http', function($http) {
var getProduct = function(productId) {
return $http.get('https://s3-eu-west-1.amazonaws.com/developer-application-test/cart/' + productId + '/detail').then(function(response) {
return response.data;
});
});
return {
getProduct: getProduct;
};
}]);
and it should be used like this, in a controller, to get the product 42:
productFetcher.getProduct(42).then(function(product) {
$scope.product = product;
});

Injecting $scope in manually instantiated controller

I'm trying to inject $scope in a controller created using the $controller service.
I'm getting the following error:
Unknown provider: $scopeProvider <- $scope <- TestController
It's important to mention that the creation is happening inside a directive's link function.
I've written a simple example to show the error.
app.js:
(function() {
angular.module('app', [])
.controller('TestController', [
'$scope',
function($scope) {
// I want to be able to use 'message' from the directive's template
// as if the controller was loaded directly using ng-controller
$scope.message = 'Hello World!';
}
])
.directive('directive', [
'$controller',
function($controller) {
return {
restrict: 'A',
template: '<div>{{ message }}</div>',
link: function(scope) {
// In the actual app the controller is dynamically selected
// I'm registering a $watch here that provides me the
// name of the controller
scope.myController = $controller('TestController');
}
};
}
]);
})();
index.html:
<!DOCTYPE html>
<html ng-app="app">
<head>
<title>Testing injection</title>
</head>
<body>
<div directive></div>
<script src="angular.js"></script>
<script src="app.js"></script>
</body>
</html>
Question: Why is this happening? Can you explain me the logic behind this behaviour? Any workaround?
Thank you.
Following Digix's answer, I was able to make it work:
var locals = {};
locals.$scope = scope;
$controller('TestController', locals);
My assumption is that in this way, instead of creating a new scope, the controller shares the one of the directive.
var locals = {};
locals.$scope = scope.$new();
$controller('testCtrl', locals);

Simple Angular $routeProvider resolve test. What is wrong with this code?

I have created a simple Angular JS $routeProvider resolve test application. It gives the following error:
Error: Unknown provider: dataProvider <- data
I would appreciate it if someone could identify where I have gone wrong.
index.html
<!DOCTYPE html>
<html ng-app="ResolveTest">
<head>
<title>Resolve Test</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.js"> </script>
<script src="ResolveTest.js"></script>
</head>
<body ng-controller="ResolveCtrl">
<div ng-view></div>
</body>
</html>
ResolveTest.js
var rt = angular.module("ResolveTest",[]);
rt.config(["$routeProvider",function($routeProvider)
{
$routeProvider.when("/",{
templateUrl: "rt.html",
controller: "ResolveCtrl",
resolve: {
data: ["$q","$timeout",function($q,$timeout)
{
var deferred = $q.defer();
$timeout(function()
{
deferred.resolve("my data value");
},2000);
return deferred.promise;
}]
}
});
}]);
rt.controller("ResolveCtrl",["$scope","data",function($scope,data)
{
console.log("data : " + data);
$scope.data = data;
}]);
rt.html
<span>{{data}}</span>
The problem is that you have ng-controller="ResolveCtrl" on your body tag in index.html when also in your $routeProvider you specify the same controller for rt.html. Take out the controller definition from your body tag and just let the $routeProvider take care of it. It works great after that.
According to the angularjs documentation for $routeprovider the resolve object is a map from key (dependency name) to factory function or name of an existing service. Try this instead:
var myFactory = function($q, $timeout) { ... };
myFactory.$inject = ['$q', '$timeout'];
$routeProvider.when("/",{
templateUrl: "rt.html",
controller: "ResolveCtrl",
resolve: {
data: myFactory
}
});
By adding data to the definition of the controller your telling angular that you expect to inject a service or factory here yet you don't have a data service or factory thus the error. To use the data variable you have all you need from the $scope.data line. So to fix this you need to remove the data injection from your controller call.
var rt = angular.module("ResolveTest",[]);
rt.config(["$routeProvider",function($routeProvider)
{
$routeProvider.when("/",{
templateUrl: "rt.html",
controller: "ResolveCtrl",
resolve: {
data: ["$q","$timeout",function($q,$timeout)
{
var deferred = $q.defer();
$timeout(function()
{
deferred.resolve("my data value");
},2000);
return deferred.promise;
}]
}
});
}]);
rt.controller("ResolveCtrl",["$scope", function($scope)
{
$scope.data = "";
}]);
If you want to have a data provider add a factory something like
rt.factory('data', ['$http', function($http){
return {
// Functions to get data here
}
}]);
Then in your controller call the appropriate function from this factory.
Also as the others have pointed out you don't need the controller both in your route and in an ng-controller (this will nest your controller in your controller if you inspect the scopes).
If you must use resolve you still need a factory as resolve will just point to the proper factory which needs to be declared separately.

Resources