Close angular modal - javascript

I am new in angularJs and I have an application that using angular modal and jqGrid. (I know that this is not good. But for some reason I must work with two, now.)
My modal content loads with a templateUrl.
<div ng-controller="SearchPerson as ctrl">
<div class="modal-body">
<table id="list"></table>
<div id="pager"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-link" data-dismiss="modal" ng-click="$close()">CLOSE</button>
<button type="button" class="btn btn-primary" ng-click="ctrl.CLOSE()">Save changes</button>
</div>
</div>
and for create I have this code:
$(function () {
$("#list").jqGrid({
url: "/Home/List",
datatype: "json",
mtype: 'GET',
colNames: [" ", "FirstName", "LastName"],
colModel: [
{
width: 30,
sortable: false,
formatter: function (cellvalue, options, rowObject) {
return 'SELECT';
},
},
{
name: "FirstName",
width: 60,
},
{
name: "LastName",
width: 90
}
],
jsonReader: {
page: "Page",
total: "Total",
records: "Records",
root: "Rows",
repeatitems: false,
cell: "cell",
id: "id",
userdata: "userdata",
},
pager: "#pager",
});
});
and for open modal I write this code:
angular.module('AngularModal', ['ui.bootstrap']);
angular.module('AngularModal').controller('ModalDemoCtrl', function ($scope, $modal) {
$scope.open = function (size) {
var modalInstance = $modal.open({
templateUrl: '/Home/SearchGrid',
controller: 'ModalInstanceCtrl',
size: size,
backdrop: 'static',
});
modalInstance.result.then(function () {
//
}, function () {
//
});
};
});
angular.module('AngularModal').controller('ModalInstanceCtrl', function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});
angular.module('AngularModal').controller('SearchPerson', function ($http, $modalStack) {
});
What I want is: when I click SELECT link in my grid, alert FirstName and then close the modal.
How to do this?
It is noteworthy that when I click CLOSE button, modal close. And also when I click "Save changes" buton, the modal will close.
But when I click SELECT link, modal does not close. While I write both ng-click of SELECT and "Save Change" for it.

