I want to pass a whole object to modal, so I can view all of its attributes there. Right now I have items that look like this:
$scope.items = [{ Title: title, Id: id }]
In my html page i am using a 'ng-repeat', something like this:
<tr ng-repeat="item in items | filter:search">
<td> {{item.Title}} </td>
<td> {{item.Id}} </td>
<td> <button ng-controller="ModalDemoCtrl" type="button" ng-click="viewItem(item)" class="btn btn-primary">View Item</button> </td>
and my modal html page:
<div class="modal-header">
<h3>{{Title }}</h3>
</div>
<div class="modal-body">
<p>{{ Id }}</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">OK</button>
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
Only enough to see if we can get our two values.
But I have no idea how my modalController should look like, I can't seem to pass the whole item (with only title, and id so far) to the modal view.
I have followed the example on the angular bootstrap github page when making my controller:
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.viewItem = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
items: function () {
return $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I am aware that this wont work, I can't type my actual controller at this moment, I will update later tonight with it. Any thoughts though on how this can be achieved?
If I understand correctly what you are after you don't need to pass the whole list of items to your modal, you should only pass the item the user has clicked on. This is actually the item that is passed as argument to your viewItem function, so you would have something like this:
$scope.viewItem = function (selectedItem) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
item: function () {
return selectedItem;
}
}
});
}
and then in your modal controller:
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance, item) {
$scope.title = item.title;
$scope.id = item.id
});
Or you can just assign the item being passed to your modal controller to the $scope.item variable and use {{item.title}} and {{item.id}} in your HTML instead.
I think you don't need to create another controller, you can use your current. And show modal window with directives ng-show or ng-if. It's not necessary use two controllers for one view. One controller - one view.
If you want create modal window and use it in different parts of your project, you can create directive and use it to create modal windows your application.
When creating the Items function I would pass an object so then you can call it in your modal ctrl like so:
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $modal, $log) {
$scope.viewItem = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
items: function () {
return myItems: $scope.items;
}
}
});
modalInstance.result.then(function (selectedItem) {
$scope.selected = selectedItem;
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modalInstance, items) {
$scope.items = items.myItems;
$scope.selected = {
item: $scope.items[0]
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I have it working in my app like this. Hope it helps
Related
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.
I've got this piece of code here
<div ng-repeat="item in items" class="col-sm-4 portfolio-item">
<script type="text/ng-template" id="myModalContent.html">
However, I can't access {{item}} within the script tags. Is there a way I can insert id and type from outside the div tags? Sorry for the newbie question.
Here is the code for the controller:
.controller('listCtrl', function($scope, $modal, $log, $stateParams, items) {
$scope.animationsEnabled = true;
$scope.open = function(size) {
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
item: function() {
return $scope.item;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.toggleAnimation = function() {
$scope.animationsEnabled = !$scope.animationsEnabled;
};
})
// Please note that $modalInstance represents a modal window (instance) dependency.
// It is not the same as the $modal service used above.
.controller('ModalInstanceCtrl', function($scope, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
items: $scope.items
};
$scope.ok = function() {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
})
You can't access item object in modal template because it's compiled in isolated scope child of the $rootScope, so there is not way for modal contents to inherit items.
What you want to do is to provide a base scope for the modal instance. Try this:
var modalInstance = $modal.open({
scope: $scope, // <--- use this scope as the base for new scope
animation: $scope.animationsEnabled,
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
item: function() {
return $scope.item;
}
}
});
In above snippet, modal service will create new child scope from the passed $scope, in which case modal scope will inherit prototypically items objects.
I have a project where I'm using the ui.bootstrap, and according to the tutorial I followed I have to set it up similar to this:
'use strict';
angular.module('academiaUnitateApp')
.controller('EntryCtrl', function ($scope, $modal) {
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'modal.html',
controller: 'ModalCtrl'
})
};
});
'use strict';
angular.module('academiaUnitateApp')
.controller('ModalCtrl', function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.delete = function () {
$modalInstance.dismiss('cancel');
};
});
<script type="text/ng-template" id="modal.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
<p class="alert alert-danger">
WARNING: By deleting the article all it's nested articles will be moved to the article holding this one.
<br/>
Do you still want to delete this article?
</p>
<button class="btn btn-primary" ng-click="delete()">Yes</button>
<button class="btn btn-primary" ng-click="cancel()">No</button>
<span ng-show="error.state" class="alert alert-danger">{{ error.message }}</span>
<span ng-show="done.state" class="alert alert-success">{{done.message}}</span>
</div>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</script>
This works find and all, but what if I want to move the $scope.delete function inside the EntryCtrl controller instead of having it in a separate controller?
You can pass in anonymous controller. That would allow you to have all the logic in a single file.
In your case it would look like this:
'use strict';
angular.module('academiaUnitateApp')
.controller('EntryCtrl', function ($scope, $modal) {
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'modal.html',
controller: [
'$scope', '$modalInstance', function($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.delete = function () {
$modalInstance.dismiss('cancel');
};
}
]
})
};
});
EDIT
You can pass variables by defining resolve function and adding variables in inner controller definition. I have used this to pass values in one-way fashion but never for two-way binding. I think you should be able to pass outer scope as well.
I don't know if it works, so be warned! :)
'use strict';
angular.module('academiaUnitateApp')
.controller('EntryCtrl', function ($scope, $modal) {
$scope.myValue = 'foobar';
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'modal.html',
controller: [
'$scope', '$modalInstance', 'outerScope', function($scope, $modalInstance, outerScope) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.delete = function () {
$modalInstance.dismiss('cancel');
};
}
],
resolve: {
outerScope: function() {
return $scope;
}
}
})
};
});
PS. Haven't tested code above, just put it together from your provided code
For more details see my answer here:
https://stackoverflow.com/a/29461685/3070052
I'm using AngularJS and BootstrapUI for my modals and I need the possibility
to open multiple modals at the same time, when I open a modal I need to put a button/link to open a secondone
Is there any way to do this?
May be I am missing smth, but there is not magic in modals: open modal = show some div with controller... The rest is done by css.
http://plnkr.co/edit/GnadPfU9OFDFfyEa11vE?p=preview
modal in modal:
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $modal, $modalInstance, items) {
$scope.items = items;
$scope.selected = {
item: $scope.items[0]
};
$scope.open = function () {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
resolve: {
items: function () {
return $scope.items;
}
}
});
};
$scope.ok = function () {
$modalInstance.close($scope.selected.item);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
I have an app that uses dynamic routing to load modal windows, with each window displaying one "activity" object from a JSON file. Currently I'm using the route to determine which card is visible, and then calling a data service to fill in the data based on a match with the route name.
But I don't like this solution, as it isolates my current card from the context of the array. I would far prefer to be able to pass the card object from the template, because then I will know what the $index is, which I can then use to navigate to "prev" and "next" elements.
If I have this mark-up:
<div ng-controller="MenuCtrl">
<ul class="menu">
<li ng-repeat="card in cards">
{{ card.shortName }}
</li>
</ul>
</div>
Which triggers this $routeProvider:
$routeProvider
.when('/page/:name', {
templateUrl : 'modalContainer',
controller : 'ModalContainerCtrl'
})
Which brings up this controller:
.controller('ModalContainerCtrl',['$scope', '$modal', '$route', function($scope, $modal, $route) {
var modalInstance = $modal.open({
templateUrl : '../assets/templates/modal.html',
controller: 'ModalCtrl'
});
$scope.activity = $route.current.pathParams.name;
console.log($scope.activity);
//Modal controls
$scope.close = function () {
console.log("close!");
$modalInstance.close();
};
}])
is there any way I can pass the card object to ModalContainerCtrl via this routing or any other means?
You can pass a scope object to a modal window controller using the resolve (definition of resolve Members that will be resolved and passed to the controller as locals; it is equivalent of the resolve property for AngularJS route). So in your case it can be done like this:
.controller('ModalContainerCtrl',['$scope', '$modal', '$route', function($scope, $modal, $route) {
//your card object
$scope.card = '';
var modalInstance = $modal.open({
templateUrl : '../assets/templates/modal.html',
controller: 'ModalCtrl',
resolve: {
card: function () {
return $scope.card;
}
}
});
$scope.activity = $route.current.pathParams.name;
console.log($scope.activity);
//Modal controls
$scope.close = function () {
console.log("close!");
$modalInstance.close();
};
}])
and in the modal controller:
var ModalCtrl = function ($scope, $modalInstance,card) {
//Modal controls
//card now can be used over here
$scope.close = function () {
console.log("close!") //This will now run
modalInstance.close();
};
}