define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
myCtrl.$inject=['$scope','$http'];
return myCtrl;
});
We have separate file for each controller and lazy loaded when required.. They have corresponding entry in application.js.
Now the problem is :
I need 2-3 child controllers all linked to a parent controller.. and all are there in a single file.. so that they can be loaded..
Tried :
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
function myCtrl1($scope,$http){};
myCtrl.$inject=['$scope','$http'];
return myCtrl;
});
but, dosen't seems to be working.
UPDATE ----
Parent --
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
myCtrl.$inject=['$scope','$http'];
return myCtrl;
});
With another controller :
define([], function() {
function myCtrl($scope,$http)
{
$scope.test = "Course Man";
}
return myCtrl;
});
function myCtrl1($scope,$http){
};
This is working .. not sure they have parent child relationship or not... confused !
You can go the other way.
it is possible to extend a controller or make a single controller a mixin of multiple controllers.
module.controller('CtrlChild', ['$scope', '$controller', function ($scope, $controller) {
// Initialize the super class and extend it.
angular.extend(this, $controller('CtrlParent', {$scope: $scope}));
… Additional extensions to create a mixin.
}]);
Related
I have a constant which is injected into a controller and I need to write a test which changes this constant and expects different results. I can use $provide to mock the constant but according to articles I've found online, I need to do it in the module declaration, which I believe is like this:
beforeEach(module("someModule"));
beforeEach(function () {
module(function ($provide) {
$provide.constant('someConstant', false);
});
});
I later load the controller like this:
function createController() {
view = $controller(
"someController",
{
$scope: $injector.get("$rootScope").$new()
});
}
Where $controller, $scope and $injector are all injected in my main beforeEach
This does provide the constant and it does change if I change the value in my beforeEach. But only for the entire test suite. I want to change this constant in a describe or an it but I'm not sure how. If I move the $provide down to a describe or it, I get the error:
Error: Injector already created, can not register a module!
I could just create a new file and that is probably what I'm going to do but is there a way I can $provide a dynamic value?
Lets consider such a code
controller
angular.module('someModule', [])
.controller('someController', function($scope, someConstant) {
$scope.someProvidedValue = someConstant;
})
and test for it
controller spec
describe('module', function () {
beforeEach(module("someModule"));
var createController;
beforeEach(inject(function (_$controller_, _$injector_) {
scope = _$injector_.get("$rootScope").$new()
createController = function createController(scope, obj) {
_$controller_("someController", {
$scope: scope,
someConstant: obj.someConstant
});
}
}))
it('someConstant', function () {
expect(scope.someProvidedValue).toBe(undefined)
createController(scope, {
someConstant: false
})
expect(scope.someProvidedValue).toBe(false)
})
it('someConstant', function () {
expect(scope.someProvidedValue).toBe(undefined)
createController(scope, {
someConstant: true
})
expect(scope.someProvidedValue).toBe(true)
})
})
In the mean time I'm looking for looks nicer solution.
How can I run the working code in createAdmobBanner function in another controller?
angular.module('starter', ['ionic', 'starter.controllers'])
.run(function ($ionicPlatform) {
$ionicPlatform.ready(function () {
var admobid = {};
if (/(android)/i.test(navigator.userAgent)) {
admobid = {
banner: 'ca-app-pub-3815248714018431/123456789'
};
}
function createAdmobBanner() {
AdMob.createBanner({
adId: admobid.banner
adSize: 'SMART_BANNER',
position: 8
});
}
createAdmobBanner();
});
})
I got createAdmobBanner is not defined if I simply do createAdmobBanner() in my controllers. I tried $rootScope but the plugin doesn't seem work with that.
You need to add it into a service or attached in on $rootScope,
$rootScope solution - faster to implement but "dirty"
.run(function($ionicPlatform,$rootScope) { //add $rootScope dependency injection
$rootScope.createAdmobBanner = function(){
AdMob.createBanner( { adId:admobid.banner
adSize: 'SMART_BANNER',
position:8
});
}
$rootScope.createAdmobBanner()
into your controllers, add the dependency $rootScope and call your function $rootScope.createAdmobBanner
Service Solution - cleaner & reusable
Create a new service that has your function
Inject your service into run
call your service function into run
inject your service into controllers
call your service function into controllers
I just found this link here. Give it a try. The important code looks like this:
var admobApp = angular.module('myapp', ['ionic'])
.run(function($ionicPlatform, $ionicPopup) {
$ionicPlatform.ready(function() {
if(window.plugins && window.plugins.AdMob) {
var admob_key = device.platform == "Android" ? "ANDROID_PUBLISHER_KEY" : "IOS_PUBLISHER_KEY";
var admob = window.plugins.AdMob;
admob.createBannerView(
{
'publisherId': admob_key,
'adSize': admob.AD_SIZE.BANNER,
'bannerAtTop': false
},
function() {
admob.requestAd(
{ 'isTesting': false },
function() {
admob.showAd(true);
},
function() { console.log('failed to request ad'); }
);
},
function() { console.log('failed to create banner view'); }
);
}
});
});
The admob stuff is within $ionicPlatform.ready(function() { and is defined like this var admob = window.plugins.AdMob;
Does that help?
Try to define external angular service/factory and provide this service to any controller you need using dependency injection.
This is a good practice to share common logic or data in this way.
EDIT:
angular.module('starter', ['ionic', 'starter.controllers']);
angular.module('starter').factory('bannerFactory',function(){
return {
createAdmobBanner: function(){
window.plugins.AdMob.createBanner({ adId:admobid.banner
adSize: 'SMART_BANNER',
position:8
});
}
}
});
angular.module('starter').controller('anyController',['bannerFactory', function(bannerFactory){
bannerFactory.createAdmobBanner();
}]);
angular.module('starter').run(function ($ionicPlatform,bannerFactory) {
$ionicPlatform.ready(function () {
bannerFactory.createAdmobBanner();
});
});
I have a function that two controllers will be using, and instead of both of them having the same source code for the same function, I want it in one place and just inject the controller parameters (or perhaps the controller itself this). These three may exist in three separate files/modules.
.controller('FirstCtrl', function() {
this.search = function(this) {
find(this);
};
});
.controller('SecondCtrl', function() {
this.seek = function(this) {
find(this);
};
});
var find = function(controller) {
.
.
.
};
Is this the best way? How about if I have services in my controllers like $http or $scope, and the function find would depend on these services? How do I inject these angular specific services to a plain JavaScript function not defined in an AngularJS module?
There are a few ways to do it; one may be:
.factory("findMixin", function() {
return {
find: function() {
// your implementation; `this` will be the controller
}
};
})
.controller("SomeCtrl", ["$scope", "findMixin", function($scope, findMixin) {
angular.extend(this, findMixin);
// here `this`, i.e. the controller, has received the methods from the mixin
...
})
The same principle (angular.extend) can be applied to the $scope, if you want find to be mixed into the scope.
You can add a service:
.factory('find', [ function() {
return function(controller, scope) {
// ...
};
}]);
And inject it into the controllers:
.controller('FirstCtrl', ['find', function(find) {
this.search = function(this) {
find(this);
};
}]);
.controller('SecondCtrl', ['find', function(find) {
this.seek = function(this) {
find(this);
};
}]);
I testing my angular-application with jasmine(http://jasmine.github.io/2.0/) and getting next error:
Unknown provider: $scopeProvider <- $scope
I know, that it's incorrect to build dependency with scope in filters, services, factories, etc., but I use $scope in controller!
Why am i getting this error? controller looks like
testModule.controller('TestCont', ['$filter', '$scope', function($filter, $scope){
var doPrivateShit = function(){
console.log(10);
};
this.lol = function(){
doPrivateShit();
};
this.add = function(a, b){
return a+b;
};
this.upper = function(a){
return $filter('uppercase')(a);
}
$scope.a = this.add(1,2);
$scope.test = 10;
$scope.search = {
};
}]);
and my test's code:
'use strict';
describe('testModule module', function(){
beforeEach(function(){
module('testModule');
});
it('should uppercase correctly', inject(function($controller){
var testCont = $controller('TestCont');
expect(testCont.upper('lol')).toEqual('LOL');
expect(testCont.upper('jumpEr')).toEqual('JUMPER');
expect(testCont.upper('123azaza')).toEqual('123AZAZA');
expect(testCont.upper('111')).toEqual('111');
}));
});
You need to manually pass in a $scope to your controller:
describe('testModule module', function() {
beforeEach(module('testModule'));
describe('test controller', function() {
var scope, testCont;
beforeEach(inject(function($rootScope, $controller) {
scope = $rootScope.$new();
testCont = $controller('TestCont', {$scope: scope});
}));
it('should uppercase correctly', function() {
expect(testCont.upper('lol')).toEqual('LOL');
expect(testCont.upper('jumpEr')).toEqual('JUMPER');
...
});
});
});
Normally, a $scope will be available as an injectable param only when the controller is attached to the DOM.
You need to associate somehow the controller to the DOM (I'm mot familiar with jasmine at all).
I am following a video tutorial from egghead (link bellow) which suggest this approach:
describe("hello world", function () {
var appCtrl;
beforeEach(module("app"))
beforeEach(inject(function ($controller) {
appCtrl = $controller("AppCtrl");
}))
describe("AppCtrl", function () {
it("should have a message of hello", function () {
expect(appCtrl.message).toBe("Hello")
})
})
})
Controller:
var app = angular.module("app", []);
app.controller("AppCtrl", function () {
this.message = "Hello";
});
I am posting it because in the answer selected we are creating a new scope. This means we cannot test the controller's scope vars, no?
link to video tutorial (1min) :
https://egghead.io/lessons/angularjs-testing-a-controller
In my angular app, I have around 30 controllers and 1 test file for each of them. Those test files always begin with something like this boilerplate:
'use strict';
describe('Controller: SomeController', function ()
{
var controller;
var scope;
beforeEach(function ()
{
module('SomeControllerModule');
inject(function ($controller, $rootScope)
{
scope = $rootScope.$new();
controller = $controller('SomeController',
{
$scope: scope
});
});
});
it('should prepare controller scope', function ()
{
console.log('scope', scope);
});
});
Is there a way to somehow make it shorter, so that I don't have to repeat it in each of my files?
There's ng-describe which looks like it could be very useful (I haven't used it personally yet). With that your code becomes something like:
ngDescribe({
modules: 'SomeControllerModule',
controllers: 'controller',
tests: function (deps) {
it('should prepare controller scope', function () {
console.log('scope', deps.controller);
});
}
});
Unfortunately they don't support jasmine at the moment which probably rules it out for a lot of people.
Yes, I have a cleaner way of doing it:
describe('HomeController', function() {
var $scope;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
$controller('HomeController', { $scope: $scope });
}));
it('true should be truthy', function() {
expect(true).toBeTruthy();
});
});
Also check out this AngularJS Scaffolding that comes with all nuts and bolts you might need in your AngularJS project.