Your code would definitely not work. For ng-click, the context of the expression is the current $scope, while for href="javascript:expression" the context is the global, or saying window.
For your situation, I would definitely suggest you try some other grid libraries which are more compatible with AngularJS (like http://angular-ui.github.io/ng-grid/ or http://ui-grid.info/). That may save tons of time.
Otherwise, if you really really insist on using jqGrid, you may need to wrap it into a directive manually by yourself. In your application, you may need to consider how to "angularize" the html snippet generated for the formatter function.
formatter: function (cellvalue, options, rowObject) {
var html = '<a ng-click="ctrl.Select()">SELECT</a>';
var linkFunc = $compile(html);
linkFunc(scope); // the scope in which you have the `ctrl.Select()` function.
return html;
},
$compile: https://docs.angularjs.org/api/ng/service/$compile
Then you will be able to call angular scope functions.
However, in my opinion, this is not a good practice. If any possible, find a replacement library, please.

Related

how to work with data from another controller in modal window?

I have a question about how can I get data from a controller and put it into a scope of another controller which is modal window...
I will explain:
here is my event, which opens dialog window (angular-strap modal):
openDialog () {
this.$modal({
show: true,
html: true,
placement: 'center',
type: 'large',
templateUrl: 'tmpl.html',
controller: myController
});
}
this is my modal window template (tmpl.html):
<div class="modal-body" id="modal-body">
<my-directive></my-directive>
</div>
as I use webpack, I initialize a module with my directive in main index.js
here is a small example of my template of a directive:
<span class="some-name">{{$ctrl.num}} </span>
here is my another controller:
constructor($scope) {
super($scope);
this.$scope = $scope;
this.num = 10;
}
So, as you can see there will be 10 in the span, I also need to get data from another controller, which stores i.e. data = ['asd', 'apple']; and so on
is there a better to do that?
If I understand your question correctly, you can pass scope from the parent controller to the modal by adding scope to your modal options. See below:
this.$modal({
show: true,
html: true,
placement: 'center',
type: 'large',
templateUrl: 'tmpl.html',
controller: myController,
scope: $scope
});
Is that all that you're asking?
You can use resolve property from modal
openDialog () {
this.$modal({
show: true,
html: true,
placement: 'center',
type: 'large',
templateUrl: 'tmpl.html',
controller: myController,
resolve : {
objData{
dataFromCtrl : $scope.dataFromAnoterCtrl// you can fetch data here either from parent scope or factory/service.
}
}
});
}
And then in myController , you can get the this data
.controller('myController',function(objData){
console.log(objData.dataFromCtrl); // you can get data from another controller in this modal controller.
})

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']);)

$scope passing has a Delay

I'm building an Angular pop-up system for multiple purposes. The way it works is that I have a directive called bitPopup which three variables get passed on to (type, action and data) as shown below:
index.html
<bit-popup type="popup.type" action="popup.action" data="popup.data"></bit-popup>
popup.js
app.directive('bitPopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
[***]
}
}
The popup controller then loads a different directive based on the type:
popup.html (The HTML template shown above)
<div class="pop-up" ng-class="{visible: visible}" ng-switch="type">
<bit-false-positive-popup ng-switch-when="falsePositive" type="type" action="action" data="data"></bit-false-positive-popup>
</div>
false_positives.js (Containing the bitFalsePositivePopup directive)
[...]
scope: {
type: '=',
action: '=',
data: '='
}
[...]
And then the html template for the bitFalsePositivePopup directive displays some properties from data.
Now the way I'm triggering a pop-up works like this:
From a template inside a directive containing the bitPopup directive i'll change $scope.popup's type, action and data.
I'll do $scope.$broadcast('showPopup');
The bitPopup directive will react because of $scope.$on('showPopup', [...]}); and makes the pop-up visible.
Now this really weird thing occurs where it works on the first try (the pop-up opens with the correct data information), but after the first try it will display the data from the previous try.
Now what's even weirder is that I tried logging the information on the first try and what I found out is that:
$scope.popup at index.html just before calling $scope.$broadcast('showPopup'); displays the right information.
$scope.data at the bitPopup directive displays null
$scope.data at the bitFalsePositivePopup directive displays the right information.
On the second try:
$scope.popup at index.html is correct again.
$scope.data at the bitPopup directive displays the information from the previous attempt
The same holds for the bitFalsePositivePopup directive.
Another weird thing is that when I use $scope.$apply() it does work correctly, only it displays the $apply already in progress error. I know I shouldn't use $scope.$apply() in this case, because it's all Angular events. But how is it possible that the passed scope is always a step behind?
Am I doing something wrong to begin with?
EDIT:
Because of amahfouz's answer I decided to post some more code for clarification. I left out some unimportant details for more clear reading.
index.html
<div class="falsePositives" ng-controller="falsePositives">
<i class="fa fa-minus color-red" ng-click="triggerPopup('falsePositive', 'delete', {detection: getDetection(row.detection, row.source), source: row.source, triggers: row.triggers, hash: row.hash, date: row.date})"></i>
<i class="fa fa-pencil" ng-click="triggerPopup('falsePositive', 'edit', {detection: getDetection(row.detection, row.source), source: row.source, triggers: row.triggers, hash: row.hash, date: row.date})"></i>
<bit-popup type="popup.type" action="popup.action" data="popup.data"></bit-popup>
</div>
index.js
var app = require('ui/modules').get('apps/falsePositives');
app.controller('falsePositives', function ($scope, $http, keyTools, bitbrainTools, stringTools) {
function init() {
$scope.getDetection = getDetection;
$scope.popup = {
type: null,
action: null,
data: null
};
}
function getDetection(hash, source) {
return {
'ids': 'BitSensor/HTTP/CSRF',
'name': 'CSRF Detection',
'description': 'Cross domain POST, usually CSRF attack',
'type': [
'csrf'
],
'severity': 1,
'certainty': 1,
'successful': false,
'input': ['s'],
'errors': []
};
}
$scope.triggerPopup = function (type, action, data) {
$scope.popup = {
type: angular.copy(type),
action: angular.copy(action),
data: angular.copy(data)
};
test();
$scope.$broadcast('showPopup');
};
function test() {
console.log('$scope.popup: ', $scope.popup);
}
}
popup.html
<div class="pop-up-back" ng-click="hidePopup()" ng-class="{visible: visible}"></div>
<div class="pop-up" ng-class="{visible: visible}" ng-switch="type">
<bit-false-positive-popup ng-switch-when="falsePositive" type="type" action="action" data="data"></bit-false-positive-popup>
</div>
popup.js
var app = require('ui/modules').get('apps/bitsensor/popup');
app.directive('bitPopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
controller: function ($scope) {
$scope.visible = false;
$scope.$on('showPopup', function () {
console.log('$scope.data: ', $scope.data);
$scope.visible = true;
});
$scope.$on('hidePopup', function () {
hidePopup();
});
function hidePopup() {
$scope.visible = false;
}
$scope.hidePopup = hidePopup;
}
};
});
false_positives.js
var app = require('ui/modules').get('apps/bitsensor/falsePositives');
app.directive('bitFalsePositivePopup', function () {
return {
restrict: 'E',
template: html,
scope: {
type: '=',
action: '=',
data: '='
},
controller: function ($scope, objectTools, bitbrainTools, keyTools) {
function init() {
console.log('$scope.data # fp: ', $scope.data);
}
function hidePopup() {
$scope.data = null;
$scope.$emit('hidePopup');
}
$scope.$on('showPopup', function () {
init();
});
init();
$scope.hidePopup = hidePopup;
}
}
}
Without the rest of the code I can only guess: You either need to use a promise when displaying the popup or use the $apply service to make the change to the popup visibility.
surround your $broadcast event in $timeout like follow:
$timeout(function() {
$broadcast('eventName');
});
It will wait for $scope update and then will trigger the event.

Inject constant to a Controller in angular js(Issue with modal popup)

I am showing a modal popup on click of a button in a row in ng grid.So to get a form in the modal popup i am filling it with JSON Schema as follows
The Html Of Modal Pop Up is
<div class="modal-header">
<h3 class="modal-title">Edit Row</h3>
</div>
<div class="modal-body">
<form sf-schema="schema" sf-form="form" sf-model="entity"></form>
</div>
<div class="modal-footer">
<button class="btn btn-success" ng-click="save()">Save</button>
<button class="btn btn-warning" ng-click="$close()">Cancel</button>
</div>
</div>
The schema is put into the constant like below in an external js file:
var app = angular.module("myApp", ['ngGrid','ui.bootstrap']);
app.constant('UserPopUpSchema', {
type: 'object',
properties: {
FullName: { type: 'string', title: 'FullName' },
UserName: { type: 'string', title: 'UserName' },
Password: { type: 'string', title: 'Password' },
EmailAddress: { type: 'string', title: 'EmailId' },
Phone: {type:'string',title:'phNum'}
}
});
So on click of a row in ng-grid I am executing a function called edit row ,which would open the pop up and fill the form according to the constant.
$scope.editRow = function (grid,row) {
$modal.open({
templateUrl: 'PopUpTemplate.htm',
controller:['$modalInstance', 'grid', 'row','UserPopUpSchema', function($modalInstance,grid,row){
schema = 'UserPopUpSchema';
entity = angular.copy(row.entity);
form = [
'FullName',
'UserName',
'Password',
'EmailId',
'phNum'
];
}],
resolve: {
grid: function () { return grid; },
row: function () { return row; }
}
});
};
But here I am a bit confused on how to inject a constant to a controller function .As a result the modal appear with a blank form.
Appreciate any help!!
Try this
$modal.open({
templateUrl: 'PopUpTemplate.htm',
controller: function ($modalInstance, grid, row, UserPopUpSchema) {
// Do Stuff
},
resolve: {
grid: function () {
return grid;
},
row: function () {
return row;
},
UserPopUpSchema: function () {
return UserPopUpSchema;
}
}
});
app.controller('Myctrl',function($scope,UserPopUpSchema) {
$scope.editRow = function (grid,row) {
$modal.open({
templateUrl: 'PopUpTemplate.htm',
controller:['$modalInstance', 'grid', 'row','UserPopUpSchema', function($modalInstance,grid,row){
schema = 'UserPopUpSchema';
entity = angular.copy(row.entity);
form = [
'FullName',
'UserName',
'Password',
'EmailId',
'phNum'
];
}],
resolve: {
grid: function () { return grid; },
row: function () { return row; }
UserPopUpSchema: function () { return UserPopUpSchema; }
}
});
};
});
and in the modal instance controller inject the UserPopUpSchema along with grid and row
$modal.open() accept a "resolve" property in which you can pass all the data you want.
$modal.open({
templateUrl: 'templateUrl',
controller: 'controllerName',
resolve: {
data: function(){
// Here, return a JSON object containing all the data you want to pass to the controller.
}
}
});
Then your controller will look like this :
yourApp.controller('controllerName', function ( $scope, $modalInstance, data ){
// Here, using data, you can access the JSON object you passed previously.
});

AngularJS custom directive in the popover redraw

I have a button that triggers a popover with a custom directive.
The problem is that on button click the popover is empty, the custom directive stuff only kicks in when I force angular cycle (change data in the input field, etc.)
Once that happens popover gets redrawn with custom directive stuff as expected.
How can I make it so the custom directive gets executed when the popover gets opened?
Some code:
Button - <button type="button" class="btn btn-primary btn-small" bs-popover="'partials/test.html'"><i class="icon-white icon-plus"></i></button>
partials/test.html - <itemlist apicall="'attributes'" editable="'false'" selectable="'true'" viewtype="'attributes'" template="partials/itemlist.html"></itemlist> (itemlist is the custom directive)
itemlist directive -
.directive('itemlist', function () {
return {
restrict: 'AE',
scope: { apicall: '=', editable: '=', viewtype: '=', selectable: '=' },
controller: function ($scope, $http, $resource, $timeout, fileReader, apiaddress, $dialog, errormsg) {
var resource = $resource(apiaddress + $scope.apicall, {}, { update: { method: 'PUT', params: { id: '#Id' } } });
$scope.apiresource = { list: resource.query() };
//TODO: See how to only display one.
toastr.info("Loading...");
},
templateUrl: function (tElement, tAttrs) { return tAttrs.template },
replace: true
};
})
One more thing - the itemlist custom directive works in other areas of the app.
Thank you!
Since the resource is retrieving data asynchronously, so resource.query() doesn't guarantee the data being returned synchronously. You can try to change the line to be
$timeout(function () {
$scope.apiresource = {
list: resource.query()
};
});

Categories

Resources