Angularjs Modal: Returning state from service - javascript

Using angularjs here:
I have looked at the following forum for my query but not able to resolve this :returning data from angularjs modal dialog service
I have the following modal html:
<div class="confirm-modal-header">
<h4 class="modal-title">{{modalOptions.headerText}}</h4>
</div>
<div class="modal-body">
<p>{{modalOptions.bodyText}}</p>
</div>
<div class="modal-footer">
<button type="button" class="btn"
data-ng-click="modalOptions.close()">
{{modalOptions.closeButtonText}}
</button>
<button type="button" class="btn btn-danger"
data-ng-click="modalOptions.ok();">
{{modalOptions.actionButtonText}}
</button>
</div>
I also have the service for this:
CTApp.factory("dialogModalService", ["$q", "$timeout", "$state", "$modal",
function ($q, $timeout, $state, $modal) {
var service = {}
var modalDefaults = {
backdrop: true,
keyboard: true,
modalFade: true,
templateUrl: '/app/modal.html'
};
var modalOptions = {
closeButtonText: 'Close',
actionButtonText: 'OK',
headerText: 'Proceed?',
bodyText: 'Perform this action?'
};
service.showModal = function (customModalDefaults, customModalOptions) {
if (!customModalDefaults) customModalDefaults = {};
customModalDefaults.backdrop = 'static';
return this.show(customModalDefaults, customModalOptions);
};
service.show = function (customModalDefaults, customModalOptions) {
//Create temp objects to work with since we're in a singleton service
var tempModalDefaults = {};
var tempModalOptions = {};
//Map angular-ui modal custom defaults to modal defaults defined in service
angular.extend(tempModalDefaults, modalDefaults, customModalDefaults);
//Map modal.html $scope custom properties to defaults defined in service
angular.extend(tempModalOptions, modalOptions, customModalOptions);
if (!tempModalDefaults.controller) {
tempModalDefaults.controller = function ($scope, $modalInstance) {
$scope.modalOptions = tempModalOptions;
$scope.modalOptions.ok = function (result) {
$modalInstance.close(result);
};
$scope.modalOptions.close = function (result) {
$modalInstance.dismiss('cancel');
};
}
}
return $modal.open(tempModalDefaults).result;
};
return service;
}
]);
And the above modal is being called from my controller as:
dialogModalService.showModal({}, modalOptions).then(function (result)
{
//check for result returned
}
Now in my controller I want to check whether the user pressed the cancel button or not. How can I check that?
I tried to pass the cancel along with
modalOptions.close('cancel')
But it never enters my controller loop of the dialogModalService.showModal.
In my browser also I tried to put a breakpoint inside the call above but again its never hit.
What am I doing wrong here?

In your Modal service try changing:
$modalInstance.dismiss('cancel');
to
$modalInstance.close('cancel');

Related

Showing events in a md-dialog from the clicked date on a calendar

I am developping a MEAN stack application, and I got a question about using md-dialog from Angular Material.
On my webpage I have a calendar displayed with the events displayed on the calendar. When the user clicks on that date, I want a dialog popping up with a list and some info about the events taking place that day.
I have succesfully implemented the dialog behaviour of showing up when clicked on a date. Now my problem is that the data in the dialog isn't updated.
This is the function in my controller that's called when a date is clicked in the calendar
function dayClick(date){
vm.dateClicked = date;
getEventsByDay(date);
showDialog();
}
As you can see I then call another method in my controller to get the events taking place on the selected date from my database.
function getEventsByDay(date){
vm.eventsday = eventService.getEventsByDay(date).then(function(res){
vm.eventsday = res;
return vm.eventsday;
});
}
The events are successfully retreived from the database.
After we get the events are retreived, showDialog is called.
function showDialog(ev) {
$mdDialog.show({
parent: angular.element(document.body),
controller: MainController,
controllerAs: 'ctrl',
templateUrl: '/templates/dialogevent.html',
hasBackdrop: true,
panelClass: 'dialog-events',
clickOutsideToClose: true,
escapeToClose: true
});
}
So when using some logging in my code I found out that somehow the dialog is rendered and called before data is retreived. I tried to chain the methods to force them to execute after one another, but I can't seem to get that to work.
Any suggestions ?
Edit:
This is the template html
<md-dialog aria-label="Event Dialog">
<md-toolbar>
<div class="md-toolbar-tools">
<h2>Events on {{ctrl.dateClicked | date:"dd/MM/yyyy"}}</h2>
<span flex></span>
<md-button class="md-icon-button" ng-click="cancel()">
<md-icon aria-label="Close dialog">close</md-icon>
</md-button>
</div>
</md-toolbar>
<md-dialog-content style="max-width:800px;max-height:810px; ">
<md-list>
<md-list-item ng-repeat="event in ctrl.eventsday">
<div class="md-list-item-text">
<h4>{{event.name}}</h4>
<h5>{{event.eventType.name}}</h5>
<p>{{event.description}}</p>
</div>
</md-list-item>
</md-list>
</md-dialog-content>
Edit 2:
I'm initializing "vm.clickedDate" in the top of my controller
vm.dateClicked = new Date();
New date will give today's date. When I add events with today's date, the events are not shown in the dialog.
Edit 3:
This is the complete controller code
(function() {
'use strict';
angular.module('ptlab').controller('MainController', MainController);
MainController.$inject = ['$http', '$log', 'auth', '$state', '$stateParams', 'eventService', 'MaterialCalendarData', '$scope', '$mdDialog', '$timeout'];
function MainController($http, $log, auth, $state, $stateParams, eventService, MaterialCalendarData, $scope, $mdDialog, $timeout) {
var vm = this;
vm.users = [];
vm.getUsers = getUsers;
vm.openingsuren = [];
vm.events = [];
vm.setDayContent = setDayContent;
vm.dateClicked = new Date();
$scope.dayClick = dayClick;
$scope.cancel = cancel;
vm.showDialog = showDialog;
vm.eventsday = {};
vm.getEventsByDay = getEventsByDay;
vm.userStudent = true;
vm.userCoworker = true;
vm.userManager = true;
activate();
function activate() {
return load();
}
function load(){
getUsers();
getOpeningsuren();
getEvents();
}
function getUsers() {
return auth.getAll()
.then(function(data) {
vm.users = data.data;
return vm.users;
});
}
function getOpeningsuren(){
return $http.get('/javascripts/content.json').success(function(data){
vm.openingsuren = data.openingsuren.dag;
});
}
function getEvents(){
vm.events = eventService.getAll().then(function(res){
vm.events = res.data;
var evenement;
for(evenement of vm.events){
var content = createContentCalendar(evenement);
setDayContent(evenement.startdate, content);
}
return vm.events;
});
}
function setDayContent(date, content){
MaterialCalendarData.setDayContent(new Date(date), content);
}
function dayClick(date){
vm.dateClicked = date;
getEventsByDay(date);
}
function cancel(){
$mdDialog.cancel();
}
function createContentCalendar(evenement){
var string = "";
string += "<div class='item-box text-center'><h7>" + evenement.name + "</h7></div>";
return string;
}
function showDialog(ev) {
console.log("showDialog");
console.log(vm.eventsday);
$mdDialog.show({
parent: angular.element(document.body),
controller: MainController,
controllerAs: 'ctrl',
templateUrl: '/templates/dialogevent.html',
hasBackdrop: true,
panelClass: 'dialog-events',
targetEvent: ev,
clickOutsideToClose: true,
escapeToClose: true,
allowParentalScroll: true
});
}
function getEventsByDay(date){
vm.eventsday = eventService.getEventsByDay(date).then(function(res){
vm.eventsday = res;
$timeout(showDialog, 1000);
return vm.eventsday;
});
}
} })();
Edit4:
Tried like suggested by adding the following to showDialog:
locals: {
eventsday: vm.eventsday,
day: vm.dateClicked
},
controller: CalendarController,
controllerAs: 'ctrl'
I also made a controller called "CalendarController":
(function() {
'use strict';
angular.module('ptlab').controller('CalendarController', CalendarController);
CalendarController.$inject = ['$mdDialog', 'eventsday', 'day'];
function CalendarController($log, auth, $state, $stateParams, $mdDialog, eventsday, day) {
var vm = this;
vm.eventsday = eventsday;
vm.dateClicked = day;
function cancel(){
$mdDialog.cancel();
}
}})();
Now I get the error that "CalendarController" used in showDialog is not defined. I've added the calendarcontroller file to my script tags, so it should find the file in my index.ejs. Am I missing something or do I need to inject something else ?
Edit 5:
Found the solution. Everything works like in "Edit 4", just forgot the quotes around CalendarController in the show method.
You don't need to load MainController again in $mdDialog.show. It hasn't data in vm.eventsday during second-time-running.
Just create new controller and inject data there.
So you controller in $mdDialog.show is something like this
locals:{parent: vm},
controller: function () { this.parent = vm },
controllerAs: 'ctrl',
Or see example here https://material.angularjs.org/latest/api/service/$mdDialog it's similar your task

How to use angular component with ui.bootstrap.modal in angular 1.5?

I'd like to use angular component with ui.bootstrap.modal. angular version is 1.5.
I tried to use like below.
component
function MyComponentController($uibModalInstance){
var ctrl = this;
ctrl.doSomething = function() {
//doSomething
}
}
app.component('myComponent', {
contoller: MyComponentController,
templateUrl: '/path/to/myComponent.html'
}
parent controller
function parentController($uibModal){
var ctrl = this;
ctrl.openModal = function(){
var modalInstance = $uibModal.open({
template: '<my-component></my-component>'
}
}
And when I execute parentController.openModal() , I got the error of $injector:unpr Unknown Provider although modal window is open.
Is there a way to use angular component with ui.bootstrap.modal?
If you need more information, please let me know that.
Thank you.
EDIT
I've got a way to use component with ui.bootstrap.modal from Renato Machado, Thanks Renato.
But I feel It's a little bit complicated and not convenient. So finally I think that it's better to use component inside the modal.
Modal is opened with regular way(just set controller and template in $uibModal.open({}) ) and the modal contains the component which has logics you want to use commonly.
Modal should have only simple logics that are related with modal like close modal window.
Another logics mainly related with business/Application should be in component.
It makes easy to commonalize.
EDIT: As of UI Bootstrap 2.1.0 there is native support for component in bootstrap modals. It looks like there have been several quick releases after 2.1.0 to fix some issues with the modals, so I'd be sure to grab the latest.
See this Plunk for a version using UI Bootstrap 2.1.0+
http://plnkr.co/edit/jy8WHfJLnMMldMQRj1tf?p=preview
angular.module('app', ['ngAnimate', 'ui.bootstrap']);
angular.module('app')
.component('myContent', {
template: 'I am content! <button type="button" class="btn btn-default" ng-click="$ctrl.open()">Open Modal</button>',
controller: function($uibModal) {
$ctrl = this;
$ctrl.dataForModal = {
name: 'NameToEdit',
value: 'ValueToEdit'
}
$ctrl.open = function() {
$uibModal.open({
component: "myModal",
resolve: {
modalData: function() {
return $ctrl.dataForModal;
}
}
}).result.then(function(result) {
console.info("I was closed, so do what I need to do myContent's controller now. Result was->");
console.info(result);
}, function(reason) {
console.info("I was dimissed, so do what I need to do myContent's controller now. Reason was->" + reason);
});
};
}
});
angular.module('app')
.component('myModal', {
template: `<div class="modal-body"><div>{{$ctrl.greeting}}</div>
<label>Name To Edit</label> <input ng-model="$ctrl.modalData.name"><br>
<label>Value To Edit</label> <input ng-model="$ctrl.modalData.value"><br>
<button class="btn btn-warning" type="button" ng-click="$ctrl.handleClose()">Close Modal</button>
<button class="btn btn-warning" type="button" ng-click="$ctrl.handleDismiss()">Dimiss Modal</button>
</div>`,
bindings: {
modalInstance: "<",
resolve: "<"
},
controller: [function() {
var $ctrl = this;
$ctrl.$onInit = function() {
$ctrl.modalData = $ctrl.resolve.modalData;
}
$ctrl.handleClose = function() {
console.info("in handle close");
$ctrl.modalInstance.close($ctrl.modalData);
};
$ctrl.handleDismiss = function() {
console.info("in handle dismiss");
$ctrl.modalInstance.dismiss("cancel");
};
}]
});
Original answer is below:
I was trying to figure this out the other day too. I took the information I found in this post along with this link to try and come up with an alternate way to accomplish this. These are some reference links I found that helped me:
https://github.com/angular-ui/bootstrap/issues/5683
http://www.codelord.net/ (this one helped in understanding passing arguments to callbacks in components)
Also here is a Plunk: http://plnkr.co/edit/PjQdBUq0akXP2fn5sYZs?p=preview
I tried to demonstrate a common real world scenario of using a modal to edit some data.
angular.module('app', ['ngAnimate', 'ui.bootstrap']);
angular.module('app')
.component('myContent', {
template: 'I am content! <button type="button" class="btn btn-default" ng-click="$ctrl.open()">Open Modal</button>',
controller: function($uibModal) {
$ctrl = this;
$ctrl.dataForModal = {
name: 'NameToEdit',
value: 'ValueToEdit'
}
$ctrl.open = function() {
$uibModal.open({
template: '<my-modal greeting="$ctrl.greeting" modal-data="$ctrl.modalData" $close="$close(result)" $dismiss="$dismiss(reason)"></my-modal>',
controller: ['modalData', function(modalData) {
var $ctrl = this;
$ctrl.greeting = 'I am a modal!'
$ctrl.modalData = modalData;
}],
controllerAs: '$ctrl',
resolve: {
modalData: $ctrl.dataForModal
}
}).result.then(function(result) {
console.info("I was closed, so do what I need to do myContent's controller now and result was->");
console.info(result);
}, function(reason) {
console.info("I was dimissed, so do what I need to do myContent's controller now and reason was->" + reason);
});
};
}
});
angular.module('app')
.component('myModal', {
template: `<div class="modal-body"><div>{{$ctrl.greeting}}</div>
<label>Name To Edit</label> <input ng-model="$ctrl.modalData.name"><br>
<label>Value To Edit</label> <input ng-model="$ctrl.modalData.value"><br>
<button class="btn btn-warning" type="button" ng-click="$ctrl.handleClose()">Close Modal</button>
<button class="btn btn-warning" type="button" ng-click="$ctrl.handleDismiss()">Dimiss Modal</button>
</div>`,
bindings: {
$close: '&',
$dismiss: '&',
greeting: '<',
modalData: '<'
},
controller: [function() {
var $ctrl = this;
$ctrl.handleClose = function() {
console.info("in handle close");
$ctrl.$close({
result: $ctrl.modalData
});
};
$ctrl.handleDismiss = function() {
console.info("in handle dismiss");
$ctrl.$dismiss({
reason: 'cancel'
});
};
}],
});
There is no need to make it more complicated by passing along the parent controller, you can just access it from within the .component that displays the modal.
Component
/**
* #ngdoc component
* #name fsad.component:video
*
* #description <fsad-video> component, in development...
*
*/
(function () {
'use strict';
angular.module('fsad').component('fsadVideo', {
bindings: {},
templateUrl: function(appConstant){return appConstant.paths.modules.fsad + 'leefloon/fsad-video.html'},
controller: controller
});
controller.$inject = ['$scope'];
function controller($scope){
var $ctrl = this;
setDataModel();
/****************************************************************/
$ctrl.ui.close = close;
/****************************************************************/
function setDataModel(){
$ctrl.ui = {};
}
function close(){
$scope.$parent.$close();
}
}
}());
Opening the modal
var modalInstance = $uibModal.open({
backdrop: 'static',
keyboard: true,
backdropClick: false,
template: '<fsad-video></fsad-video>',
windowClass: 'edit-contactenblad',
});
Since you are stating that the template is a component, the $scope.$parent will always be pointing to the modal instance. Allowing you to access the $close() function.
Passing and receiving data
If you need to pass data to the component, or receive data back from the component, you can do it like this.
var modalInstance = $uibModal.open({
backdrop: 'static',
keyboard: true,
backdropClick: false,
template: '<fsad-video method="$ctrl.method" on-viewed="$ctrl.userHasViewedVideo(time)"></fsad-ideo>',
controller: function(){
this.method = method;
this.userHasViewedVideo = function(time){}
},
controllerAs: '$ctrl',
windowClass: 'edit-medewerker',
});
Just on a side note, i'm using this structure style guide to create the component.
If you want access to the $uibModal's $close() and $dismiss() functions, along with some parent data and function binding within your component, you can pass them all along as such:
Open Modal Logic
$uibModal.open({
template: '<login close="$close()" dismiss="$dismiss()" ' +
'email="$ctrl.cookieEmail" check-login="$ctrl.ajaxLogin(user, pass)"></login>',
controller: function () {
this.cookieEmail = $cookies.get('savedEmail');
this.ajaxLogin = AjaxLoginService.login;
},
controllerAs: '$ctrl'
});
Modal Login Component
{
templateUrl: 'view/login.html',
bindings: {
email: '<',
checkLogin: '&',
close: '&',
dismiss: '&'
},
controller: function () {
var viewModel = this;
viewModel.password = '';
viewModel.submit = function () {
viewModel.checkLogin(
{ user: viewModel.email, pass: viewModel.password }
).then(function (success) {
viewModel.close();
});
}
}
}
Modal HTML
<form ng-submit="$ctrl.submit()">
<input type="text" ng-model="$ctrl.email" />
<input type="password" ng-model="$ctrl.password" />
<button type="button" ng-click="$ctrl.dismiss()">Cancel</button>
<button type="submit">Login</button>
</form>
The AngularJS 1.5 docs are a little sparse, but they show the usage of the & binding as a function wrapper: https://docs.angularjs.org/guide/component
You need to pass the parent controller to the modal component with the modal instance on it. To do that you need to append the generate HTML of the modal in the parent component
parent component
$ctrl.openModal = function(){
$ctrl.modalInstance = $uibModal.open({
template: '<your-modal></your-modal>',
appendTo : $document.find('parentComponent')
});
}
modal component
.component('yourModal', {
templateUrl: 'path/to/modal.html',
replace: true,
require: {
parent : '^parentComponent'
},
controller: ModalCtrl
});
function ModalCtrl() {
var $ctrl = this;
$ctrl.$onInit = function(){
var instance = $ctrl.parent.modalInstance;
$ctrl.items = ['item1', 'item2', 'item3'];
$ctrl.selected = {
item: $ctrl.items[0]
};
$ctrl.ok = function () {
instance.close($ctrl.selected);
};
$ctrl.cancel = function () {
instance.dismiss('cancel');
};
instance.result.then(function (selectedItem) {
$ctrl.selected = selectedItem;
}, function () {
console.log('Modal dismissed at: ' + new Date());
});
};
}
Be carefull because the required controller will only be available after the $onInit.

What is the best way to call a method in a controller from a service in angular?

I've done some research on this question both in SO and on Google as well. But none of the answers serve my purpose.
I have a modal in an HTML view, which I use to show popup notifications in my app.
I want to show some buttons ('OK', 'Cancel', 'Login', etc.,) in a div on modal which I pass dynamically as a JS object. Name of the button being the key and the callback function being the value.
Examples:
{
"Login": function(){....}
}
{
"OK": function(){...},
"Cancel": function(){...}
}
Now I pass this kind of objects to a method showPopup(message, buttonMap) in the controller of the popup modal I have in view.
message being the display message on the popup and buttonMap being the object in examples.
Controller:
angular.module('core').controller('PopupController', ['$rootScope', 'LogService', 'MessageHandlerService',
function ($rootScope, LogService, MessageHandlerService) {
var ctrl = this;
ctrl.buttonMap = {};
ctrl.btnWidth = 100;
$rootScope.$on('popup', showPopup);
function showPopup (event, message, buttonMap) {
$('#genericModalDialog .popup-content p').html(message);
ctrl.buttonMap = buttonMap;
var numberOfButtons = Object.keys(buttonMap).length;
ctrl.btnWidth = (100 - numberOfButtons*2)/numberOfButtons;
$("#genericModalDialog").modal('show');
}
ctrl.callbackFor = function callbackFor(key) {
ctrl.buttonMap[key].call(null);
};
}
]);
Service:
angular.module('core').service('PopupService', ['$rootScope', 'LogService', 'CacheService', 'MessageHandlerService',
function ($rootScope, LogService) {
this.isPopupShown = function (){
return $("#genericModalDialog").hasClass('in');
}
this.showPopup = function (message, btnMap){
$rootScope.$broadcast('popup', message, btnMap);
}
this.closePopup = function (){
$("#genericModalDialog").modal('hide');
}
}
]);
View:
<div ng-controller="PopupController as popupCtrl" class="modal fade" id="genericModalDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="vertical-alignment-helper">
<div class="modal-dialog vertical-align-center" style="width:94%;">
<div class="modal-content">
<div class="modal-body">
<br/><br/>
<div class="popup-content">
<p align="center"></p><br/>
<div class="popup-action">
<button type="button" class="btn" style="width:{{popupCtrl.btnWidth}}%; margin:1%" ng-repeat="(buttonName, callBack) in popupCtrl.buttonMap" ng-click="popupCtrl.callbackFor(buttonName)">{{buttonName}}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Since I don't want to instantiate PopupController in every other controller/service, I've written a service called PopupService which has a method that can called and will $broadcast an event called "popup" on $rootscope and I am handling this event in PopupController. Everything is triggered and working properly in this case. The only problem I face is the delay in rendering the popup on the UI, I see the message as soon as the popup is displayed but the rendering of buttons is very slow (approx 3 secs) this is because of loading of some other web page in the background.
When I searched about the issue on the internet I also found this total setup can be changed to a directive and the rendering of dynamic content (in this case the popup and buttons on it.) could be placed in a link function of the directive.
Another approach I saw was directly handling the DOM manipulation in the service which of course is not a good way.
Did I miss any other approach or a solution to this issue?
All I want to know is what would the best way be, to handling this situation which is programmatically and design wise good.
If I am not clear please let me know. I'll try explaining the problem again.
Directive would be a much better choice that the DOM manipulation. Additionally you should consider changing the usage of jQuery dialog into a https://angular-ui.github.io/bootstrap/#/modal.
Here is an example how it can be done:
1) Create the directive
app.directive('myModal', function() {
return {
restrict: 'E',
scope: {
items: "=",
message: "="
},
replace: true,
templateUrl: "directiveTemplate.html",
controller: function($scope, $uibModal, $log) {
$scope.open = function(size) {
var modalInstance = $uibModal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.items;
},
message: function() {
return $scope.message;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.button = selectedItem.name;
$scope[selectedItem.callback](selectedItem.name);
});
};
}
}
});
2) Create modalInstance directive
app.controller('ModalInstanceCtrl', function($scope, $uibModalInstance, items, message) {
$scope.items = items;
$scope.message = message;
$scope.close = function(item) {
$uibModalInstance.close(item);
};
});
3) create directive template directiveTemplate.html
<div>
{{buttonClicked}}
<br> {{button}}
<button type="button" class="btn btn-default" ng-click="open('sm')">{{message}}</button>
</div>
4) create popup template 'myModalContent.html'
{{message}}
<button ng-repeat="item in items" type="button" class="btn btn-default" ng-click="close(item)">{{item.name}}</button>
5) define you controller where you will have list of buttons and message to be displayed in the popup (for demo purpose here are two different list of items and two messages)
app.controller('ModalDemoCtrl', function($scope) {
$scope.message = "modal 1";
$scope.items = [{
name: 'item1',
callback: "test1"
}, {
name: 'item2',
callback: "test2"
}, {
name: 'item3',
callback: "test3"
}];
$scope.message2 = "modal 12222";
$scope.items2 = [{
name: 'item1222',
callback: "test1"
}, {
name: 'item2222',
callback: "test2"
}, {
name: 'item3222',
callback: "test3"
}];
$scope.test1 = function(message) {
$scope.buttonClicked = "clicked test1";
}
$scope.test2 = function(message) {
$scope.buttonClicked = "clicked test2";
}
$scope.test3 = function(message) {
$scope.buttonClicked = "clicked test3";
}
});
and in order to you the directive you need:
<div ng-controller="ModalDemoCtrl">
<my-modal items="items" message="message"></my-modal>
</br>
<my-modal items="items2" message="message2"></my-modal>
</div>
If you have directive accessed from different angular application instances, than just inject directive application into the needed one (make sure the directive and modal instance to have a their own defined module ex: var app = angular.module('myPopupDynamicModule', ['ngAnimate', 'ui.bootstrap']); and this is the one to be used for injection into other modules following this example: var app2 = angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap', 'myPopupDynamicModule']);)

Angular Modal not binding to scope properties

I have a model controller like such:
pcApp.controller("ModalInstanceController", function ($scope, $modalInstance, model) {
$scope.claim = model;
$scope.payNow = function () {
$modalInstance.close("now");
};
$scope.refuse = function () {
$modalInstance.close("later");
};
$scope.payLater = function () {
$modalInstance.dismiss("cancel");
};
});
The modal template is:
<script type="text/ng-template" id="newClaimPopup.html">
<div class="modal-header">
<h3 class="desktop">PayCaddy Claim</h3>
</div>
<div class="modal-body">
<p>{{claim.SenderFullName}} is claiming an amount of R{{claim.Amount}} for a golfing bet with him that you lost, with the message:</p>
<br />
<p>{{claim.Description}}</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="payNow()">Yes</button>
<button class="btn btn-danger" type="button" ng-click="refuse()">Refuse</button>
<button class="btn btn-warning" type="button" ng-click="payLater()">Later</button>
</div>
</script>
This is in a partial view included in _Layout.cshtml:
<div id="claim-notice-template">
#Html.Partial("_NewClaimPopupTemplate")
</div>
I show the modal using this code:
$scope.showNewClaimPopup = function (viewModel) {
$scope.claim = viewModel;
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: "newClaimPopup.html",
controller: "ModalInstanceController",
size: "sm",
resolve: {
model: function () {
return $scope.claim;
}
}
});
modalInstance.result.then(function () {
debugger;
$log.info("Modal accepted at: " + new Date());
}, function () {
$log.info("Modal dismissed at: " + new Date());
});
};
The viewModel parameter passed in to $scope.showNewClaimPopup is valid and fully populated. The resolve option for open is also working, because in ModalInstanceController I can see that the model parameter is the same valid viewmodel. Yet when the modal displays, the binding templates are all blank.
All code shown is in one MainController assigned to a div that surrounds everything, including the partial containing the modal template.
Why is the template not binding as expected?
You need to pass $scope to be used to compile modal template:
var modalInstance = $modal.open({
scope: $scope, // <--- this line is necessary
animation: $scope.animationsEnabled,
templateUrl: "newClaimPopup.html",
controller: "ModalInstanceController",
size: "sm",
resolve: {
model: function () {
return $scope.claim;
}
}
});
If you don't provide scope config then modal will create new child scope of the $rootScope, which obviously doesn't contain claim object.

