How to test a $uibModal instance controller? - angularjs

I'm trying to test a certain controller functionality with Jasmine unit testing.
I have a function which opens a modal instance with a new controller:
function openFilterModal(data) {
var modalInstance = $uibModal.open({
controller: FilterModalController,
controllerAs: 'vm',
templateUrl: 'modal.html',
resolve: {
data: function () {
return data;
}
}
});
return modalInstance.result;
}
The controller is defined below:
FilterModalController.$inject = ['alertsModalService', '$uibModalInstance', 'data', '$scope', 'logger', '$timeout'];
function FilterModalController(alertsModalService, $uibModalInstance, data, $scope, logger, $timeout) {
// code goes here }
When I try to call 'openFilterModal', I'm unable to get the controller instance, just getting the promise from it.
I've tried to used the $controller(FilterModalController) function in Jasmine, but it couldn't find the controller.
What is the best practice in that case?
Should I add the controller definition to my module? right now it defined like anonymous controller... not sure if that's good behavior or not.
like that:
app.controller('FilterModalController', FilterModalController);
Is it ok to define 2 controllers on the same module?
Or there is another way to get test the controller?
Thanks!

Related

Call Ionic function from the state views options

I have a controller 'ProjectController' which has various CRUD functions inside.
I want to be able to call my API when loading a view and without having to create another controller.
Pseudo code:
.state('app.my-project', {
url: '/projects/my-projects',
views: {
'menuContent': {
templateUrl: 'templates/my_projects.html',
function: myProject()
}
}
});
And in my controller:
.controller('ProjectCtrl', function($scope, $state, $auth, $http, $ionicPopup, $rootScope) {
$scope.myProjects = function() {
// foo
}
});
So the goal is that when setting up the states, I can call a function instead of just going to a controller?
The example im using is that when setting up the state for "my projects" I can call a function from within the controller.
Instead of having to create individual controllers for each action.

Inject dependency to controller called by directive

I would like to know if it is possible (if it is, so how? :)), to inject a dependency to a controller called by a directive.
I have a controller controller called MyCtrl. Here is his signature:
app.controller('MyCtrl', function ($scope, dataService, aDependency){...}
This Controller is usually defined in my route:
.segment('myPage', {
templateUrl: templatesUrl + 'mypage.html',
resolve: {
aDependency: ['$q', 'dataService', '$location', function ($q, dataService, $location) {
var defer = $q.defer();
dataService.retrieveCCData(defer, $location);
return defer.promise;
}],
},
controller: 'MyCtrl'
})
But now, I would also like to call this controller from a directive.
Problem is that I don't know How to inject the aDependency.
It said that the provider is unknown.
Here's my directive:
app.directive('gettingStarted1', ['dataService', function (dataService) {
return {
restrict: 'E',
templateUrl: templatesUrl + 'mypage.html',
controller: 'MyCtrl',
//resolve: {
//datasources: ['dataService', function (dataService) {
//return null;
//}]
//}
};
}]);
Resolve is impossible in directive.
Some help will be appreciate
Thank you
Make aDependency a separate service.
app.provider('aDependency', function () {
this.$get = ['$q', 'dataService', '$location', function ($q, dataService, $location) {
var defer = $q.defer();
dataService.retrieveCCData(defer, $location);
return defer.promise;
}];
});
You can resolve it with
resolve: {
'aDependency': 'aDependency',
}
or
resolve: ['aDependency'];
you could use the controller Function from the directive
.directive("sampledirective", function (dependancy1, dependancy2, ....) {
return {
scope: '=',
controller: function ($rootScope, $scope) {
//DO your controller magic here where you got your scope stuff
}
}
})
One thing i learned it seems the $scope values arent immediatly updated from directive to Controller. If you use objects like
$scope.smth.smth = 'test'
It gets updated immediatly else you would need to $apply

Working with $emit and $on from child modal to parent angularjs

I have this situation
two files, both in the same app
var app = angular.module('myapp');
file one is the parent and I have:
app.controller("ControllerOne", ['$scope', '$http', '$modal',
function ($scope, $http, $modal) {
$scope.$on('refreshList', function (event, data) {
console.log(data);
});
$scope.openModal = function () {
var modalInstance = $modal.open({
templateUrl: '/SomeFolder/FileWithControllerTwo',
controller: 'ControllerTwo',
size: 'lg',
resolve: {
someParam: function () {
return "param"
}
}
});
}
}]);
file two is the child and I have:
app.controller("ControllerTwo", ['$scope', '$http', 'someParam',
function ($scope, $http, someParam) {
$scope.SaveSomething = function () {
$http.post(url, obj)
.success(function (data) {
$scope.$emit('refreshList', [1,2,3]);
}).error(function () {
});
};
}]);
Assuming that i can open the modal and I can "SaveSomething".
What I need to do to send some data from ControllerTwo to ControllerOne?
I already checked this post Working with $scope.$emit and .$on
but I cant't solve the problem yet.
Obs:
FileOne.js -> I have the ControllerOne (parrent) -> $on
FileTwo.js -> I have the ControllerTwo (child) -> $emit
Yes, I can hit the code inside $http.post.success condition
Assuming you are using angular-ui bootstrap (which has a $model), then the $scope in the model is a childscope of $rootScope.
According to $model documentation you can supply the ControllerOne $scope by using the scope option which will make the modal's $scope a child of whatever you supply. Thus:
var modalInstance = $modal.open({
templateUrl: '/SomeFolder/FileWithControllerTwo',
controller: 'ControllerTwo',
size: 'lg',
scope: $scope,
resolve: {
someParam: function () {
return "param"
}
}
});
Then you could emit to that using $scope.$parent.$emit(...). Strictly speaking, this creates a coupling in that it assumes that the user of the modal listens to the events.
If you don't want to inject your scope, they you could inject $rootScope and emit on that. But that would also send the event to every scope in the application.
This is assuming that you actually want to leave the modal open and send a message back to the parent controller. Otherwise, just use close() or dismiss() methods.

How to test a Anonymous controller

I started writing unit tests using Jasmine for angularJS applications and stuck when writing a unit test to the controller in model which doesn't have a name.
How can I instantiate the controller in the model and test the controller?
angular.module('test',[])
.controller('alpha', function($scope, $modal){
$scope.openModal = function() {
$modal.open({
templateUrl: 'template.html',
backdrop: true,
windowClass: 'content-modal hbox',
controller: function(
$scope,
$modalInstance
) {
$scope.test = function() {
//test
}
}
));
}
});
Thanks in advance.
I would suggest it is much easier to unit test if you don't use an anonymous function for your controller. If you just write out another full controller you can use it for your modal and test it easily.
That said - here are several approaches to doing what you want to do: http://sirarsalih.com/tag/unit-testing-anonymous-javascript-functions/

Angular router with popup

I'm currently working on an app where a button triggers a method that will emit an event to elsewhere. This works great, however I also want to add a url to trigger this action.
So currently my button looks like this
<a class="addJob" ng-click="addNewJob()" ng-controller="AddJobController"></a>
But what I really want is it to just be
<a class="addJob" href="/new"></a>
Now, I can't figure out how to do the routing for this. It would mean that when I go to /new, the AddJobController should be triggered.
When I go directly to http://www.example.com/new, it should still load the page properly and trigger that action.
I don't want to create a separate page for this route as it is an essential part of the app flow.
(Think of it like when you create a new note in trello.com)
One Option
If you are willing to move to uiRouter, this is a common pattern.
Copied and pasted directly from the uiRouter FAQ
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-open-a-dialogmodal-at-a-certain-state
$stateProvider.state("items.add", {
url: "/add",
onEnter: ['$stateParams', '$state', '$modal', '$resource', function($stateParams, $state, $modal, $resource) {
$modal.open({
templateUrl: "items/add",
resolve: {
item: function() { new Item(123).get(); }
},
controller: ['$scope', 'item', function($scope, item) {
$scope.dismiss = function() {
$scope.$dismiss();
};
$scope.save = function() {
item.update().then(function() {
$scope.$close(true);
});
};
}]
}).result.then(function(result) {
if (result) {
return $state.transitionTo("items");
}
});
}]
})
Second Option
The second options would be to launch the modal the constructor of your controller. I have included a modalFactory. This is a common pattern. It allows your modals to be reusable, and cleans up your controllers. The uiRouter example above should use the factory pattern as well to abstract the modal setup out of the state config.
This example should work with ngRouter.
app.controller('addJobModalController', ['modalFactory', function(modalFactory) {
modalFactory.addJob();
}]);
app.factory('modalFactory', ['$modal', function($modal) {
return {
addJob: function() {
return $modal.open({
templateUrl: 'views/addjob-modal.html',
controller: 'AddJobController',
size: 'md'
});
}
}
}]);
The addJob() method returns the modal's promise. If you want, you can store this promise in the factory to be returned by another method so that another controller or service can act on the result of the modal.

Resources