Closing uibModal automatically after array length equals zero - angularjs

Our team is developing an attachment widget in ServiceNow. The widget allows a user to attach documents, view them, and delete them as necessary. In order to view the attachments, we're leveraging modal windows through uibModal. If a user deletes all of the attachments and the array length reaches zero, we want the modal window to close automatically, but we can't seem to get that working.
<label>
<sp-attachment-button></sp-attachment-button>
</label>
<span ng-if="attachments.length>0" class="badge" ng-click="c.openModal()">{{attachments.length}}</span>
<script type="text/ng-template" id="modalTemplate">
<div class="panel panel-default">
<div class="panel-heading flex">
<h4 class="panel-title">Attachments</h4>
<i type="button" class="fa fa-times" style="margin-left:auto;" ng-click="c.closeModal()"></i>
</div>
<div class="panel-body">
<now-attachments-list template="sp_attachment_single_line"></now-attachments-list>
</div>
<!--<div class="panel-footer text-right">
<button class="btn btn-primary" ng-click="c.closeModal()">${Close Modal}</button>
</div>-->
</div>
</script>
Our controller looks like this:
function ($uibModal, spModal, cabrillo, $scope, $http, spUtil, nowAttachmentHandler, $rootScope, $sanitize, $window, $sce) {
var c = this;
$scope.attachments=[];
$scope.m = $scope.data.msgs;
$scope.submitButtonMsg = $scope.m.submitMsg;
if(c.options.case_sysid){
$scope.data._attachmentGUID = c.options.case_sysid;
}
var ah = $scope.attachmentHandler = new nowAttachmentHandler(setAttachments, function() {});
ah.setParams('sn_hr_core_case_workforce_admin', $scope.data._attachmentGUID, 1024 * 1024 * 24);
function setAttachments(attachments, action) {
$scope.attachments = attachments;
}
$scope.attachmentHandler.getAttachmentList();
$scope.confirmDeleteAttachment = function(attachment) {
if (cabrillo.isNative()) {
if (confirm($scope.data.msgs.delete_attachment)) {
$scope.attachmentHandler.deleteAttachment(attachment);
}
} else {
spModal.confirm($scope.data.msgs.delete_attachment).then(function() {
$scope.attachmentHandler.deleteAttachment(attachment);
if($scope.attachments.length==0){
c.modalInstance.close();
}
});
}
}
c.openModal = function() {
c.modalInstance = $uibModal.open({
templateUrl: 'modalTemplate',
scope: $scope
});
}
c.closeModal = function() {
c.modalInstance.close();
}
console.log('custom-attachments');
console.log($scope);
}
Any suggestions on how to get the modal to close automatically?

We figured it out. We have to watch the array, then close the modal after array hits zero:
$scope.$watch(function () {
return $scope.attachments;
}, function (value) {
if(value.length==0){
if(c.modalInstance){
c.modalInstance.close();
}
}
});

Related

Save Form data from Popover in Angularjs

