Injecting $scope in manually instantiated controller - angularjs

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);

Related

Angular 1.6 + Components: passing constants between modules

How do I pass the value of a constant from one module to another? Thank you in advance. Here's the Plunker demo. The constant "config" is defined in app.module.js, and I want to be able to pass it to the child.module.js for defining constant "childConfig". Right now the console is saying "config is not defined".
Thank you very much in advance.
// app.module.js
(function(){
"use strict";
var myApp = angular
.module("myApp", ["child"]);
myApp.constant("config", {
rootURL: "components/"
, version: "myApp1.0.0"
})
})();
// app.component.js
(function(){
"use strict";
// Define controller
function mainController(){
this.$onInit = function() {
var mainVM = this;
mainVM.parent = {
"lastName": "Smith"
, "firstName": "Jordan"
};
};
}
// Define component
var mainComponent = {
controller: mainController
, controllerAs: "mainVM"
};
// Register controller and component
angular.module("myApp")
.controller("mainController", mainController)
.component("mainComponent", mainComponent);
})();
//components/child.module.js
(function(){
"use strict";
var child = angular.module("child", []);
child.constant("childConfig", config);
})();
//components/child.component.js
(function(){
"use strict";
// Define controller
function childController(config) {
this.$onInit = function() {
var vm = this;
vm.getTemplateUrl = function(){
return config.rootURL + 'child.html';
}
vm.rootURL = config.rootURL;
vm.version = config.version;
vm.child = {
"firstName": "Jack"
}
};
// end of $onInit()
}
// Define component
var child = {
//templateUrl: vm.getTemplateUrl + "child.html"
//templateUrl: "components/child.html"
template: "<ng-include src='vm.getTemplateUrl()'/>"
, controller: childController
, controllerAs: "vm"
, bindings: {
parent: "<"
}
};
// Register controller and component
angular.module("child")
.controller("childController", childController)
.component("child", child);
})();
<!DOCTYPE html>
<html>
<head>
<script src="//code.angularjs.org/snapshot/angular.js"></script>
<link rel="stylesheet" href="style.css">
<script src="app.module.js"></script>
<script src="app.component.js"></script>
<script src="components/child.module.js"></script>
<script src="components/child.component.js"></script>
</head>
<body ng-app="myApp" ng-controller="mainController as mainVM">
Parent: {{mainVM.parent.firstName}} {{mainVM.parent.lastName}}<br>
<child parent="mainVM.parent"></child>
</body>
</html>
I think you might've misunderstood the constant factory's capabilities. It's not supposed to be mutable neither variable, therefore it won't be able to use another external provider to compose it's value, it'll only serve a plain value that is pure.
On the other hand, if you don't mind, a factory can be use to achieve the result your are looking for. Basically you can create a factory that fabricate a string for you based on the injected config provider.
For example:
child.factory("childConfigURL", ['config', function(config) {
return config.rootURL + "/component/";
}]);
The only problem with this approach is that it can't be injected into a module configuration function, because it's not pure anymore and needs to be bootstrapped to resolve it's dependencies.
Note on constants:
constant(name, value);
Register a constant service with the $injector, such as a string, a
number, an array, an object or a function. Like the value, it is not
possible to inject other services into a constant.
But unlike value, a constant can be injected into a module
configuration function (see angular.Module) and it cannot be
overridden by an AngularJS decorator.
Ref.: $provide.constant

Executing any one function in controller executes the entire controller function

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)

Access AngularJs directive variable inside controller

I'm little bit new to Angularjs. What I want is access "$scope.myVar" variable inside 'myController' controller. It would be great help if you can provide a solution.
angular.module('myDirective', [])
.controller('myController', ['$scope', function ($scope) {
}])
.directive('myDirective', function () {
return {
scope: {
myVar: '='
},
controller: function ($scope) {
$scope.myVar = 'xyz';
alert($scope.myVar);
}
};
});
<html lang="en-US">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
<script type="text/javascript" src="newjavascript.js"></script>
<body ng-app="myDirective">
<div ng-controller="myController">
<my-directive></my-directive>>
</div>
</body>
</html>
You just create a myVar variable in your controller and pass it to the directive using my-var attribute.
In your myController, Define myVar as
$scope.myVar= "Hello"
I your DOM, pass it to the directive as
<my-directive my-var="myVar"></my-directive>
Since you are using two way binding, any changes made to myVar by the directive are available in your controller.
You can put a watch on myVar to track the changes.
angular.module('myDirective', [])
.controller('myController', ['$scope', function ($scope) {
$scope.show = function() {
alert($scope.myVar);
};
}])
.directive('myDirective', function () {
return {
scope: {
myVar: '='
},
controller: function ($scope) {
$scope.myVar = 'xyz';
alert($scope.myVar);
$scope.$parent.myVar = $scope.myVar; // here you can access the controller scope by using $parent
}
};
});

