I'm wrapping a global variable in a factory so that it can be injectable. It looks something like this:
angular.module('Analytics').factory('woopra', [
'$window',
function ($window) {
return $window.woopra;
}
]);
Because of how this tool works, at some point in the future after running initialisation, the variable woopra on the window gets replaced with a new value.
I need the injectable factory woopra to reference this new woopra variable that is on the window. What's a nice way to do this? At the moment I'm just referencing it as $window.woopra so I can mock $window.
factory
angular.module('Analytics').factory('woopra', [function () {
return {
'injectableVar': 'test Value'
}
}]);
Ctrl2
angular.module('Analytics').controller('Ctrl1', ['$scope', 'woopra', function ($scope, woopra) {
woopra.injectableVar = 'Value from Ctrl1';
}]);
Ctrl2
angular.module('Analytics').controller('Ctrl2', ['$scope', 'woopra', function ($scope, woopra) {
$scope.Ctrl2Var = woopra.injectableVar;
}]);
This sample show enjectableVar share between 2 controller
Why use factories? So much typing, I use the angular value and constant properties. Use the constant one if you don't want it editable. And then just inject it into the controller you want to you:
app.constant("woopra", "test 1234");
// OR the value property, can also be an array
app.value("proptypes", [
{
Description: "Duplex"
}, {
Description: "Simplex"
}, {
Description: "Duet"
}, {
Description: "Cluster"
}, {
Description: "House"
}, {
Description: "Townhouse"
}]);
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.
Question: how do I access a dynamically generated data in scope B, when I go from scope A and generate this data in scope A, using angular's ui-controller. Data is not available when the scope is initialized.
Note: I am fine with showing request data in the URL. I'm looking for the simplest way for new state to read data it needs and pass it to server and properly generate its contents.
When the page loads, it fetches data from server and populates scope "tests" with new data. This new data is shown on the page. I create links to scope "test" with this data. Links look like this:
<a ui-sref="test({id:test._id})">{{test.name}}</a>
On a rendered page it looks like this:
<a ui-sref="test({id:test._id})" class="ng-binding" href="#/test/57adc0e30a2ced3810983640">A test</a>
The href is correct and points to a database reference of an item. My goal is to have this reference as a variable in scope "test". My state provider:
$stateProvider
.state('tests', {
url: '/tests/',
templateUrl: 'test/index.html',
controller: 'Test.IndexController',
controllerAs: 'vm',
data: { activeTab: 'tests' }
})
.state('test', {
url: '/test/{id}',
templateUrl: 'test/item.html',
controller: 'Test.ItemController',
controllerAs: 'vm',
data: {
activeTab: 'tests',
testId: '{id}'
}
});
So far no matter what I tried I couldn't access "testId" in the "test" scope. It was either "undefined", created errors or returned "itemId: {id}".
My Item.Controller:
(function () {
'use strict';
function Controller(TestService) {
var vm = this;
vm.test = null;
function getTest(id) {
TestService.GetTestById(id).then(function(test) {
vm.test = test;
});
}
function initController() {
getTest(...);
}
initController();
}
angular
.module('app')
.controller('Test.ItemController', Controller);
})();
TestService provides http get methods for getting data from server.
(function () {
'use strict';
function Service($http, $q) {
var service = {};
function handleSuccess(res) {
return res.data;
}
function handleError(res) {
return $q.reject(res.data);
}
function GetTestById(_id) {
var config = {
params: {
testId: _id
}
};
return $http.get('/api/tests/:testId', config).then(handleSuccess, handleError);
}
service.GetTests = GetTests;
service.GetTestById = GetTestById;
return service;
}
angular
.module('app')
.factory('TestService', Service);
})();
I tried $scope - scope is not defined. I tried a number of other techniques, shown by other users with similar success - either "undefined" or error of some sort.
This is based on another person's code so there may be obvious mistakes, please let me know if you find any. If you need more code, let me know - I'll upload it to github (its a messy work in progress at the moment so I'm not sure what should be uploaded).
I'm trying to figure out how to pass unit_number into the modal when it pops up. I'm pretty new with Angular and I'm a little confused with what resolve: and group: are doing and how I can include the unit_number in that return statement.
$scope.openTenantModal = function (unit_number) {
var modalInstance = $uibModal.open({
animation: true,
templateUrl: 'views/addtenantmodal.html',
controller: 'AddTenantModalCtrl',
size: 'large',
resolve: {
group: function () {
return $scope.group;
}
}
});
modalInstance.result.then(function () {
}, function () {
});
};
You are using ui-bootstrap
Bootstrap components written in pure AngularJS
To pass a variable to a modal's controller you need to use
resolve: {
A: function() {
return 'myVal'
}
}
And then you can access that variable 'A' from the modal`s controller by injecting it
controller: ['A', function(A) {
// now we can add the value to the scope and use it as we please...
$scope.myVal = A;
}]
Check out: https://angular-ui.github.io/bootstrap/#/modal
Resolve:
Members that will be resolved and passed to the controller as locals; it is equivalent of the resolve property in the router.
And group is just a member (it could be anything you choose)
Just add a property in resolve object unitNumber with a function returning unit_number value from it. So that you can get the unit_number value inside AddTenantModalCtrl by injecting unitNumber dependency in controller factory function.
resolve: {
group: function () {
return $scope.group;
},
unitNumber: function(){
return unit_number
}
}
Note: Don't directly do unitNumber: unit_number, because when you have that, angular DI system will try to search the dependency
with name unit_number(value) and It will try to evaluate it as
function. Resultant you will get $injector error in console.
Please can anyone explain why the following throws an "Argument 'MainCtrl' is not a function, got undefined" error which seems to be tied into the use of module dependency injection with directives(?!).
angular.module('app', [])
.controller('MainCtrl', [function() {
var self = this;
self.name = "will this work";
self.items = [
{
name: "name 1",
test: "test 1"
},
{
name: "name 2",
test: "test 2"
}
];
}]);
angular.module('app',[])
.directive('typeahead', [function() {
return {
templateUrl: 'type-ahead.html',
restrict: 'AEC',
scope: {
items: '=',
prompt: '#',
title: '#',
subtitle: '#',
model: '=',
onSelect: '&'
}, <...more code>
Yet it will work perfectly fine when I remove the
[ ]
module dependency braces from the directive to read
angular.module('app').directive('typeahead', ...)
It also works perfectly fine if I define the directive as a cascade following the controller definition i.e.
angular.module('app', [])
.controller('MainCtrl', [function() {
var self = this;
self.name = "will this work";
self.items = [
{
name: "name 1",
test: "test 1"
},
{
name: "name 2",
test: "test 2"
}
];
}])
.directive('typeahead', [function() {
return {
Thanks in advance!
You are running into Angular's Creation versus Retrieval Problem:
Beware that using angular.module('myModule', []) will create the module myModule and overwrite any existing module named myModule. Use angular.module('myModule') to retrieve an existing module.
The first time you run angular.module('app',[]), Angular will create a module called app. Next time, you only need to run angular.module('app'), Angular will try to load the existing module app.
Since you call angular.module('app',[]) once again, module app has been re-initialized. That's why MainCtrl is undefined now.
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);
};
}]);