var App = angular.module('myApp', []);
App.controller('myPopoverCtrl',
function($scope){
$scope.myPopover = {
isOpen: false,
open: function open() {
$scope.myPopover.isOpen = true;
},
close: function close() {
// alert('hi');
$scope.myPopover.isOpen = false;
}
};
$scope.SaveNotes = function() {
console.log('hi');
console.log($scope.noteText);
//getting undefined here
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app = "App">
<a uib-popover-template="'AddNote.html'"
popover-title="AddNote"
popover-trigger="'outsideClick'"
ng-controller="myPopoverCtrl"
popover-is-open="myPopover.isOpen"
ng-click="myPopover.open()">Add
</a>
</div>
<script type="text/ng-template" id="AddNote.html">
<div>
<textarea class="form-control height-auto"
ng-model="noteText"
placeholder="This is a new note" ></textarea>
<input class="btn btn-outline btn-primary"
type="button"
ng- click="SaveNotes();" value="Save">
</div>
</script>
I have web a page where i have a button and when click the button popover appears.In that popover i have textarea but when i click save button i want get the text in my controller but i am getting undefined using $scope.modelname
How can i get that data?
I think you want to use a modal rather than a popover like so, as a popover is really just to display text :-
var modalInstance = $modal.open({
animation: $rootScope.animationsEnabled,
templateUrl: 'views/myTemplate.html',
size: 'md'
}).result.then(function(result) {
$scope.result = result;
});
This is what it's designed for more info here.

Is there any way to use $uibModal and $uibModalInstance in a single controller to implement modal popup using angular with typescript?

As I am a newbie in angular with typescript I am facing a issue while implemented angular modal popup. The issue is I have one drop-down on which change I have to open a modal popup and that modal popup will have two buttons "Yes" or "No". For this I have one controller where I have injected a dependency.
export class QuestionnaireController {
static ngControllerName = 'questionnaireController';
static inject = ["$uibModal"];
constructor(private $uibModal: ng.ui.bootstrap.IModalService) {
}
public openModalPopup() {
let options: ng.ui.bootstrap.IModalSettings = {
controller: QuestionnaireController,
controllerAs:'ctrl',
templateUrl: 'app/views/Dialogbox.html',
};
this.$uibModal.open(options);
}
}
Most of my code is written in 'QuestionnaireController' and the popup is getting open using this controller but I also want to close this popup so I read a article where it was written that I have to created a new controller "ModalController " to make popup close.
export class ModalController {
static inject = ["$uibModalInstance"];
constructor(private $uibModalInstance: ng.ui.bootstrap.IModalServiceInstance) {
}
public close() {
this.$uibModalInstance.close();
}
}
Popup code is here...
<div ng-app="" id="dvModal">
<div class="modal-header">
</div>
<div class="modal-body">
<p> Evaluated result will be discarded if you continue. Are you sure you want to continue?</p>
</div>
<div class="modal-footer">
<input id="yesBtn" type="button" class="btn btn-default" ng-click="ctrl.Yes('true')" value="Yes" />
<input id="npBtn" type="button" class="btn btn-default" ng-click="ctrl.close()" value="No" />
</div>
and to close this passed Controller : ModalController in options which makes my popup closed on click of "No". But now the issue is generated here, how I again went to "QuestionnaireController" to do "Yes" functionality as "Yes" functionality is written in QuestionnaireController.
Yes, you can!
$uibModal is super flexible tool.
I'm not super familiar with Typescript, but here's my JS solution:
angular
.module('appName', ['ui.bootstrap'])
.controller('SomePageController', ['$scope', '$uibModal', '$log',
function ($scope, $uibModal, $log) {
First you want to do, is to change your openModalPopup() method:
// Instantiate the modal window
var modalPopup = function () {
return $scope.modalInstance = $uibModal.open({
templateUrl: 'blocks/modal/dialog.html',
scope: $scope
});
};
// Modal window popup trigger
$scope.openModalPopup = function () {
modalPopup().result
.then(function (data) {
$scope.handleSuccess(data);
})
.then(null, function (reason) {
$scope.handleDismiss(reason);
});
};
// Close the modal if Yes button click
$scope.yes = function () {
$scope.modalInstance.close('Yes Button Clicked')
};
// Dismiss the modal if No button click
$scope.no = function () {
$scope.modalInstance.dismiss('No Button Clicked')
};
// Log Success message
$scope.handleSuccess = function (data) {
$log.info('Modal closed: ' + data);
};
// Log Dismiss message
$scope.handleDismiss = function (reason) {
$log.info('Modal dismissed: ' + reason);
}
}
]);
Second - modal window HTML template will look like this:
<script type="text/ng-template" id="blocks/modal/dialog.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
Modal content
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="yes()">Yes</button>
<button class="btn btn-warning" type="button" ng-click="no()">No</button>
</div>
</script>
Third - pretty simple SomePage HTML (in your case - Questionnaire) View example :
<div ng-controller="SomePageController">
<button type="button" class="btn btn-default" ng-click="openModalPopup()">Open modal</button>
</div>
All together:
angular
.module('appName', ['ui.bootstrap'])
.controller('SomePageController', ['$scope', '$uibModal', '$log',
function($scope, $uibModal, $log) {
$scope.modalPopup = function() {
modal = $uibModal.open({
templateUrl: 'blocks/modal/dialog.html',
scope: $scope
});
$scope.modalInstance = modal;
return modal.result
};
$scope.modalPopupTrigger = function() {
$scope.modalPopup()
.then(function(data) {
$scope.handleSuccess(data);
},function(reason) {
$scope.handleDismiss(reason);
});
};
$scope.yes = function() {
$scope.modalInstance.close('Yes Button Clicked')
};
$scope.no = function() {
$scope.modalInstance.dismiss('No Button Clicked')
};
$scope.handleSuccess = function(data) {
$log.info('Modal closed: ' + data);
};
$scope.handleDismiss = function(reason) {
$log.info('Modal dismissed: ' + reason);
}
}
]);
<!DOCTYPE html>
<html>
<head>
<link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
</head>
<body ng-app="appName">
<div ng-controller="SomePageController">
<script type="text/ng-template" id="blocks/modal/dialog.html">
<div class="modal-header">
<h3 class="modal-title">I'm a modal!</h3>
</div>
<div class="modal-body">
Modal content
</div>
<div class="modal-footer">
<button class="btn btn-primary" type="button" ng-click="yes()">Yes</button>
<button class="btn btn-warning" type="button" ng-click="no()">No</button>
</div>
</script>
<button type="button" class="btn btn-default" ng-click="modalPopupTrigger()">Open modal</button>
</div>
<script src="https://code.angularjs.org/1.5.7/angular.min.js"></script>
<script src="https://code.angularjs.org/1.5.7/angular-animate.min.js"></script>
<script src="https://raw.githubusercontent.com/angular-ui/bootstrap-bower/master/ui-bootstrap-tpls.min.js"></script>
</body>
</html>
Well, if you are that lazy guy like me, the following will also work ;)
var objects = [{
name: "obj1",
value: 1
}, {
name: "obj2",
value: 2
}];
// Generating the modal html
var html = "<div class='modal-header'><h4 class='modal-title'>Select Object</h4></div>";
html += "<div class='modal-body'>";
html += "<select class='form-control' ng-model='selection'>";
for (var i = 0; i < objects.length; i++) {
var ob = objects[i];
html += "<option value='" + ob.value + "'>" + ob.name + "</option>";
}
html += "</select>";
html += "</div>";
html += "<div class='modal-footer'>";
html += '<button type="button" ng-click="dismissModal()" class="btn btn-default" >Close</button>';
html += '<button type="button" ng-click="closeModal()" class="btn btn-primary">Select</button>';
html += "</div>";
// Showing the modal
var objectSelectionModal = $uibModal.open({
template: html,
controller: function($scope) {
// The function that is called for modal closing (positive button)
$scope.closeModal = function() {
//Closing the model with result
objectSelectionModal.close($scope.selection);
};
//The function that is called for modal dismissal(negative button)
$scope.dismissModal = function() {
objectSelectionModal.dismiss();
};
}
});
//Processing the Result
objectSelectionModal.result.then(function(selected) {
alert(selected);
});

Angular Basic Factory Binding not updating Controllers

I'm trying to create an SPA website where I have multiple controllers that depend on an AuthService, this service is just in charge of telling all the controllers if the User is signedIn and who the user is. AFAIK since all the controllers share the same service object, the changes should propagate to them, but so far this is not happening.
My code goes as follows:
I have a NavbarController and a LoginController. The NavbarController is in charge of displaying the Login/Logout links depending on the Logged property of the AuthService. The LoginController is in charge of the login panel and calling the login on the server through the AuthService.
AuthService
(function () {
var auth = angular.module('Auth', []);
auth.factory('AuthService', ['$http', function ($http) {
var obj = {};
obj.User = {};
obj.Logged = true;
obj.GetUser = function () {
$http.get('api/Account/UserDetails')
.success(function (data) {
obj.User = data;
obj.Logged = true;
})
}
obj.Login = function (data) {
var func = $http.post('api/Account/Login', data);
func.success(function (data) {
if (data.StatusCode == 200) {
obj.GetUser();
}
});
return func;
}
obj.Logoff = function () {
$http.get('api/Account/Logoff')
.success(function () {
obj.User = {};
obj.Logged = false;
});
}
return obj;
}]);
})();
LoginController
(function () {
var login = angular.module('Login', ['LoginService','Auth']);
login.controller('LoginController', ['accountService','AuthService', function (accountService,authService) {
this.OnFocus = function(obj)
{
$(obj.target).closest(".textbox-wrap").addClass("focused");
}
this.OnBlur = function(obj)
{
$(obj.target).closest(".textbox-wrap").removeClass("focused");
}
this.loginCredentials = {};
this.Login = function () {
authService.Login(this.loginCredentials)
.success(function (data) {
alert(data.StatusCode);
})
.error(function () {
alert("error");
});
}
}]);
login.directive('hcCheckbox', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.iCheck({
checkboxClass: 'icheckbox_square-blue',
increaseArea: '20%' // optional
});
}
}
});
})();
NavbarController
(function () {
var app = angular.module('main');
app.controller('NavbarController', ['AuthService', function (AuthService) {
var self = this;
self.Logged = AuthService.Logged;
self.User = AuthService.User;
self.Logoff = function()
{
AuthService.Logoff();
}
}]);
})();
Navbar html template
<nav class="navbar navbar-default" ng-controller="NavbarController as NavbarCtrl">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="navegacion">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">HACSYS</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="navegacion">
<ul class="nav navbar-nav">
<li class="active">Home</li>
<li>Details</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li ng-hide="NavbarCtrl.Logged">
Login
</li>
<li ng-show="NavbarCtrl.Logged">
<a ng-click="NavbarCtrl.Logoff()" href=""> Logoff</a>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
I know I could use $watch to propagate changes to other controllers, but this way seems cleaner to me, I have seen other examples where this works, and I still can't figure out whats going wrong with my code
In order to ensure that changes are propagated correctly across all the nested scopes, you need to bind to an object by reference. If you bind to a primitive, it creates a copy of the value on scope, effectively breaking the linkage between the scope variables. To resolve this, bind by reference:
app.controller('NavbarController', ['AuthService', '$scope', function (AuthService, $scope) {
var self = this;
$scope.AuthService = AuthService;
self.Logoff = function()
{
$scope.AuthService.Logoff();
}
}]);
When you bind by reference, any controller where you inject AuthService will bind and propagate as expected.
app.controller('otherController', function($scope, AuthService) {
$scope.AuthService = AuthService;
});
HTML
<div ng-controller="otherController">
<div ng-show="AuthService.Logged">
User is logged in!
</div>
</div>