How to access $rootScope value defined in one module into another module

I need to understand "How can I access the value of $rootScope value defined in one module into other module?"
Below is my code :
Index.html
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"> </script>
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
<script src="test.js"></script>
</head>
<div ng-controller="serviceDIController">
Service Values is : <b> {{sdiParam}} </b> <br/>
Factory Values is : <b> {{fdiParam}} </b> <br/>
Root Scope Values is : <b> {{rootParam}} </b>
</div>
</html>
script.js
var app = angular.module("myapp", ['testModule']);
app.controller('serviceDIController',['$scope','$rootScope',
'testService','testFactory',function($scope, $rootScope,testService, testFactory)
{
$scope.sdiParam = testService.param;
$scope.fdiParam = testFactory.fparam;
// $scope.rootParam = $rootScope.root; // How to access "$rootScope.root" value defined in test.js in current module inside a controller?
}
]);
test.js
var testapp = angular.module("testModule", []);
testapp.service('testService', function() {
this.param = "Service Param1 DI";
});
testapp.factory('testFactory', function() {
var fact = {};
fact.fparam = "Fact Param1 DI";
return fact;
});
testapp.controller('testCtrl', ['$scope',
function($rootScope) {
$rootScope.root = "Root Scope Param1";
}
]);
Live demo : http://plnkr.co/edit/X0aamCi9ULcaB63VpVs6?p=preview
Checked below example but did not work:
AngularJS $rootScope variable exists, but not accessible
Explicitly inject '$scope', not '$rootScope' in testCtrl, so a new scope is created just for that controller and passed as the first argument regardless of the name used for that argument.
Incorrect:
testapp.controller('testCtrl', ['$scope',
function($rootScope) {
$rootScope.root = "Root Scope Param1";
}
]);
Correct:
testapp.controller('testCtrl', ['$rootScope',
function($rootScope) {
$rootScope.root = "Root Scope Param1";
}
]);
Here is your updated working Plunkr
Basically I prefer to use $scope.$root to prevent the injection of the $rootScope.
You have also set the testCtlr on the <head> tag, don't know if it was on purpose or not.

Inject $log into every controller and service

Is there any way to inject AngularJS's $log into every service and controller? It just feels a little redundant specifying it for every one.
Another way you could do it is to add a method to the rootScope, and then access it through $scope.$root in your controllers, thus avoiding another injection. I don't know if it is as bad as globals.
testapp.js
(function(){
'use strict';
angular.module('app', [])
.run(function($rootScope, $log) {
$rootScope.log = function(msg){
$log.info(msg);
}
})
.controller('LogCtrl', ['$scope', function LogCtrl($scope) {
$scope.logThis = function(msg){
$scope.$root.log(msg);
};
}]);
})();
test.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.5/angular.min.js"></script>
<script src="testapp.js"></script>
</head>
<body ng-app="app">
<div ng-controller="LogCtrl">
<p>Enter text and press the log button.</p>
Message:
<input type="text" ng-model="message"/>
<button ng-click="logThis(message)">log</button>
</div>
</body>
</html>
Injecting it seems impossible to me without defining it in the function parameters. But you can make it available:
var $log;
app.run(['$log',function(logService) {
$log = logService;
}]);
app.controller('MainCtrl', function($scope, myService) {
$log.warn('Controlling');
});
app.service('myService', function() {
$log.warn('Ha!');
return {};
});
http://plnkr.co/edit/Zwnay7dcMairPGT0btmC?p=preview
Another way would be to set it as a global variable (window.$log), but I wouldn't do that.
Here's a solution for those who think that using the $rootscope requires too much fuss: add the $log to the angular object.
angular.module('myModule')
.run(['$log', function($log) {
angular.log = $log;
}]);
Then when you create your controllers, no $log is required.
angular.module('myModule')
.controller('MyController', MyController);
MyController.$inject = []; // <-- see, no $log required!
function MyController() {
angular.log.info("Hello world");
}
You could even take it a step further and add angular.info = $log.info if you wish to shorten it a bit more.

Resources