Using different info messages in one Ctrl with $modal service (AngularJS)

I've a question to my problem. I'm using on each page an info button who's opening when you click on it. For the modal window I'm using an own defined service who's giving the values to the controller of the modal window. For each page exists an different info message and I want to use only one View for all info messages. But only the message for the corresponding page should be displayed. How can I define this?
Here is a code of Home view Info btn:
<button type="button" class="btn pull-right" ng-click="msgBtn()">
click on info
</button>
The same code is in Person view.
How can I tell the modalCtrl that the msgBtn() was clicked on Person view and give me the message of person info?
Here is my solution example:
//First the HomeCtrl:
$scope.info = function (message) {
modalService.infoDia(message);
};
//HomeView:
<button type="button" class="btn" ng-click="info('Home')">
Click Info
</button>
//PersonCtrl:
$scope.info = function (message) {
modalService.infoDia(message);
};
//PersonView:
<button type="button" class="btn" ng-click="info('Person')">
Click Info
</button>
//infoDia Service:
...
return {
infoDia: function (message) {
return $modal.open({
templateUrl: 'info.html',
controller: function ($scope, params) {
$scope.message = params.message;
},
resolve: {
params: function () {
return {
message: message;
}
}
}
});
}
}
//ModalView:
...
<div class="modal-body">
<p ng-if="message == 'Home'">
This is the home information dialog.
</p>
<p ng-if="message == 'Person'">
This is the person information dialog.
</p>
</div>
...
You can have a property named something like infoMsg in both homeCtrl and personCtrl and access it in you modal view using {{infoMsg}}. But this will force you to add infoMsg property to each of your controllers scope whereever you are going to use the modal. Better you can have your home and person controllers pass the message string to the modal controller through the service. This will reduce coupling and will give your code more flexibility. This is all I can tell without seeing your code.
If you just want to show a message then this is pretty easily achieved. To put #ankur's answer in a form of code, it could be something like below.
Have a Info service, taking message as a parameter.
app.factory('Info', function($modal) {
return {
show: function(message) {
$modal.open({
templateUrl: 'info-modal-template.html',
controller: function($scope, params) {
$scope.message = params.message;
},
resolve: {
params: function() {
return {
message: message
};
}
}
});
}
};
});
Then inject and call it from within your HomeCtrl.
app.controller('HomeCtrl', function(Info) {
var vm = this;
vm.info = function(message) {
Info.show(message);
};
});
Where HTML template is simply.
<div ng-controller="HomeCtrl as vm">
<button type="button"
class="btn btn-default"
ng-click="vm.info('Home message')">
Home button
</button>
</div>
Answer to your new question posted as an answer.
// Home
app.controller('HomeCtrl', function($scope, modalService) {
$scope.info = function() {
modalService.infoDia('Home');
};
});
<button type="button" class="btn" ng-click="info()">Click Info</button>
// Person
app.controller('PersonCtrl', function($scope, modalService) {
$scope.info = function() {
modalService.infoDia('Person');
};
});
<button type="button" class="btn" ng-click="info()">Click Info</button>
// Service
app.factory('modalService', function($modal) {
return {
infoDia: function(message) {
return $modal.open({
templateUrl: 'info.html',
controller: function($scope, params) {
$scope.message = params.message;
},
resolve: {
params: function() {
return {
message: message
};
}
}
});
}
};
});
<div class="modal-body">
<p>{{ message }}</p>
</div>

Categories

Resources