Normally, I created a controller which used $scope syntax, so, I could pass a current $scope to a isolated scope of the modal directive as following:
$scope.openEditModal = function() {
$scope.modalInstance = $modal.open({
templateUrl: 'views/budgets/mainbudgets/edit',
scope: $scope // Passing a $scope variable
});
$scope.modalInstance.close();
};
However, I just switched the controller to use this syntax:
var self = this;
// User edit modal
this.openEditModal = function() {
self.modalInstance = $modal.open({
templateUrl: 'views/budgets/mainbudgets/edit',
scope: self; // This doesn't work
});
self.modalInstance.close();
};
So, how can I pass a current this to be used in isolated scope of the modal directive?
EDIT
Here is the whole code of my controller:
angular.module('sms.budgets').controller('BudgetsMainController', ['$scope', 'Global', '$modal', '$timeout', '$rootScope','Budgets', function($scope, Global, $modal, $timeout, $rootScope,Budgets) {
var self = this;
this.newBudget = {};
this.budgets = [];
function init() {
var data = {};
// Load main budget from DB
Budgets.load('main-budgets').success(function(budgets) {
self.budgets = budgets || [];
});
}
init();
/**
* Create a new main budget
*/
this.create = function() {
var data = {};
data.budget = self.newBudget;
data.dbName = 'Budget';
Budgets.create('budgets', data).success(function() {
self.isSuccess = true;
$timeout(function() {
self.isSuccess = false;
}, 5000);
}).error(function(err) {
self.isError = true;
$timeout(function() {
self.isError = false;
}, 5000);
});
};
this.edit = function() {
self.modalInstance.close();
};
// User edit modal
this.openEditModal = function() {
var newScope = $rootScope.$new();
newScope.modalInstance = self.modalInstance;
newScope.edit = self.edit;
self.modalInstance = $modal.open({
templateUrl: 'views/budgets/mainbudgets/edit',
scope: newScope
});
self.modalInstance.close();
};
this.cancelEditModal = function() {
self.modalInstance.dismiss('cancel');
};
}]);
You can't use this as a scope. They are different. Since $scope is a internal variable of AngularJS you have to keep it as it.
To show that, I've created a Plunkr (open the console and see the diffence between this and $scope)
http://plnkr.co/edit/DkWQk4?p=preview
Anyway, is a good practise to use a different scope on the modal controller. Here you have an example showing how to communicate between the main controller and the modal controller:
From the MainCtrl:
var modalInstance = $modal.open({
templateUrl: 'views/parts/modalUrlImg.html',
controller: 'ModalUrlCtrl',
resolve: {
url: function () { // pass the url to the modal controller
return $scope.imageUrl;
}
}
});
// when the modal is closed, get the url param
modalInstance.result.then(function (url) {
$scope.courses[i].imageUrl = url;
});
From the Modal Ctrl:
.controller('ModalUrlCtrl', function($scope, $modalInstance, url) {
$scope.url = url; // get the url from the params
$scope.Save = function () {
$modalInstance.close($scope.url);
};
$scope.Cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.Clean = function () {
$scope.url = '';
};
});
Hope this help you, cheers.
--- EDIT ---
You can keep the controller as syntax. In fact, you must mix both syntax, since you can only use this to add vars and functions, but not tu access other scope things, such as $scope.$on...
So, to do that in your case, just pass $scope:
var self = this;
// User edit modal
this.openEditModal = function() {
self.modalInstance = $modal.open({
templateUrl: 'views/budgets/mainbudgets/edit',
scope: $scope;
});
self.modalInstance.close();
};
I've tried in the updated plunkr and it's working now:
http://plnkr.co/edit/DkWQk4?p=preview
$scope != controller
By passing this to $modal.open() you are passing the reference of the controller and not the scope. Try passing $scope instead.
Related
I've written a couple of these tests previously on other controllers and it works just fine. But on this more complex controller, no matter which function I test it on, the .callFake using $q is not entering into the .then block and so no logic in there is being executed at all. I know the controller works for sure as this is on a production website. So why doesn't my test even enter the .then block when I've returned the deferred promise?
Controller Code -
(function() {
'use strict';
angular
.module('myApp')
.controller('EntryAddController', EntryAddController);
function EntryAddController($scope, $state, moment, $sanitize, EntryFactory, $modal, toastr, $log) {
var vm = this;
var now = moment();
var $currentYear = now.year();
vm.queues = [];
vm.calculating = false;
vm.total = 0;
vm.saving = false;
vm.addTxt = 'Confirm Entry';
var tomorrow = now.add(1, 'days').toDate();
var to = now.subtract(1, 'days').add(12, 'months').toDate();
vm.fromDate = tomorrow;
vm.toDate = to;
activate();
////////////////
function activate() {
var queueCache = {};
vm.updateEntrys = function() {
var payload = {
'from': moment(vm.fromDate).format('MM/DD/YYYY'),
'to': moment(vm.toDate).format('MM/DD/YYYY'),
'freq': vm.frequency.value,
'total': vm.total_amount
};
var key = JSON.stringify(payload);
if (!(key in queueCache)) {
EntryFactory.getEntryQueue(payload)
.then(function(resp) {
//LOGIC HERE BUT TEST NEVER ENTERS HERE DESPITE USING $Q
});
} else {
vm.queues = queueCache[key].queue;
vm.total = queueCache[key].total;
vm.calculating = false;
}
}
}
}
})();
Test Code
(function() {
'use strict';
describe('Entry Add Controller Spec', function(){
var vm;
var $controller;
var $q;
var $rootScope;
var EntryFactory;
var $scope;
var toastr;
beforeEach(module('myApp'));
beforeEach(inject(function(_$controller_, _$q_, _$rootScope_, _EntryFactory_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
$scope = _$rootScope_.$new();
$q = _$q_;
EntryFactory = _EntryFactory_;
vm = $controller('EntryAddController', { $scope: $scope });
}));
it('expect EntryFactory.getEntryQueue to correctly set queues and total upon successful response', function() {
var payload = "blah";
var resp = {
"data": 1
}
spyOn(EntryFactory, 'getEntryQueue').and.callFake(function(payload) {
var deferred = $q.defer();
deferred.resolve(resp);
return deferred.promise;
});
EntryFactory.getEntryQueue(payload);
$rootScope.$apply();
//expect logic in .then to have worked
});
});
})();
Edit
Just thought of something... is this because I'm calling the factory function (EntryFactory.getEntryQueue) directly in the test, instead of calling the vm.updateEntrys function around it and therefore it doesn't ever proceed onto the .then portion of the code?
You allways need to consider what your SUT (System/Software under test) is.
In this case it is a controller's component method vm.updateEntrys.
It calls your Mocked EntryFactory.getEntryQueue() method. Also your code is not complete (where do you define vm.frequency object?). Nevertheless I have tested this method with the following test :
/*property
$apply, $log, $modal, $new, $sanitize, $scope, $state, EntryFactory, and,
callFake, data, defer, getEntryQueue, moment, promise, resolve, toastr,
updateEntrys
*/
(function () {
'use strict';
describe('Entry Add Controller Spec', function () {
var vm;
var $controller;
var $q;
var $rootScope;
var EntryFactory;
var $scope;
var toastr;
beforeEach(module('app.controllers'));
beforeEach(inject(function (_$controller_, _$q_, _$rootScope_) {
$controller = _$controller_;
$rootScope = _$rootScope_;
$q = _$q_;
}));
beforeEach(function () {
$scope = $rootScope.$new();
EntryFactory = {
getEntryQueue: function () {
}
};
moment = function () {
return {
format: function () {
}
}
}
vm = $controller('EntryAddController', { '$scope': $scope, '$state': {}, 'moment': moment, '$sanitize': {}, 'EntryFactory': EntryFactory, '$modal': {}, 'toastr': {}, '$log': {} });
});
it('expect EntryFactory.getEntryQueue to correctly set queues and total upon successful response', function () {
var payload = "blah";
var resp = {
"data": 1
};
spyOn(EntryFactory, 'getEntryQueue').and.callFake(function (payload) {
var deferred = $q.defer();
deferred.resolve(resp);
return deferred.promise;
});
vm.updateEntrys();
EntryFactory.getEntryQueue(payload);
$scope.$apply();
//expect logic in .then to have worked
});
});
})();
I have the following instruction in a file called loginCtrl.js and I cannot make $rootScope to hold the value of vm.userEmail.
app.controller('LoginCtrl', function($timeout, $q, $log, $rootScope /*$auth*/, $location, $mdToast) {
var vm = this;
vm.loginMessage = '';
vm.login = function(){
$auth.login({
$rootScope: vm.userEmail,
password: vm.userPassword
})
}
});
$rootScope would help me in this conditional whenever this particular user gets connected.
if ($rootScope == 'y_y#hotmail.com') {
data = [1,2,3,4]
} else {
data = [5,6,7,8]
}
Any ideas, suggestions or corrections are welcome ;)
$rootscope is like an application context. So you can add new value to the application context. Below is your working code.
app.controller('LoginCtrl', function($timeout, $q, $log, $rootScope /$auth/, $location, $mdToast)
{
var vm = this;
vm.loginMessage = '';
vm.login = function(){
$auth.login({
$rootScope.userEmail : vm.userEmail,
password: vm.userPassword
})
}
});
Now you can use read $rootScope.userEmail from anywhere in the application.
if ($rootScope.userEmail == 'y_y#hotmail.com')
{
data = [1,2,3,4]
} else {
data = [5,6,7,8]`enter code here`
}
instead of
$rootScope = someValue;
use
$rootScope.someValue= someValue;
For second part your code try:
you should watch change on $rootSchope.someValue
something like:
$rootScope.$watch('$rootScope.someValue', function(newValue) {
if (newValue='y_y#hotmail.com') {
data = [1,2,3,4]
}
});
I want to call a function or change the value of variable/s which is there inside another controller. I looked online for the solution and understood that I have to create a service and use it inside both the controller, however I am not able to understand that how service will have access to $scope.home_main and $scope.home_main variables as they are in different scope.
JS
app.controller('Controller1', function ($scope, $window) {
$scope.toggle = function() {
$scope.home_main = !$scope.home_main;
$scope.full_page_place_holder = !$scope.full_page_place_holder;
};
});
app.controller('Controller2', function ($scope, $window) {
$scope.onTabSelect=function(){
// here I want to call toggle function which is inside another controller.
};
});
Updated HTML
<div ng-controller="Controller1">
<div ng-hide="home_main"></div>
</div>
<div ng-controller="Controller2">
<div ng-hide="full_page_place_holder"></div>
</div>
Looked here: SO-Q1, SO-Q2. Thanks.
you can create a service as follows,
angular.module('someApp').service('shareDataService', function() {
var popup;
var setDetails = function(param) {
popup = param;
};
var getDetails = function() {
return popup;
};
return {
setDetails: setDetails,
getDetails: getDetails,
};
});
This service will not have access to the $scope variables of the two controllers, instead you can call getDetails and setDetails to get and set the variable in the service.
suppose you want to send the value of 'home_main' from controller1 to controller2, in controller1, you call the service function setDetails
app.controller('Controller1', function ($scope, $window, shareDataService) {
$scope.toggle = function() {
$scope.home_main = !$scope.home_main;
$scope.full_page_place_holder = !$scope.full_page_place_holder;
shareDataService.setDetails($scope.home_main);
};
});
and in controller2, you get the value by calling the service
app.controller('Controller2', function ($scope, $window) {
var home_main_value = shareDataService.getDetails();
});
You can do a similar thing for functions also.....
Hope it helps
You misunderstood the concept service will have a single variable that is going to be shared by two controllers:-
$scope is local for controller and cannot accessed by another controller:-
FOR Example:-
myApp.factory('Data', function () {
var data = {
home_main : ''
};
return {
gethome_main : function () {
return data.home_main ;
},
sethome_main : function (home_main ) {
data.home_main = home_main ;
}
};
myApp.controller('FirstCtrl', function ($scope, Data) {
$scope.home_main= '';
$scope.$watch('home_main', function (newValue, oldValue) {
if (newValue !== oldValue) Data.sethome_main(newValue);
});
});
myApp.controller('SecondCtrl', function ($scope, Data) {
$scope.$watch(function () { return Data.gethome_main(); }, function (newValue, oldValue) {
if (newValue !== oldValue) $scope.home_main= newValue;
});
});
I have two controllers
app.controller('TestCtrl1', ['$scope', function ($scope) {
$scope.save = function () {
console.log("TestCtrl1 - myMethod");
}
}]);
app.controller('TestCtrl2', ['$scope', function ($scope) {
$scope.var1 = 'test1'
$scope.save = function () {
console.log("TestCtrl1 - myMethod");
}
}]);
Then i have two services
.service('Service1', function($q) {
return {
save: function(obj) {
}
}
})
.service('Service2', function($q) {
return {
save: function(obj) {
}
}
})
For my 60% of stuff i just call save on ctrl1 which then called service save method
Now There are cases where before saving i need to do some stuff like chnaging some object parameters different than genral case there i check e,g
if(model == 'User'){
//Here i do this (sample of code)
var service = $injector.get('Service2');
service.save()
Now my problem is in Service 2 i need access to var1. How can i do that
Use the service(s) itself to share the variable as part of the service object as well as methods of each service
.service('Service2', function($q) {
var self = this;
this.var1 = 'test1';
this.save = function(obj) {
}
});
app.controller('TestCtrl2', ['$scope','Service1','Service2', function ($scope, Service1, Service2, ) {
// bind scope variable to service property
$scope.var1 = Service2.var1;
// add a service method to scope
$scope.save = Service1.save;
// now call that service method
$scope.save( $scope.var1 );
}]);
You can also inject a service into another service if needed
injecting services into other services (one possible method) :
html:
<div id="div1" ng-app="myApp" ng-controller="MyCtrl">
<!--to confirm that the services are working-->
<p>service three: {{serviceThree}}</p>
</div>
js:
angular.module('myApp',[])
.service('s1', function() {
this.value = 3;
})
.service('s2', function() {
this.value = 10;
})
.service('s3', function(s1,s2) { //other services as dependencies
this.value = s1.value+s2.value; //13
})
.controller('MyCtrl', function($scope, $injector) { //$injector is a dependency
$scope.serviceThree = $injector.get('s3').value; //using the injector
});
here's the fiddle: https://jsfiddle.net/ueo9ck8r/
this might be simple but I cant find any solution anywhere. Right now i have a directive which uses a filter function. Everything works fine but I am trying to create a unit test for it and it is giving me
Error: [$injector:unpr] Unknown provider: inArrayFilterProvider <- inArrayFilter
This is my directive
var directiveApp = angular.module('myApp.directives',[]);
directiveApp.directive('navbar', function($filter) {
return {
templateUrl: 'partials/navbar.html',
controller: function($scope, $location) {
$scope.checkLocation = function(){
return $location.path();
};
$scope.isActive = function (viewLocation) {
var locations = viewLocation.split(',');
//return viewLocation === $location.path();
var path = '/' + $location.path().split('/')[1];
//return $filter('inArray')(locations, $location.path());
return $filter('inArray')(locations, path);
};
}
};
});
And this is my unit test
'use strict';
describe('myApp.directives module', function() {
beforeEach(module('myApp.directives'));
describe('navbar directive', function() {
var $scope,controller,template;
beforeEach(inject(function($rootScope,$compile,$location,$filter,$templateCache){
$scope = $rootScope.$new();
$templateCache.put('partials/navbar.html','I am here');
spyOn($location,'path').andReturn("/festivals");
var element = angular.element('<div data-navbar=""></div>');
template = $compile(element)($scope);
controller = element.controller;
$scope.$digest();
}));
it('Check Festival nav', function()
{
expect($scope.isActive('/festivals')).toBeTruthy();
});
});
});
I think i will have to spyOn filter when it is called with inArray and return a mock value but not really sure how to do it.
Thank you
Fixed.
I had to include my main module which had inArray function
describe('myApp.directives module', function() {
var $filter;
beforeEach(module('myApp'),function(_$filter_){
$filter = _$filter_;
});
.../*Rest as Before*/
...
...});