Bind values to UI Bootstrap Modal

I have a table with a view button, when view is clicked modal display but now I want to display certain data on the modal. I am using .html pages.
I am not sure what am I missing here
html
<td>
<span>
<input class="btn btn-sm btn-dark" data-ng-click="launch('create',client)" type="button" value="view" />
</span>
</td>
This will luanch the modal
Modal
<div class="modal fade in" ng-controller="dialogServiceTest">
<div class="modal ng-scope">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">
<span class="glyphicon glyphicon-star"></span> Client Details
</h4>
</div><div class="modal-body">
<ng-form name="nameDialog" novalidate="" role="form" class="ng-pristine ng-invalid ng-invalid-required">
<div class="form-group input-group-lg" ng-class="{true: 'has-error'}[nameDialog.username.$dirty && nameDialog.username.$invalid]">
<label class="control-label" for="username">Name:</label>
<input type="text" name="username" id="username" ng-model="client.ClientName" ng-keyup="hitEnter($event)" required="">
<span class="help-block">Enter your full name, first & last.</span>
</div>
<div>{{client.ClientName}}</div>
</ng-form>
</div><div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" ng-click="save()" ng-disabled="(nameDialog.$dirty && nameDialog.$invalid) || nameDialog.$pristine" disabled="disabled">Save</button>
</div>
</div>
</div>
</div>
Angular
angular.module('modalTest', ['ngRoute','ui.bootstrap', 'dialogs'])
.controller('dialogServiceTest', function ($scope,$http, $rootScope, $timeout, $dialogs) {
$scope.clients = []; //Array of client objetcs
$scope.client = {}; //Single client object
$scope.launch = function (which,client) {
var dlg = null;
alert(client.ClientName);
dlg = $dialogs.create('/templates/Modal.html', 'whatsYourNameCtrl', {}, { key: false, back: 'static' });
dlg.result.then(function () {
$scope.client.ClientName = client.ClientName;
});
})
.run(['$templateCache', function ($templateCache) {
$templateCache.put('/templates/Modal.html');
}]);
here is some of my code
$scope.showScreenSizePicker = function(){
$scope.actionmsg = "";
var modalWindow = $modal.open({
templateUrl: '{{url()}}/modals/modalScreenSizePicker',
controller: 'modalScreenSizePickerController',
resolve: {
titletext: function() {return "Screen Sizes: ";},
oktext: function() {return "Close";},
canceltext: function() {return "Cancel";},
labeltext: function() {return "";},
}});
modalWindow.result.then(function(returnParams) {
$scope.setViewSize(returnParams[0], returnParams[1]);
});
}
you can see i am passing variables into modal using resolve. If you want to pass values back from the modal you can grab the variable returnParms (array)
and here is my controller code:
angular.module('myWebApp').controller('modalScreenSizePickerController', function($scope, $modalInstance, titletext, labeltext, oktext, canceltext) {
$scope.titletext = titletext;
$scope.labeltext = labeltext;
$scope.oktext = oktext;
$scope.canceltext = canceltext;
$scope.customHeight = 800;
$scope.customWidth = 600;
$scope.selectCustomSize = function(width, height){
if (width < 100){ width = 100; }
if (height < 100){ height = 100; }
$scope.selectItem(width, height);
}
$scope.selectItem = function(width, height) {
var returnParams = [width, height];
$modalInstance.close(returnParams);
};
$scope.cancel = function() {
$modalInstance.dismiss();
};
});
hope my sample helps
I think what you are looking for is the resolve property you can use with the $modal service. I am not exactly sure which version of UI Bootstrap you are using, but the latest one works as follows:
var myModal = $modal.open({
templateUrl: '/some/view.html',
controller: 'SomeModalCtrl',
resolve: {
myInjectedObject: function() { return someObject; }
});
myModal.result.then(function(){
// closed
}, function(){
// dismissed
});
Then you can use the injected resolved value inside the modals controller:
app.controller('SomeModalCtrl', function ($scope, $modalInstance, myInjectedObject) {
// use myInjectedObject however you like, eg:
$scope.data = myInjectedObject;
});
You can acces the client in modal by using "$scope.$parent.client" - "$parent" give you $scope from witch the modal was open with all data.

AngularJS - animation callback / sequence

I'm just finding my way with Angular, and more importantly trying to find my way without jQuery!
I'd like to have a view that shows a loading spinner while data is fetched from the server, and when it arrives (the model will have a property of "Populated") I want the spinner to fade out, and the content to fade in.
I'm using a directive for the loading bit, and ng-show seems simple enough to switch the sections in the view.
View:
<div ng-hide="model.Populated" loading-spinner></div>
<div ng-show="model.Populated"><!-- Content goes here --> </div>
Directive:
module App.Directives {
declare var Spinner: any;
export class LoadingSpinner {
constructor() {
var directive: ng.IDirective = {};
directive.restrict = 'A';
directive.scope = { loading: '=myLoadingSpinner'},
directive.template = '<div class="loading-spinner-container"></div>';
directive.link = function (scope, element, attrs) {
var spinner = new Spinner().spin();
var loadingContainer = element.find('.loading-spinner-container')[0];
loadingContainer.appendChild(spinner.el);
};
return directive;
}
}
It's the animation I'm not sure about. In particular, I want the content to fade in once the spinner has completely faded out, and I'm not sure how to do this with a callback.
Should I attempt all the animation with CSS or expand on my directive and use javascript?
(I'm using TypeScript for anyone wondering about the syntax)
I did a quick spike for my app yesterday and this is how it can be easily done.
This uses ui.bootstrap modal dialog.
When you have a long running process such as remote service call you "raise" an event via $emit. This will bubble up to your outer most scope. Here is a sample from typeahead search functionality I spiked it against.
function autoCompleteClientName(searchValue, searchType) {
var self = this;
self.$emit("WorkStarted", "Loading Clients...");
//return promise;
if (searchType === 'name') {
return $scope.clientSearchDataService.getClientsByName(searchValue)
.then(function (response) {
self.$emit("WorkCompleted", "");
return response.results;
}, function(error) {
self.$emit("WorkCompleted", "");
console.warn("Error: " + error);
});
} else if (searchType === 'number') {
return $scope.clientSearchDataService.getClientsByNumber(searchValue)
.then(function (response) {
self.$emit("WorkCompleted", "");;
return response.results;
}, function (error) {
self.$emit("WorkCompleted", "");
console.warn("Error: " + error);
});
}
};
Then we have a "shell" controller that is the controller for outermost view, the one that hosts ng-view.
(function () {
'use strict';
// Controller name is handy for logging
var controllerId = 'shellCtrl';
// Define the controller on the module.
// Inject the dependencies.
// Point to the controller definition function.
angular.module('app').controller(controllerId,
['$scope', '$modal', shellCtrl]);
function shellCtrl($scope,$modal) {
// Bindable properties and functions are placed on vm.
$scope.title = 'shellCtrl';
$scope.$on("WorkStarted", function(event, message) {
$scope.modalInstance = $modal.open({ templateUrl: "app/common/busyModal/busyModal.html" });
});
$scope.$on("WorkCompleted", function (event, message) {
$scope.modalInstance.close();
});
}
})();
Here is the modal template
<div class="modal-dialog">
<div class="modal-content">
<img src="/images/loading.gif"width="55" height="55"/>
<h3>Loading data...</h3>
</div>
<!-- /.modal-content -->
</div><!-- /.modal-dialog -->
For this to appear you have to override some styles
<style>
.modal
{
display: block;
}
.modal-body:before,
.modal-body:after
{
display: table;
content: " ";
}
.modal-header:before,
.modal-header:after
{
display: table;
content: " ";
}
</style>
and if you need full template for modal
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
...
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
Keep in mind that this is just a spike that took me about 30 min to wire together. For more robust solution, you need to be able to keep track of number of processes started and completed etc, if you are executing multiple calls to remove service.

Resources