Display angular-ui Modal with data from a REST request (with delay) - angularjs

I want to open an Modal Dialog (angular-ui), but when the open() function is called, the data are not available. Data are loaded by a resource call so there's a delay.
I tried to play with the promise opened, but data are not changed.
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: ModalInstanceCtrl,
resolve: {
mydata: function() {
return "Loading...";
}
}
});
modalInstance.opened.then(function() {
$scope.mydata = $scope.loadData();
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.loadData = function() {
$timeout( function(){
$log.info("data loaded");
return "data loaded...";
}, 3000)
};
Something is missing in my understanding between the resolve property, the modal promises and the deferred loading.
(I would like to use restangular to load the resource).
Here is the sample :
http://plnkr.co/edit/Mj6JolD06DUJd6N6ECYi
Thanks in advance for any clue

You are mostly there. The problem is in the way you coded the loadData function. Since you are doing an asynchronous call you can't just do a return of data like that. Instead, what you can do is in your loadData you can call a function on the modalInstance that will set a value in the $scope of the modal.
So in your ModalInstanceCtrl you can add a function like this:
$modalInstance.setMyData = function(theData) {
$scope.mydata = theData;
};
And then you can call that in your loadData like this:
$scope.loadData = function(aModalInstance) {
$log.info("starts loading");
$timeout(function() {
$log.info("data loaded");
aModalInstance.setMyData("data loaded...");
}, 3000);
};
You also need to make sure that you pass the instance of the modal when you call loadData:
modalInstance.opened.then(function() {
$scope.loadData(modalInstance);
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
I created an updated plunk so you can see how it works: http://plnkr.co/edit/M7qfegYIOqOQekoxLaj5?p=preview

Related

AngularJS Factory object not being updated on controller and view

I'm with a problem with binding an object of a Factory and a Controller and it's view.
I am trying to get the fileUri of a picture selected by the user. So far so good. The problem is that I am saving the value that file to overlays.dataUrl. But I am referencing it on the view and it isn't updated. (I checked and the value is actually saved to the overlays.dataUrl variable.
Here goes the source code of settings.service.js:
(function () {
"use strict";
angular
.module("cameraApp.core")
.factory("settingsService", settingsService);
settingsService.$inject = ["$rootScope", "$cordovaFileTransfer", "$cordovaCamera"];
function settingsService($rootScope, $cordovaFileTransfer, $cordovaCamera) {
var overlays = {
dataUrl: "",
options: {
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
destinationType: Camera.DestinationType.FILE_URI
}
};
var errorMessages = [];
var service = {
overlays: overlays,
selectOverlayFile: selectOverlayFile,
errorMessages: errorMessages
};
return service;
function selectOverlayFile() {
$cordovaCamera.getPicture(overlays.options).then(successOverlay, errorOverlay);
}
//Callback functions
function successOverlay(imageUrl) {
//If user has successfully selected a file
var extension = "jpg";
var filename = getCurrentDateFileName();
$cordovaFileTransfer.download(imageUrl, cordova.file.dataDirectory + filename + '.' + extension, {}, true)
.then(function (fileEntry) {
overlays.dataUrl = fileEntry.nativeURL;
}, function (e) {
errorMessages.push(e);
});
}
function errorOverlay(message) {
//If user couldn't select a file
errorMessages.push(message);
//$rootScope.$apply();
}
}
})();
Now the controller:
(function () {
angular
.module("cameraApp.settings")
.controller("SettingsController", SettingsController);
SettingsController.$inject = ["settingsService"];
function SettingsController(settingsService) {
var vm = this;
vm.settings = settingsService;
activate();
//////////////////
function activate(){
// Nothing here yet
}
}
})();
Finnally on the view:
<h1>{{vm.settings.overlays.dataUrl}}</h1>
<button id="overlay" class="button"
ng-click="vm.settings.selectOverlayFile()">
Browse...
</button>
Whenever I change the value in the factory, it doesn't change in the view.
Thanks in advance!
Unfortunately Factories in angularjs are not meant to be used as two way bindings. Factories and Services are only singletons. They are only there to be used when called.
Ex Factory:
app.factory('itemFactory', ['$http', '$rootScope', function($http, $rootScope) {
var service = {};
service.item = null;
service.getItem = function(id) {
$http.get(baseUrl + "getitem/" + id)
.then(function successCallback(resp) {
service.item = resp.data.Data;
$rootScope.$broadcast("itemready");
}, function errorCallback(resp) {
console.log(resp)
});
};
return service;
}]);
I use the $broadcast so if I call getItem my controller knows to go get the fresh data.
Ex Directive:
angular.module("itemApp").directive("item", ['itemFactory', '$routeParams', '$location', '$rootScope', '$timeout', function (itemFactory, $routeParams, $location, $rootScope, $timeout) {
return {
restrict: 'E',
templateUrl: "components/item.html",
link: function (scope, elem, attr) {
scope.item = itemFactory.item;
scope.changeMade = function(){
itemFactory.getItem(1);
}
scope.$on("itemready", function () {
scope.item = itemFactory.item;
})
}
}
}]);
So as you can see in my code above anytime I need a fresh item I use $broadcast and $on to update my service and directive. I hope this makes sense, feel free to ask any questions.
As pointed by Ohjay44, the factory is not updated on the view. The way to do it is using a directive (also as Ohjay44 said). To use $broadcast, $emit and $on and keep the encapsulation I did what is recommended by John Papa's Angular Style Guide: created a factory (in my case a named it comms).
Here goes the newly created directive (overlay.directive.js):
(function () {
angular
.module('cameraApp.settings')
.directive('ptrptSettingsOverlaysInfo', settingsOverlaysInfo);
settingsOverlaysInfo.$inject = ["settingsService", "comms"];
function settingsOverlaysInfo(settingsService, comms) {
var directive = {
restrict: "EA",
templateUrl: "js/app/settings/overlays.directive.html",
link: linkFunc,
controller: "SettingsController",
controllerAs: "vm",
bindToController: true // because the scope is isolated
};
return directive;
function linkFunc(scope, element, attrs, vm) {
vm.overlays = settingsService.overlays;
comms.on("overlaysUpdate", function (event, overlays) {
vm.overlays = overlays;
});
}
}
})();
I created overlay.directive.html with:
<div class="item item-thumbnail-left">
<img ng-src="{{vm.overlays.dataUrl}}">
<h2>{{vm.overlays.dataUrl}}</h2>
</div>
And finally I put an $emit on the settingsService where the overlay is updated:
(function () {
"use strict";
angular
.module("cameraApp.core")
.factory("settingsService", settingsService);
settingsService.$inject = ["comms", "$cordovaFileTransfer", "$cordovaCamera"];
function settingsService(comms, $cordovaFileTransfer, $cordovaCamera) {
var overlays = {
dataUrl: "",
options: {
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
destinationType: Camera.DestinationType.FILE_URI
}
};
var errorMessages = [];
var service = {
overlays: overlays,
selectOverlayFile: selectOverlayFile,
errorMessages: errorMessages
};
return service;
function selectOverlayFile() {
$cordovaCamera.getPicture(overlays.options).then(successOverlay, errorOverlay);
}
//Callback functions
function successOverlay(imageUrl) {
//If user has successfully selected a file
var extension = "jpg";
var filename = getCurrentDateFileName();
$cordovaFileTransfer.download(imageUrl, cordova.file.dataDirectory + filename + '.' + extension, {}, true)
.then(function (fileEntry) {
overlays.dataUrl = fileEntry.nativeURL;
// New code!!!!
comms.emit("overlaysUpdated", overlays);
}, function (e) {
errorMessages.push(e);
});
}
function errorOverlay(message) {
//If user couldn't select a file
errorMessages.push(message);
//$rootScope.$apply();
}
}
})();
I used an $emit instead of a broadcast to prevent the bubbling as explained here: What's the correct way to communicate between controllers in AngularJS?
Hope this helps someone else too.
Cheers!

Pass data from modal to another page in angularjs

I need to pass data from modal to another page. Could you please help me to accomplish this task?
$scope.productdetails = function (size,selectedproduct)
{
var modalInstance = $uibModal.open({
templateUrl: 'ProductDetails.html',
controller: function ($scope, $uibModalInstance, product) {
$scope.product = product;
$scope.buynow = function (path) {
$uibModalInstance.close($scope.product);
$location.path(path); // Need to pass $scope.product to the new page
};
$scope.cancel = function () {
$uibModalInstance.dismiss('cancel');
};
},
size: size,
resolve: {
product: function () {
return selectedproduct;
}
}
});
Be sure to read the docs on $uibModal: https://github.com/angular-ui/bootstrap/tree/master/src/modal/docs
the open() method will return an object with some useful stuff in it. Currently you're not doing anything with that object, but that's where the magic is.
To pass data to the controller that opened the modal, use the modalInstance.result promise like this:
modalInstance.result.then(function(data) { /*... do something with the data*/ });
Once that is set up, you can use the $close() function that this modal library places on the scope to resolve the result promise.
var data = {info: 'information to be returned to the parent controller'};
$scope.$close(data);

Updating the $scope value

I wanted to display the added users dynamically in the dashboard.
My code is in the following way.
Controller: where the actual action triggers .
Adding the user function
$scope.addUser= function(){
modalService.addUser();
}
function init(){
// Someother functions
getUserRequests()
};
function getUserRequests() {
datacontext.getExtranetUserRequests()
.then(function (data){
vm.ExtranetUserRequest = data;
});
};
Service: modalService
addUser: function (column) {
var modalInstance = $modal.open({
templateUrl: 'app/NewExtranetSite/Popup/userModal.html',
controller: 'userModal',
});
updateUser: function(){
// updates the user
});
Controller :userModal
In the userModal.html after adding the info and on clicking save, add user function will be triggered.
function addUser(){
datacontext.saveNewExtranetuserRequest($scope.user);
};
I would like to initiate the getUserRequests() after the completion of add user in the user modal
So that the newly added user can be visble on the dashboard without refreshing the page
Let me answer u shortly.
You have a view where you are adding user details from input using one form ().
On ng-submit or ng-click action you can call one method in your that particular view's controller.
Now to display user details, you might know json. So create a blank $scope variable which will contain added user details.($scope.variable=[];)
Now on submit just hit **
$sope.variable.push({'key':value,'value':value});
**
once your object is populated with new data it will automatically displayed in the view.
5. We have just awesome ng-repeat angular's directive to show dynamical list containing objects.
6. **
ng-repeat="key in variable track by $index"
**
The $modal.open function returns a promise, so it's easy to wait for the modal to close and then execute another function. Let 'addUser' return this promise, then wait for it to finish before executing getUserRequests:
in modalService:
addUser: function (column) {
var modalInstance = $modal.open({
templateUrl: 'app/NewExtranetSite/Popup/userModal.html',
controller: 'userModal',
});
return modalInstance;
}
in controller:
$scope.addUser= function(){
modalService.addUser().then((resultReturnedFromModal) => {
getUserRequests();
});
}
Sorry for the bad post.
Let me explain briefly in this post
I would like to display the added data dynamically on the page.
I have a controller where the user addition action takes place.
(function () {
'use strict';
var controllerId = 'newUser';
angular.module('app').controller(controllerId,
['modalService', '$scope', 'dataContext', newUser]);
function newUser(modalService, $scope, dataContext) {
init();
function init() {
var extranetSiteRequestId = +$routeParams.id;
if (extranetSiteRequestId && extranetSiteRequestId > 0) {
getItem(extranetSiteRequestId);
getUserRequests();
}
}
$scope.newuserRequest = function () {
modalService.addUser();
}
function getUserRequests() {
datacontext.getExtranetUserRequests().then(function (data) {
vm.UserData = data;
});
};
}
}());
I am using a service modalService to handle the add user request.
(function (){
'use strict';
var serviceId = 'modalService'
angular.module('app').service(serviceId, ['$modal', modalService]);
function modalService($modal) {
return {
addUser: function () {
var modalInstance = $modal.open({
templateUrl: 'app/NewExtranetSite/Popup/userModal.html',
controller: 'userModal',
});
modalInstance.result.then(function (userDetails) {
if (userDetails) {
alert(userDetails) ;
};
})
},
};
}
})();
finally in the userModal controller am handling the new user added request
(function () {
'use strict';
var controllerId = 'userModal';
angular.module('app').controller(controllerId, ['$scope', '$modalInstance', 'datacontext', 'common', addUserModalFunction]);
function addUserModalFunction($scope, $modalInstance, datacontext, common) {
var vm = $scope;
vm.cancel = cancel;
vm.submit = addUser;
init();
function init() {
common.logger.log("controller loaded", null, controllerId);
common.activateController([], controllerId);
}
function cancel() {
$modalInstance.close();
}
$scope.open = function ($event, opened) {
$event.preventDefault();
$event.stopPropagation();
$scope[opened] = true;
};
function addUser() {
datacontext.saveNewExtranetuserRequest($scope.user).then(function(data){
$modalInstance.close($scope.user);
});
};
};
})();
Now the problem is I would like to add a success or then function in the newUser Controller after the modalService.adduser complete
EX: modalService.addUser().then(function(results){
});
Fikkatra Thanks for the reply but couldn't able to achieve
am very bad # angular

AngularJS Error: [$injector:unpr] Unknown provider:

I am getting an unknown provider error when attempting to launch an angular bootstrap modal window from my app by clicking on an image. I launch the same type of modal successfully elsewhere in the app.
Here is a screenshot of the error in the debugger
Is there something wrong with my controller code below? I looked at several other unknown provider error posts on stack, and to my knowledge I'm doing things properly.
app.controller('ModalInstanceCtrl', function($scope, $modalInstance, items,
removeVideoFromCart, removeLiteratureFromCart, productHasItems, cartItemHasVideos,
cartItemHasLiterature, getCartMailToBody, cartMailtoLink, logSentItems) {
$scope.items = items;
$scope.ok = function() {
$modalInstance.close($scope.test);
};
$scope.removeVideoFromCart = function (video, familyIndex, index) {
removeVideoFromCart(video, familyIndex, index);
$scope.cartMailtoLink = getCartMailToBody(); //update the mailto link body to remove video links
}
$scope.removeLiteratureFromCart = function (literature, familyIndex, index) {
removeLiteratureFromCart(literature, familyIndex, index);
$scope.cartMailtoLink = getCartMailToBody(); //update the mailto link body to remove lit links
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.productHasItems = function(index) {
return productHasItems(index);
}
$scope.cartItemHasVideos = function(index) {
return cartItemHasVideos(index);
}
$scope.cartItemHasLiterature = function (index) {
return cartItemHasLiterature(index);
}
$scope.getCartMailToBody = function () {
getCartMailToBody();
}
$scope.cartMailtoLink = getCartMailToBody();
$scope.logSentItems = function () {
logSentItems();
}
});
Thank you very much for your time. Let me know if you need more information or if I am being unclear.
I'm going to assume that app points to a declaration of that module that is defined at the root of your app e.g. in app.js:
app = angular.module('app', []);
And that you're including each dependency within your index.html e.g. after any angular scripts and app.js
<script src="yourDependency.js"></script>
In terms of the controller code itself, you don't need to assign to $scope a property that contains a function that calls the removeVideoFromCart service within your 'ModalInstanceCtrl' controller, because then you will still need to call that wrapper function again (which it currently looks like you are not doing).
You can just call the method within the controller rather than wrap it in a function e.g.
$scope.removeVideoFromCart = removeVideoFromCart(video, familyIndex, index);
or just call the service e.g. if you don't need to bind the data to the UI like sending form data that on success just redirects elsewhere (although in your case it looks like you do want to bind the data to the UI):
removeVideoFromCart(video, familyIndex, index);
It's not clear from your code where the parameters for each service originate from. Are they within the items object? e.g.
var video, familyIndex, index
vm.items = items;
video = items.video;
familyIndex = items.familyIndex;
index = items.index;
In terms of style, I prefer not assigning the module instances to a variable and instead use the setter syntax (following [John Papa's] (https://github.com/johnpapa/angular-styleguide#modules) styleguide, but also included in Todd Motto's), like so:
angular
.module('app')
.controller('ModalInstanceCtrl', ModalInstanceCtrl);
ModalInstanceCtrl.$inject['your', 'dependencies', 'go', 'here']
function ModalInstanceCtrl(/*dependencies here as parameters e.g.*/, removeVideoFromCart) {
var vm = this; // use in place of $scope and clarifies the context of the this keyword
vm.items = items;
video = items.video;
familyIndex = items.familyIndex;
index = items.index;
$scope.removeVideoFromCart = removeVideoFromCart(video, familyIndex, index);
$scope.removeLiteratureFromCart = removeLiteratureFromCart(literature, familyIndex, index);
//etc
});
NB: I would prefer a facade into all of those methods e.g. clearCartAndCloseModal('other', 'services') to hide all of the implementation details from the controller. This also makes it easier to create one controller per view that is in turn easier to test beacuse you have pushed all logic into the services. But I'm not clear from your code whether there is any relationship between each of the services.
#Claies #ritesh I was typing a long edit with responses to the questions when I happened upon my solution. I had multiple functions that opened modal windows using ModalInstanceController. For example, here are two:
$scope.open = function(size) {
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return $scope.selectedVideo3;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
$scope.openCart = function(size) {
var modalInstance = $modal.open({
templateUrl: 'myAttachmentModalContent.html',
controller: 'ModalInstanceCtrl',
size: size,
resolve: {
items: function() {
return "";
},
removeVideoFromCart: function() {
return $scope.removeVideoFromCart;
},
removeLiteratureFromCart: function() {
return $scope.removeLiteratureFromCart;
},
productHasItems: function() {
return $scope.productHasItems;
},
cartItemHasVideos: function() {
return $scope.cartItemHasVideos;
},
cartItemHasLiterature: function() {
return $scope.cartItemHasLiterature;
},
getCartMailToBody: function() {
return $scope.getCartMailToBody
},
cartMailtoLink: function() {
return $scope.cartMailtoLink
},
logSentItems: function () {
return $scope.logSentItems;
}
}
});
modalInstance.result.then(function(selectedItem) {
$scope.selected = selectedItem;
}, function() {
$log.info('Modal dismissed at: ' + new Date());
});
};
I only use most of the dependencies for ModalInstanceController in the openCart function, so I didn't add all of the dependency function declarations to my other open methods (You can see in the resolve for the $scope.open method above I only declare items and no functions).
I needed to declare all of these functions like I did in $scope.openCart and it fixed my problem.
Thank you for reaching out.
My case was UpperCase LowerCase problem in the injected service name.

How to get data from http request in angular ui bootstrap model

I am trying to get data from http request inside angular ui bootstrap modal here is my modal code
$scope.open = function (size, id) {
$scope.hallName=[];
var modalInstance = $modal.open({
templateUrl: 'video_gallery.html',
controller: HomeCtrl1,
size: size,
resolve:{
hallId:function(){
$http({
url:"../home/get_hall_name/"+id,
method: "POST"
}).success(function (data) {
$scope.hallName=data.hall_name;
alert($scope.hallName);
}).error(function(){
console.log("Something went wrong get_RegisteredHalls");
});
var hall=$scope.hallName;
return hall;
}
}
});
};
var HomeCtrl1 = function ($scope, $modalInstance, hallName) {
$scope.hallName = hallName;
$scope.selected = {};
$scope.ok = function () {};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
Here is what I am trying to get hall_name
<script type="text/ng-template" id="video_gallery.html">
<div class="modal-header">
<h4 class="modal-title" style="text-align:center;">Videos of {{hallName}} Hall</h4>
</div>
</script>
I am getting hall_name in alert but cant able to get that in view. please help me to save my day. Thanks in advance.
In order to wait for the http promise to resolve, you need to return it from your function:
hallId:function(){
return $http({
url:"../home/get_hall_name/"+id,
method: "POST"
}).success(function (data) {
}).error(function(){
console.log("Something went wrong get_RegisteredHalls");
});
}
Then in your controller you get this resolved promise:
var HomeCtrl1 = function ($scope, $modalInstance, hallId) {
$scope.hallName = hallId.data.hallName;
$scope.selected = {};
$scope.ok = function () {};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
Make sure you have the same name in both the resolve function and the controller (hallId in this case)
There's a couple of confusions.
First, between resolve and model $scope
from documentation,
scope - a scope instance to be used for the modal's content (actually the $modal service is going to create a child scope of a provided scope). Defaults to $rootScope
resolve - members that will be resolved and passed to the controller as locals; it is equivalent of the resolve property for AngularJS routes
Then, you have 2 options to "pass data" to your modal :
Using resolve. If I understand correctly, following code :
$scope.open = function (size, id) {
var modalInstance = $modal.open({
resolve:{
hallId: function() {
return "Walter white";
}
}
});
}
var HomeCtrl1 = function ($scope, $modalInstance, hallId) {
//hallId == "Walter white"
}
injects a hallId variable inside your controller. So inside resolve you provide the code to fill hallId and you get it in your controller, injected as hallId.
Using scope. For example
$scope.open = function (size, id) {
var modalScope = parent.$new();
modalScope.whatever = "Jessee Pinkman";
var modalInstance = $modal.open({
scope: modalScope
});
};
var HomeCtrl1 = function ($scope, $modalInstance) {
//$scope.whatever == "Jessee Pinkman"
}
Then, between synchronous and asynchronous
Inside resolve, you are making an asynchronous $http call, but return synchrounously the expected result. Therefore, this piece of code
var hall=$scope.hallName;
return hall;
is executed before this one
.success(function (data) {
$scope.hallName=data.hall_name;
alert($scope.hallName);
})
I am getting hall_name in alert but cant able to get that in view
Now you understand why.

Resources