Simple way of opening modal with ngDialog is this:
ngDialog.open({
template: 'template.html',
controller: 'someCtrl'
})
How can I send variables to that 'someCtrl'?
Is there such thing as 'resolve' in ngDialog?
Example from angular-bootstrap modal:
$modal.open({
template: "<p>This is template</p>",
controller: "someCtrl",
resolve: {
someVar: function(){
return "Value of someVar"
}
}
})
this would open the modal send the 'someVar' to the responsible Controller.
UPDATE:
It seems like new version of ngDialog added this feature:
ngDialog.open({
controller: function Ctrl(dep) {/*...*/},
resolve: {
dep: function depFactory() {
return 'dep value';
}
}
});
Well looks like ngDialog doesn't support resolve and custom injection in controller. However you can alway do it manually by creating controller instance yourself:
ngDialog.open({
scope: $scope,
template: 'template.html',
controller: $controller('someCtrl', {
$scope: $scope,
name: 'Thomas'
})
});
then in controller you will be able to access injected service/variable:
app.controller('someCtrl', function($scope, name) {
console.log(name); // Thomas
});
However there is a caveat with this approach, because when controller in instantiated by ngDialog itself it also injects $element service in it, which is an angular.element instance of the opened dialog HTML (however I doubt it's even necessary in controller). But you should know it anyway.
Demo: http://plnkr.co/edit/3YpQ2bemk8fntKAPWY9i?p=preview
Related
Is is possible to use property injection in angularJS?
Scenario
I know this will work
app
.module('myapp')
.config(function($stateProvider) {
$stateProvider.state('the-state', {
url: '/the/url',
templateUrl: 'view.html',
controller: 'ctrl',
controllerAs: 'vm',
resolve: {
'boolFlag': function(service){return service.getBoolean();}
}
});
})
.service('service', function(){
this.getBoolean = function() {return ...};
})
.controller('ctrl', function(boolFlag) {
this.boolFlag = boolFlag;
this.execute = function(){...};
});
<div ng-show="vm.boolFalg">
simple dynamic content
</div>
<button type="button" ng-click="vm.execute()">Click Me</button>
But it feels leaky. boolFlag` is only used in the view to show/hide content. Is there I way I can resolve the controller and set the boolFlag property on the controller instance? I assume providers or factories would be the way to go, but I'm going around in circles trying to make sense of it.
I envision it would look something like
app
.module('myapp')
.config(function($stateProvider) {
$stateProvider.state('the-state', {
url: '/the/url',
templateUrl: 'view.html',
controller: 'ctrl',
controllerAs: 'vm',
});
})
.provider('ctrlProvider', function(ctrlProvider, service) {
var ctrl = ctrlProvider.$get/invoke/build/create();
ctrl.boolFlag = service.getBoolean();
return ctrl;
})
.service('service', function(){
this.getBoolean = function() {return ...};
})
.controller('ctrl', function() {
this.execute = function(){...};
});
I could also be going about this the wrong way. Many controllers will need the boolFlag property. Maybe it should be part of a $parentScope? But I don't know how to code that.
Update
I was thinking about this more last night. The boolFlag doesn't need to be associated to the controller at all. It only needs to be part of the scope. $scope.boolFlag = service.getBoolean();
The question then becomes, how can I populate $scope without the controller?
you could use factory to maintain the value of boolFlag so that it is shared between the controllers. It is better to avoid using $parentScope. This explains how to proceed with that. If you need to set the value of factory initially you could also use it in app.config of your main module to set its value.
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!
I have the following code in a large Angular project:
$stateProvider.state('my-app', {
url : '/',
views: {
'content#': {
templateUrl: '/?page=/home',
controller: 'HomeController'
}
}
});
I know I can remove the controller using delete element['views']['content#']['controller']; and init with element afterwards but how can I check if HomeController exists? After a day of researching. No working solution seems to exist in Angular.
You should not remove controllers. This should be done by angular.
But you could set some injected service variable on controller creation and clear it 'on destroy'. I.e. to do something like:
angular
.module('something')
.controller('HomeController', HomeController);
HomeController.$inject = ['$scope', 'myGlobalService'];
function HomeController($scope, myGlobalService) {
myGlobalService.isHomeControllerPresent = true;
$scope.$on("$destroy", function() {
myGlobalService.isHomeControllerPresent = undefined;
});
}
But it's more a hack than 'Angular style'.
If you have access to $scope and you know controller name (i.e. ng-controller="homeCtrl as HomeController" or controllerAs: 'homeCtrl') then you could just check:
if ($scope.homeCtrl) {
...
}
because any controller is a part of its scope - each controller is 'attached' to its scope by name.
But once again: no one should remove controllers except angular ;)
Based on a comment of another question from me I tried to create a directive to reduce my code. Here what I got:
Directive (very small for testing. Later it will be more elements):
BebuApp.directive('inputText', function(){
return {
restrict: 'E',
scope: {
model: '='
},
template: '<input type="text" ng-model="model" />'
}
});
State:
.state('app', {
abstract: true,
url: '',
templateUrl: 'layout.html',
resolve: {
authorize: function ($http) {
return $http.post(API.URL_PING);
}
}
})
.state('app.application-detail', {
url: "/bewerbungen/{id}",
templateUrl: "views/application-detail/application-detail.html",
data: {pageTitle: 'Meine Bewerbungen', pageSubTitle: ''},
controller: "ApplicationDetailController",
resolve: {
prm: function ($http, $stateParams) {
// $http returns a promise for the url data
return $http.get(API.URL_JOBAPPLICATION_GET_DETAILS + "/" + $stateParams.id);
}
}
})
Controller:
'use strict';
BebuApp.controller('ApplicationDetailController', function($rootScope, $scope, $http, $stateParams, API, prm) {
$scope.jobApplication = prm.data;
console.log(prm);
$scope.$on('$viewContentLoaded', function() {
// initialize core components
App.initAjax();
});
});
Template / View:
<div class="margin-top-10">
{{ jobApplication }}
<input-text model="jobApplication.description"></input-text>
</div>
When the page is loaded I can see the correct model (output by {{jobApplication}}), but the input field is empty. I need a normal two way binding. When the model changes in the scope it should also change in the directive and vice versa. As far as I understand the model is retrieved by the resolve callback / function in the state, so it should be "there" when the template is compiled.
Where is my problem?
I found the problem after an closer look to the model (thanks to the comments!). In fact the model I received from my backend was a collection with just one entry. It looked like this:
[{id:"xxx", description:"test".....}]
Of course it must look like this:
{id:"xxx", description:"test"...}
After fixing this stupid mistake, everything works fine!
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.