I am trying to pass some model data into a modal window when it opens. Its in a $http.post success and also in failure then() with some different titles and button texts. I pass multiple data to it like:
//.then(){...//same as below }
,function (response){
$scope.PostRespData = response.data.message || 'Rq Failed';
$scope.pn = $scope.PostRespData.substring(53,63);
//(status=400) response modal string
$scope.name = 'Hey there!';
//modal options
$scope.opts = {
backdrop: true,
backdropClick: true,
dialogFade: false,
keyboard: true,
templateUrl : 'alreadyexists.html',
controller : ModalInstanceCtrl,
resolve: {}
};
$scope.opts.resolve.item = function() {
return angular.copy({name:$scope.name, errmsg:$scope.PostRespData, num:$scope.pn}); // pass name to modal dialo
}
//open the modal and create a modal Isntance
var modalInstance = $uibModal.open($scope.opts);
var ModalInstanceCtrl = function($scope, $uibModalInstance, $uibModal, item) {
$scope.item = item;
$scope.ok = function () {
$uibModalInstance.close();
};
$scope.cancel = function () {
$uibModalInstance.close();
}
My HTML template looks like
<div class="modal-header">
<h1>{{item.name}}</h1>
</div>
<div ng-controller="Nav" class="modal-body">
<p>{{item.errmsg}}
View {{item.num}}
</p>
</div>
<div class="modal-footer">
<button class="btn btn-primary" ng-click="ok()">Edit Current Details</button>
<button class="btn btn-warning" ng-click="cancel()">Edit {{item.num}}</button>
</div>
I get my modal and I also get the static data but data being passed to it does not reflect. I've spent the whole day trying to make this work.
Edit: Adding current output pic.
Please help me!
As pointed out by #charlietfl, it was a hoisting issue. To use the above code, move var ModalInstanceCtrl = func.. outside the button's ng-click call of $http.
Related
I have a authentication directive that looks like this:
function loginModal(jwtHelper, responseStringProvider, loadingScreen, $timeout, $http, $route) {
return {
restrict: 'E',
scope: {
onLogin: '=method',
control: '='
},
link: linkFunc,
templateUrl: 'partials/loginModal.html',
controller: loginCtrl,
controllerAs: 'loginVm',
bindToController: true
};
function linkFunc(scope, elm, attrs) {
if (scope.loginVm.onLogin) {
scope.loginVm.onLogin();
}
scope.loginVm.externalControl = scope.loginVm.control || {};
scope.loginVm.externalControl.authenticate = scope.loginVm.authenticate;
}
function loginCtrl($scope) {
//viewmodel
var vm = this;
//bound functions
vm.authenticate = authenticate;
function authenticate(functionToRunOnLogin) {
functionToRunOnLogin(sessionStorage.token, sessionStorage.username).then(function (functionResponse) {
loadingScreen.hide();
$scope.$apply();
}, function (functionResponse) {
if (functionResponse.status === 401) {
loadingScreen.hide();
$('#invalidOperationDialog').modal({ backdrop: 'static' });
}
});
}
There is a single instance of this directive in the index page. The controller for this directive is stored in a $scope variable
index.html:
<login-modal control="vm.authenticationControl"></login-modal>
indexController.js:
vm.authenticationControl = {};
$scope.loginDirective = vm.authenticationControl;
To run a function that needs authentication another controller accesses the directive's authenticate function from the $scope variable and passes the function that needs the authentication
settingsController.js
function openAuthDialog(value) {
settingsVm.currentAction = value;
$scope.loginDirective.authenticate(authorize);
};
function authorize(token, username) {
switch (settingsVm.currentAction) {
case settingsVm.globalSettingAction:
if (_isUpdated) {
return settingsVm.saveGlobalSettings(token, username);
}
break;
case settingsVm.edsAddEditAction:
return settingsVm.edsAddEditConfirm(token, username);
break;
case settingsVm.edsDeleteAction:
return settingsVm.edsDeleteConfirm(token, username);
break;
case settingsVm.updateYellowPod:
case settingsVm.updateBluePod:
case settingsVm.updateDrillThrough:
return updateImagePath(token, username);
break;
}
}
function saveGlobalSettings(token, username) {
settingsVm.isSaving = true;
var globalSettings = getCurrentGlobalSettings();
return GlobalSettings.save(globalSettings, username, token)
.then(function () {
//success
GlobalSettings.setGlobalSettingsChanges(globalSettings);
_savedGlobalSettings = globalSettings;
_isUpdated = false;
openDialogModal('Global Settings have been updated successfully.');
},
function () {
//error
openDialogModal( 'There was an error updating Global Settings at this time. Please try again later.');
});
}
function openDialogModal( message) {
settingsVm.modalMessage = message;
$('#dialogModal').modal({ backdrop: 'static' });
}
settings.html:
<div id="dialogModal" class="modal fade" data-keyboard="false" style="z-index: 30000">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
Update Result
</div>
<div class="modal-body">
<h4>
<span ng-bind-html="settingsVm.modalMessage"></span>
</h4>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="globalSettingsVm.closeDialogModal()">OK</button>
</div>
</div>
</div>
</div>
Most of the functions that are being passed to the directive simply need a token to make a call to the back end. The directive provides that token and runs the function and the back end updates the database.
But there are a couple that need to make front end changes, which is where the problems arises.
After successfully saving the settings, the settingsVm.saveSettings function tries to set settingsVm.modalMessage, but there is no change to the page.
I have stepped through and can see the settingsVm.modalMessage get updated, so I know the function is running properly but cannot figure out why it's not reflected on the page. My suspicion is that the scope of the directive is not set up properly so when settingsVm.modalMessage gets updated it is not in the parent scope which means that instance of settingsVm.modalMessage is not bound to the html. My other thought is that I am not using $scope.$apply properly
I have tried many different scope setups for the directive but nothing works. Any advice would be appreciated
I have a $scope that needs to show the iso code on select from a list of countries.
So the countries list is being fetched a $http call factory and it populates into a scope, so far so good here
///////// get countries ////////////////
tableFactory.getCountries().then(function(data){
$scope.presentCountries = data;
});
presentCountries will be sent into typeahead where the user will type for a country and select it, like so:
<div class="countrySearchField">
<div class="row">
<h3 class="searchTitle">Pick a country</h3>
</div>
<input type="text" class="form-control search-input" ng-model="countryPicked" uib-typeahead="presentCountry.name for presentCountry in presentCountries | filter:{name: $viewValue}:startsWith" typeahead-min-length="1" typeahead-on-select="onSelectCountry($item, $model, $label)" typeahead-editable="false"/>
</div>
Once it's selected, it will call the scope function onSelectCountry.
$scope.onSelectCountry = function($item, $model, $label){
$scope.brandCountryCode = $item.iso_code_2;
// $scope.brandCountryCode = $item;
console.log($scope.brandCountryCode);
}
I can see the data,$scope.brandCountryCode, in the console from console.log, but not in the modal view(overlay view).
<div class="row subtitleModalRow">
<h5 class="unlock-description">Please check the following parameters before merging</h5>
<!-- <p class="unlock-description" ng-bind-html="overlayMessage"></p> -->
<ul>
<li>Brand Country: {{brandCountryCode}}</li>
</ul>
</div>
I don't understand why it's not showing up, but I can see the console.log output. I'm suspecting is because the scope is under a function, but I thought if you set a value into a $scope, it should be available anywhere, please correct me if I'm wrong.
Also, it could be that the overlay view is a separate html file from the typeahead html. However, this overlay view has the controller, tablePageCtrl. The controller should be present in both html files.
this.openModal = function(type) {
this.type = type;
if(type == 'mergeRequest'){
var templateUrl = 'app/views/dataMerge/overlayMsg.html';
var backdropClass = 'auth-backdrop';
var controller = 'tablePageCtrl';
}
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
controller: controller,
windowTopClass: 'auth-template',
backdrop: 'static',
backdropClass: backdropClass,
});
};
I did a test, outside the function called $scope.test = 'test' in the controller, and I see the value in the overlay view. I have other scopes based onSelect function that are running into this situation, and they all are under a $scope function.
I added an initializer, $scope.brandCountryCode = '', but it didn't do anything. If I can solve this one, then the other ones should be fine.
Not sure what's going here, does anybody have any suggestions. Still new to Angular and your help will be appreciated. Thanks!
EDIT
full service code for the overlay modal:
(function() {
'use strict';
angular
.module('com.xad.se-tools')
.service('tableModalService', function($scope, $uibModal){
this.modalInstance = null;
this.type = null;
this.openModal = function(type) {
this.type = type;
if(type == 'mergeRequest'){
var templateUrl = 'app/views/dataMerge/overlayMsg.html';
var backdropClass = 'auth-backdrop';
var controller = 'tablePageCtrl';
}
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
// controller: 'AuthCtrl',
controller: controller,
windowTopClass: 'auth-template',
backdrop: 'static',
backdropClass: backdropClass,
scope: $scope
});
};
this.closeModal = function() {
this.modalInstance.dismiss('cancel');
}
});
})();
I think that you should set scope config option in your modal instance to set scope used in modal:
this.modalInstance = $uibModal.open({
animation: true,
templateUrl: templateUrl,
controller: controller,
windowTopClass: 'auth-template',
backdrop: 'static',
backdropClass: backdropClass,
scope: $scope
});
Please check angular bootstrap modal documenation here for more details.
I run the factory, and excecute the function "fn_generarPopupConfirm()" in any controller.
in this method, I have now created a template. This template has a buton that has an ng-click, which calls an existing function inside the same factory. In my example I have this:
<button type="submit" class="btn btn-primary" ng-click="fn_confirmar()">
How can I do it to call it ("oElim.fn_confirmar()")?, without needing to define a function in which I define a $scope object, to call the function needed. this function is present in the same factory.
controller: function($scope){
$scope.fn_confirmar=function(){
oElim.fn_confirmar();
}
},
I need the function to be called directly "oElim.fn_confirmar()" with the ng-click event. it's possible?
this is my factory.
.factory('eliminar', function($state,$rootScope,$uibModal,popup_generico) {
var oElim= {};
oElim.fn_generarPopupConfirm = function(objeto,array,titulo,contenido) {
$rootScope.modalInstances.push($uibModal.open({
template: '<form id="form_popup" class="form-horizontal"><div class="modal-header">
<h3 class="modal-title"><button type="submit" class="btn btn-primary"
ng-click="fn_confirmar()">
OK</button></div></form>',
controller: function($scope){
$scope.fn_confirmar=function(){
oElim.fn_confirmar();
}
},
backdrop: 'static'
}));
}
oElim.fn_confirmar = function(){
var index = oElim.array.indexOf(oElim.objeto);
oElim.array.splice(index, 1);
popup_generico.fn_CerrarModal();
}
return oElim;
})
I do not believe that this is possible within angular, as the template is a string that will be interpolated within the context of the controller, not the factory.
If you really want to have scope access to the oElim factory without injecting it into a controller, you could bind the oElim object directly to the $rootScope, giving you prototypal access to its methods within the template "$rootScope.oElim.fn_confirmar()" or equivalently just "oElim.fn_confirmar()" from any template you define in your angular app.
As I know it's not possible, but you can do the following:
controller: function($scope){
$scope.fn_confirmar = oElim.fn_confirmar;
},
or add the object to $rootScope
.factory('eliminar', function($state,$rootScope,$uibModal,popup_generico) {
var oElim= {};
$rootScope.oElim = oElim;
oElim.fn_generarPopupConfirm = function(objeto,array,titulo,contenido) {
$rootScope.modalInstances.push($uibModal.open({
template: '<form id="form_popup" class="form-horizontal"><div class="modal-header">
<h3 class="modal-title"><button type="submit" class="btn btn-primary"
ng-click="oElim.fn_confirmar()">
OK</button></div></form>',
controller: Function.prototype, //Just dummy function
backdrop: 'static'
}));
}
oElim.fn_confirmar = function(){
var index = oElim.array.indexOf(oElim.objeto);
oElim.array.splice(index, 1);
popup_generico.fn_CerrarModal();
}
return oElim;
})
or better, just used built-in angular event service
.factory('eliminar', function($state,$rootScope,$uibModal,popup_generico) {
var oElim= {};
oElim.fn_generarPopupConfirm = function(objeto,array,titulo,contenido) {
$rootScope.modalInstances.push($uibModal.open({
template: '<form id="form_popup" class="form-horizontal"><div class="modal-header">
<h3 class="modal-title"><button type="submit" class="btn btn-primary"
ng-click="$emit('fn_confirmar')">
OK</button></div></form>',
controller: Function.prototype, //Just dummy function
backdrop: 'static'
}));
}
oElim.fn_confirmar = function(){
var index = oElim.array.indexOf(oElim.objeto);
oElim.array.splice(index, 1);
popup_generico.fn_CerrarModal();
}
$rootScope.$on('fn_confirmar', oElim.fn_confirmar);
return oElim;
})
I am trying to pop up a modal to get some input, but the angular binding via ng-model seems to be read only. This is my modal markup:
<script type="text/ng-template" id="signatureWindow.html">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Signature Capture</h4>
</div>
<input type="text" width="100px" ng-model="theName" />
<div class="modal-footer">
<button ng-click="accept()" class="btn btn-primary">Accept</button>
<button ng-click="cancel()" class="btn btn-default">Cancel</button>
</div>
</script>
Then, I invoke this modal as follows:
$scope.getSignatureModal = function(signatureBase64) {
var modalInstance = $modal.open({
templateUrl: 'signatureWindow.html',
controller: 'SignatureModalController',
size: 'lg',
resolve: {
signatureData: function() {
return signatureBase64;
}
}
});
modalInstance.result.then(function(signatureData) {
alert('Signed');
signatureBase64 = signatureData;
}, function() {
alert('Canceled');
});
};
And the following controller code for the modal:
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, signatureData) {
$scope.base64 = signatureData;
$scope.thename = "NamesRus";
$scope.accept = function() {
debugger;
$modalInstance.close($scope.thename);
}
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
}
});
The modal pops up as expected, and the input has the initial value "NamesRus", but when I close the modal, invoking accept in the modal controller, $scope.thename still has it's initial value, not any new value I type when the modal is active. What could be wrong here?
NOTE: At the debugger breakpoint in accept, no matter what I type in the modal's input, theName still has the initial assigned value.
MORE: My Plunker for this works fine. It's when in-place, in an ASP.NET MVC5 project, that I get the strange behaviour.
I think that you mix up two differents scopes.
If you want several variables to be passed to and retrieved from the modal you have to mention them:
in the resolve attribute
resolve: {modalData: function (){return {signatureData:$scope.base64,name:$scope.theName}}}
pass modalData as dependencie to your controller
MlamAngularApp.controller('SignatureModalController', function($scope, $modalInstance, modalData)
update the modal controller $scope with modalData
$scope.signatureData = modalData.signatureData;
$scope.name=modalData.name;
invoke
$modalInstance.close({signatureData:$scope.base64,name:$scope.theName})
reassign the original $scope with the result of promise
modalInstance.result.then(function (data) {
$scope.base64 = data.signatureData;
$scope.thename=data.name;
}
take a look at this plunker forked from ui-boostrap modal orginal example: http://plnkr.co/edit/whLSYt?p=info
I'm trying to use the Angularjs-UI Modal into my application and I'm a little confused as to what goes where. I have a button for new groups which calls a model, the models controller is in another file. I'm trying to call the newGroupCtrl within the groupCtrl but it's returning undefined.
HTML for new group button:
<button type="button" class="delGroupBtn" ng-click="newGroup()">New Group</button>
In my groupCtrl I have this newgroup() function:
$scope.newGroup = function (){
var modalForm = '/Style%20Library/projects/spDash/app/partials/newGroup.html';
var modalInstance = $modal.open({
templateUrl: modalForm,
backdrop: true,
windowClass: 'modal',
controller: newGroupCtrl,
resolve:{
newGroup:function (){
return $scope.newGroup;
}
}
});
};
Then I've got my newGroup.html (the modal) which is where the user would enter the groups name, description, owner:
<div class="modal-header">
<form>
<label for="groupName">Group Name:
<input id="groupName" type="text" ng-model='newGroup.name'>
</label>
<hr>
<label for="groupDesc">Group Description:
<input id="groupDesc" type="text" ng-model='newGroup.desc'>
</label>
<hr>
<label for="groupOwner">Group Name:
<select id="groupOwner" type="text" ng-model=''></select>
</label>
<div class="modal-footer">
<button class="btn btn-warning" ng-click="cancel()">Cancel</button>
<button class="btn primary-btn" type="button" ng-click="newGroup()">Create</button>
</div>
</form>
</div>
Here's the newGroupCtrl:
spApp.controller('newGroupCtrl',
function newGroupCtrl($scope, $modalInstance){
$scope.newGroup = {
name:null,
desc:null
};
$scope.submit = function(){
console.log('creating new group');
console.log(newGroup);
$modalInstance.dismiss('cancel');
}
$scope.cancel = function (){
$modalInstance.dismiss('cancel');
};
$modalInstance.result.then(function (){
groupService.newGroup($scope.newGroup);
}, function (){
console.log('No new group created.');
});
}
);
I've injected the group and newGroup controllers with my group service which is where I'm trying to get the information from the model to the groupService function to make the AJAX call to my server and create the new group. It seems like I'm repeating myself in both controllers with the $model.open({})
Here's a plunker
When controller is defined as part of the angular module (using .controller() method) than it must be referenced as a string.
When controller is defined as a simple JS function, then it must be referenced through a variable.
In you modal configuration object, you referenced newGroupCtrl as a variable, not as a string.
...
controller: newGroupCtrl,
...
vs
...
controller: 'newGroupCtrl',
...
But your controller is defined with angular.module().controller():
spApp.controller('newGroupCtrl',
function newGroupCtrl($scope, $modalInstance){...})
To fix, you need to put the name of the controller in quotes OR define the controller as a simple standalone JS function.
So, it's either this:
...
controller: 'newGroupCtrl',
...
spApp.controller('newGroupCtrl',
function newGroupCtrl($scope, $modalInstance){...})
OR this:
...
controller: newGroupCtrl,
...
function newGroupCtrl($scope, $modalInstance){...}