AngularJs UI Grid rebind from Modal - angularjs

I have a main controller in which I load data into a "angular-ui-grid" and where I use a bootstrap modal form to modify detail data, calling ng-dlbclick in a modified row template :
app.controller('MainController', function ($scope, $modal, $log, SubjectService) {
var vm = this;
gridDataBindings();
//Function to load all records
function gridDataBindings() {
var subjectListGet = SubjectService.getSubjects(); //Call WebApi by a service
subjectListGet.then(function (result) {
$scope.resultData = result.data;
}, function (ex) {
$log.error('Subject GET error', ex);
});
$scope.gridOptions = { //grid definition
columnDefs: [
{ name: 'Id', field: 'Id' }
],
data: 'resultData',
rowTemplate: "<div ng-dblclick=\"grid.appScope.editRow(grid,row)\" ng-repeat=\"(colRenderIndex, col) in colContainer.renderedColumns track by col.colDef.name\" class=\"ui-grid-cell\" ng-class=\"{ 'ui-grid-row-header-cell': col.isRowHeader }\" ui-grid-cell></div>"
};
$scope.editRow = function (grid, row) { //edit row
$modal.open({
templateUrl: 'ngTemplate/SubjectDetail.aspx',
controller: 'RowEditCtrl',
controllerAs: 'vm',
windowClass: 'app-modal-window',
resolve: {
grid: function () { return grid; },
row: function () { return row; }
}
});
}
});
In the controller 'RowEditCtrl' I perform the insert/update operation and on the save function I want to rebind the grid after insert/update operation. This is the code :
app.controller('RowEditCtrl', function ($modalInstance, $log, grid, row, SubjectService) {
var vm = this;
vm.entity = angular.copy(row.entity);
vm.save = save;
function save() {
if (vm.entity.Id === '-1') {
var promisePost = SubjectService.post(vm.entity);
promisePost.then(function (result) {
//GRID REBIND ?????
}, function (ex) {
$log.error("Subject POST error",ex);
});
}
else {
var promisePut = SubjectService.put(vm.entity.Id, vm.entity);
promisePut.then(function (result) {
//row.entity = angular.extend(row.entity, vm.entity);
//CORRECT WAY?
}, function (ex) {
$log.error("Subject PUT error",ex);
});
}
$modalInstance.close(row.entity);
}
});
I tried grid.refresh() or grid.data.push() but seems that all operation on the 'grid' parameter is undefinied.
Which is the best method for rebind/refresh an ui-grid from a bootstrap modal ?

I finally solved in this way:
In RowEditCtrl
var promisePost = SubjectService.post(vm.entity);
promisePost.then(function (result) {
vm.entity.Id = result.data;
row.entity = angular.extend(row.entity, vm.entity);
$modalInstance.close({ type: "insert", result: row.entity });
}, function (ex) {
$log.error("Subject POST error",ex);
});
In MainController
modalInstance.result.then(function (opts) {
if (opts.type === "insert") {
$log.info("data push");
$scope.resultData.push(opts.result);
}
else {
$log.info("not insert");
}
});

The grid that received inside RowEditCtrl is not by reference, so it wont help to refresh inside the RowEditCtrl.
Instead do it right after the modal promise resolve in your MainController.
like this:
var modalInstance = $modal.open({ ...});
modalInstance.result.then(function (result) {
grid.refresh() or grid.data.push()
});

Related

Angular Js - select dropdown becomes empty some times on page refresh , why?

i have a simple dropdown which i made with the help of select. The dropdown works fine in normal flow, but when i update my page or sometimes refresh my page the selected value in dropdown becomes empty because of the late response from the backend.
Html
<div class="col-lg-12 col-md-12" ba-panel ba-panel-title="Registration" ba-panel-class="" ng-init="driver.phoneNumberPrefixFunc();">
<div class="col-lg-12 col-md-12" ba-panel ba-panel-title="Registration" ba-panel-class="" ng-init="driver.phoneNumberPrefixFunc();driver.getVehicleTypes();driver.getUnArchBankListing()">
<form class="form-vertical" name="driver.registrationForm" ng-submit="driver.register(driver.registrationInformation);">
<select class="form-control" id="phonePrefix" name="phonePrefix" ng-model="driver.registrationInformation.phoneNumberPrefix"
required>
<option value="" selected>Select Code</option>
<option ng-repeat="item in driver.registrationInformation.phonePrefix" value="{{item.code}}">{{item.code}}</option>
</select>
</form>
</div>
Controller
function editDriverDetails() {
phoneNumberPrefixFunc();
var params = {
id: $stateParams.driverId
};
return driverServices.getDriverDetails(params).then(function (res) {
if (res.success == "1") {
driverData = res.data.driver;
driver.registrationInformation.phoneNumberPrefix = driverData.phoneNumberPrefix;
usSpinnerService.stop('spinner-1');
} else {
usSpinnerService.stop('spinner-1');
toastr.error(res.message, 'Driver');
}
});
};
editDriverDetails function gets called when I am editing my form. As you can see I am calling phoneNumberPrefixFunc() in the beginning as I need the list of phonenumber prefix. below is the function code.
function phoneNumberPrefixFunc(data) {
usSpinnerService.spin('spinner-1');
return driverServices.phoneNumberPrefix(data).then(function (response) {
if (response.success == '1') {
usSpinnerService.stop('spinner-1');
driver.registrationInformation.phonePrefix = response.data.countryCode;
} else {
usSpinnerService.stop('spinner-1');
toastr.error(response.message);
}
});
};
function phoneNumberPrefixFuncwill bring the list of objects in array for dropdown and driver.registrationInformation.phoneNumberPrefix is the preselected value which i get in editDriverDetails function. Now sometimes the response of phoneNumberPrefixFunc or editDriverDetails is late and thats why my drop down does not get populated. How can i fix this ?
I worked it out like this.
Routes.js
(function () {
'use strict';
angular.module('Driver', []).config(routeConfig);
/** #ngInject */
function routeConfig ($stateProvider, $urlRouterProvider) {
function authentication (GlobalServices, $q, localStorageService, $state) {
var d = $q.defer();
var checkUser = localStorageService.get('user');
if (checkUser !== null) {
d.resolve(checkUser);
} else {
GlobalServices.currentUser().then(function (data) {
if (data.success === 0) {
$state.go('login');
} else {
localStorageService.set('user', data.data.account);
d.resolve(data.user);
}
});
}
return d.promise;
}
function phoneNumberPrefix ($q,driverServices) {
var d = $q.defer();
driverServices.phoneNumberPrefix().then(function (data) {
d.resolve(data.data.countryCode);
});
return d.promise;
}
function getVehicleTypes ($q,driverServices) {
var d = $q.defer();
driverServices.vehicleType().then(function (data) {
d.resolve(data.data.vehicleTypes);
});
return d.promise;
}
function getUnArchBankListing ($q,bankServices) {
var d = $q.defer();
bankServices.getUnArchiveBankListing({type : 'unarchive'}).then(function (data) {
d.resolve(data.data.banks);
});
return d.promise;
}
$stateProvider
.state('drivers', {
url: '/drivers',
template: '<ui-view autoscroll="true" autoscroll-body-top></ui-view>',
abstract: true,
// controller: 'DriverMainController',
title: 'Drivers',
sidebarMeta: {
icon: 'fa fa-motorcycle',
order: 3,
},
resolve: {
$user: authentication,
phoneData : function () {},
vehcileTypesData: function () {},
banksData: function () {}
},
})
.state('driverView', {
url: '/driver/:driverId',
templateUrl: '../app/pages/driver/driverView.html',
title: 'Profile',
controller: 'driverCtrl',
controllerAs: 'profile',
resolve: {
$user: authentication,
phoneData : function () {},
vehcileTypesData: function () {},
banksData: function () {}
},
})
.state('driverEdit', {
url: '/driver-edit/:driverId',
params: {
driverDetails: null,
driverId : null
},
templateUrl: '../app/pages/driver/registration/registration.html',
title: 'Driver Profile',
controller: 'driverCtrl',
controllerAs: 'driver',
resolve: {
$user: authentication,
phoneData : function ($q, driverServices) {
var d = $q.defer();
return phoneNumberPrefix($q, driverServices);
},
vehcileTypesData: function ($q, driverServices) {
var d = $q.defer();
return getVehicleTypes($q, driverServices);
},
banksData: function ($q, bankServices) {
var d = $q.defer();
return getUnArchBankListing($q, bankServices);
}
}
});
$urlRouterProvider.when('/drivers', '/drivers/registration');
}
})();
Instead of using ng-init="driver.phoneNumberPrefixFunc();" i made sure that the page only open when the required data is loaded. Then i can access the data in controller like this .
controller.js
(function () {
angular.module('Driver').controller('driverCtrl', driverCtrl);
driverCtrl.$inject = ['$scope', '$state','phoneData','vehcileTypesData','banksData'];
function driverCtrl($scope, $state,phoneData,vehcileTypesData,banksData) {
if(phoneData){
driver.phonePrefix = phoneData;
}
if(banksData){
driver.bankListing = banksData;
}
if(vehcileTypesData){
driver.vehicleTypes = vehcileTypesData;
}
}
})();

AngularJS - moving Material mdDialog to service

I'm trying to cleanup code of one of my controllers which became too big. First I have decided to move attendee registration, which uses AngularJS Material mdDialog, to the service.
Original (and working) controller code looks like:
myApp.controller('RegistrationController', ['$scope','$routeParams','$rootScope','$location','$filter','$mdDialog', function($scope, $routeParams, $rootScope, $location, $filter, $mdDialog){
var attendee = this;
attendees = [];
...
$scope.addAttendee = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'views/regForm.tmpl.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
controllerAs: 'attendee',
fullscreen: $scope.customFullscreen, // Only for -xs, -sm breakpoints.
locals: {parent: $scope}
})
.then(function(response){
attendees.push(response);
console.log(attendees);
console.log(attendees.length);
})
};
function DialogController($scope, $mdDialog) {
var attendee = this;
$scope.hide = function() {
$mdDialog.hide();
};
$scope.cancel = function() {
$mdDialog.cancel();
};
$scope.save = function(response) {
$mdDialog.hide(response);
};
}
}]);
and the code for the controller after separation:
myApp.controller('RegistrationController', ['$scope','$routeParams','$rootScope','$location','$filter','$mdDialog','Attendees', function($scope, $routeParams, $rootScope, $location, $filter, $mdDialog, Attendees){
...
$scope.attendees= Attendees.list();
$scope.addAttendee = function (ev) {
Attendees.add(ev);
}
$scope.deleteAttendee = function (id) {
Attendees.delete(id);
}
}]);
New service code looks like:
myApp.service('Attendees', ['$mdDialog', function ($mdDialog) {
//to create unique attendee id
var uid = 1;
//attendees array to hold list of all attendees
var attendees = [{
id: 0,
firstName: "",
lastName: "",
email: "",
phone: ""
}];
//add method create a new attendee
this.add = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'views/regForm.tmpl.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
controllerAs: 'attendee',
fullscreen: this.customFullscreen, // Only for -xs, -sm breakpoints.
//locals: {parent: $scope}
})
.then(function(response){
attendees.push(response);
console.log(attendees);
console.log(attendees.length);
})
};
//simply search attendees list for given id
//and returns the attendee object if found
this.get = function (id) {
for (i in attendees) {
if (attendees[i].id == id) {
return attendees[i];
}
}
}
//iterate through attendees list and delete
//attendee if found
this.delete = function (id) {
for (i in attendees) {
if (attendees[i].id == id) {
attendees.splice(i, 1);
}
}
}
//simply returns the attendees list
this.list = function () {
return attendees;
}
function DialogController($mdDialog) {
this.hide = function() {
$mdDialog.hide();
};
this.cancel = function() {
$mdDialog.cancel();
};
this.save = function(response) {
$mdDialog.hide(response);
};
}
}]);
but I'm not able to "save" from the spawned mdDialog box which uses ng-click=save(attendee) neither close the dialog box.
What I'm doing wrong?
I'm not able to "save" from the spawned mdDialog box which uses ng-click=save(attendee) neither close the dialog box.
When instantiating a controller with "controllerAs" syntax, use the name with which it is instantiated:
<button ng-click="ctrl.save(ctrl.attendee)">Save</button>
this.add = function(ev) {
$mdDialog.show({
controller: DialogController,
templateUrl: 'views/regForm.tmpl.html',
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
controllerAs: ̶'̶a̶t̶t̶e̶n̶d̶e̶e̶'̶ 'ctrl',
fullscreen: this.customFullscreen, // Only for -xs, -sm breakpoints.
//locals: {parent: $scope}
})
.then(function(response){
attendees.push(response);
console.log(attendees);
console.log(attendees.length);
return response;
});
To avoid confusion, choose a controller instance name that is different from the data names.
$scope is not available to be injected into a service when it's created. You need to refactor the service and its methods so that you don't inject $scope into it and instead pass the current scope to the service methods when you call them.
I actually have a notification module that I inject which uses $mdDialog. Below is the code for it. Perhaps it will help.
(() => {
"use strict";
class notification {
constructor($mdToast, $mdDialog, $state) {
/* #ngInject */
this.toast = $mdToast
this.dialog = $mdDialog
this.state = $state
/* properties */
this.transitioning = false
this.working = false
}
openHelp() {
this.showAlert({
"title": "Help",
"textContent": `Help is on the way for ${this.state.current.name}!`,
"ok": "OK"
})
}
showAlert(options) {
if (angular.isString(options)) {
var text = angular.copy(options)
options = {}
options.textContent = text
options.title = " "
}
if (!options.ok) {
options.ok = "OK"
}
if (!options.clickOutsideToClose) {
options.clickOutsideToClose = true
}
if (!options.ariaLabel) {
options.ariaLabel = 'Alert'
}
if (!options.title) {
options.title = "Alert"
}
return this.dialog.show(this.dialog.alert(options))
}
showConfirm(options) {
if (angular.isString(options)) {
var text = angular.copy(options)
options = {}
options.textContent = text
options.title = " "
}
if (!options.ok) {
options.ok = "OK"
}
if (!options.cancel) {
options.cancel = "Cancel"
}
if (!options.clickOutsideToClose) {
options.clickOutsideToClose = false
}
if (!options.ariaLabel) {
options.ariaLabel = 'Confirm'
}
if (!options.title) {
options.title = "Confirm"
}
return this.dialog.show(this.dialog.confirm(options))
}
showToast(toastMessage, position) {
if (!position) { position = 'top' }
return this.toast.show(this.toast.simple()
.content(toastMessage)
.position(position)
.action('OK'))
}
showYesNo(options) {
options.ok = "Yes"
options.cancel = "No"
return this.showConfirm(options)
}
uc() {
return this.showAlert({
htmlContent: "<img src='img\\underconstruction.jpg'>",
ok: "OK",
title: "Under Construction"
})
}
}
notification.$inject = ['$mdToast', '$mdDialog', '$state']
angular.module('NOTIFICATION', []).factory("notification", notification)
})()
Then inject notification (lower case) into my controllers in order to utilize it. I store notification in a property of the controller and then call it with something like this:
this.notification.showYesNo({
clickOutsideToClose: true,
title: 'Delete Customer Setup?',
htmlContent: `Are you sure you want to Permanently Delete the Customer Setup for ${custLabel}?`,
ariaLabel: 'Delete Dialog'
}).then(() => { ...
this.notification.working = true
this.ezo.EDI_CUSTOMER.remove(this.currentId).then(() => {
this.primaryTab = 0
this.removeCustomerFromList(this.currentId)
this.currentId = 0
this.notification.working = false
}, error => {
this.notification.working = false
this.notification.showAlert({
title: "Error",
htmlContent: error
})
})

AngularJS: after-select-item not triggering

I am using angularjs version 1.6.4 with angular-multiple-select module for multi selecting. Every thing is working fine. I am able to select from suggestions but whenever i do selection "after-select-item" directive is not triggering. According to angular-multiple-select module documentation
afterSelectItem : Listen for event before adding an item
<div class="form-group float-label-control">
<label>Skills</label>
<multiple-autocomplete ng-model="model.user.skills"
object-property="name"
after-select-item="model.afterSelectItem"
suggestions-arr="model.skills">
</multiple-autocomplete>
</div>
My controller few code lines:
(function () {
"use strict";
var module = angular.module(__appName);
function fetchSkills($http) {
return $http.get(__apiRoot + "/skills")
.then(function (response) {
return response.data;
})
}
function controller($http) {
var model = this;
model.$onInit = function () {
fetchSkills($http).then(function (skills) {
model.skills = skills;
});
};
model.afterSelectItem = function (item) {
console.log("after select item");
console.log(item);
}
}
module.component("userEdit", {
templateUrl: "components/user-edit/user-edit.template.html",
bindings: {
userId: "<",
onUserSaved: "&"
},
controllerAs: "model",
controller: ["$http", controller]
});
}());

Cancel function that has $timeout running

I have the following code. It checks a factory that polls a server every few minutes. It works fine, but I want to pause the "fetchDataContinously" method when a modal is opened, and resume it when the modal is closed. In short, I need to find a way to toggle the "fetchDataContinously" on the opening and closing of modals. Please advise, or let me know if my question is not clear.
angular.module('NavBar', []).controller('NavBarCtrl', function NavBarCtrl($scope,$modal,$timeout,MyPollingService) {
var ctrl = this;
fetchDataContinously ();
function fetchDataContinously () {
MyPollingService.poll().then(function(data) {
if(data.response[0].messages[0].important_popup){
ctrl.showImportantNotice(data.response[0].messages[0]);
return;
}
$timeout(fetchDataContinously, 3000);
});
}
ctrl.showNotices = function () {
var noticesModalOptions = {
templateUrl: "notices.html",
controller: "NoticesCtrl",
controllerAs: "ctrl",
show: true,
scope: $scope,
resolve: {
notices: function(NoticesFactory) {
return NoticesFactory.getMessages();
}
}
}
var myNoticesModal = $modal(noticesModalOptions);
myNoticesModal.$promise.then(myNoticesModal.show);
}
ctrl.showImportantNotice = function (importantNotice) {
ctrl.importantNotice = importantNotice;
var importantNoticeModalOptions = {
templateUrl: "/importantNotice.html",
controller: "ImportantNoticeCtrl",
controllerAs: "ctrl",
show: true,
scope: $scope,
onHide: function() {
console.log("Close !");
fetchDataContinously();
},
onShow: function() {
console.log("Open !");
}
}
var myImportantNoticeModal = $modal(importantNoticeModalOptions);
myImportantNoticeModal.$promise.then(myImportantNoticeModal.show);
}
})
Wrap your $timeout in function and return it's promise
var timer;
function startPolling(){
return $timeout(pollData, 3000);
}
// start it up
timer = startPolling();
To cancel:
$timeout.cancel(timer);
Then to start again :
timer = startPolling();

Directive inside $modal window throws "undefined is not a function"

Using ui-bootstrap I have a really simple custom directive that lists alerts at the top of the page. On normal pages it works like a champ. When I use my directive inside a $modal popup I get "undefined is not a function" at ngRepeatAction.
The directive I have behind the modal on the main page still works. I can see it behind the modal. It's just the one in the modal popup that breaks. What am I doing wrong?
Modal open code:
$modal.open({
templateUrl: 'partials/main/servers/serverAuths/edit.html',
controller: function($scope, $modalInstance) {
$scope.auth = angular.copy(auth);
$scope.auth.password = null;
$scope.saveAuth = function() {
Auths.editAuth($scope.auth).then(
function(resp) {
if (resp.rc===0) {
Alerts.addAlert('success', 'Auth `'+$scope.auth.name+'` saved.');
_.extend(auth, $scope.auth);
$modalInstance.close();
} else {
Alerts.addAlert('danger', 'Auth `'+$scope.auth.name+'` could not be saved. ' + resp.message, 'serverAuths');
}
}
);
};
$scope.resetAuth = function() {
$modalInstance.close();
};
}
}).result.then(
function() {
Auths.getAuthList().then(
function(resp) {
$scope.auths = resp;
}
);
}
);
Directive template:
<div class="alert-wrapper alert-{{ alert.type }}"
ng-repeat="alert in alerts"
ng-class="{ 'relative':relative }">
<div class="container">
<div alert type="alert.type" close="closeAlert($index)">{{alert.msg}}</div>
</div>
</div>
Directive code:
angular.module('app')
.directive('appAlerts', function() {
return {
restrict: 'A',
replace: true,
scope: {
watchForm: '=',
relative: '#'
},
templateUrl: 'partials/directives/appAlerts.html',
controller: function($scope, Alerts) {
$scope.closeAlert = function(idx) { Alerts.closeAlert(idx); };
$scope.alerts = Alerts.getAlerts();
}
};
});
Alerts Factory:
angular.module('app').factory('Alerts', function($timeout) {
var alerts = [];
function timeoutAlert(a) {
$timeout(function() {
a.splice(0, 1);
}, 2500);
}
var addAlert = function(type, msg) {
alerts.push({type:type, msg:msg});
timeoutAlert(alerts);
};
var closeAlert = function(index) {
alerts.splice(index, 1);
};
var getAlerts = function() {
return alerts;
};
var killAlert = function(msg) {
var alert = _.where(alerts, {msg:msg});
var idx = _.indexOf(alerts, alert[0]);
if (idx > -1) {
closeAlert(idx);
}
};
return {
addAlert:addAlert,
closeAlert:closeAlert,
getAlerts:getAlerts,
killAlert:killAlert
};
});

Resources