i have a delete button, for the confirmation from the user, i am using directive for modal.
this is the directive code
app.directive('modal', function() {
return {
template: '<div class="modal fade">' + '<div class="modal-dialog">' + '<div class="modal-content">' + '<div class="modal-header">' + '<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-click="close()">×</button>' + '<h4 class="modal-title">{{ title }}</h4>' + '</div>' + '<div class="modal-body" ng-transclude></div>' + '</div>' + '</div>' + '</div>',
restrict: 'E',
transclude: true,
replace: true,
scope: true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value) {
if (value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
});
});
}
};
});
this is the controller code
$scope.deletePlace = function(place) {
if (place._id) {
var url = '/api/places/' + place._id;
$http.delete(url, {})
.then(function(response) {
$scope.showModal = false;
$state.transitionTo('dashboard.places.list', null, { reload: true, inherit: true, notify: true });
}, function(response) { // fail
$scope.errorMessage = true;
});
}
}
after clicking ok button on delete modal, the modal will hide, but the black screen remains same and the buttons on page or not clickable, untill i refresh the page manually. Is there any way to remove the black screen after clicking ok button on confirmation modal. If i click the refresh manually, it will work. i don't want to refresh it manually. i want it to be refreshed automatically or from other way to hide the black screen.
Try $state.reload() It should work in most cases if you're using latest version of ui-router.
If that doesn't work try routing to the same page using $state.go() or $state.transitionTo() with additional parameter as {reload: true}.
If that also doesn't work create a auto executing method to initialize your controller and call that method again.
activate();
function activate(){
}
Related
I have created a directive, its behave like when you click on button it will show spinner or loader, its show user there is something in progress like API call or moving to another page, I am facing following issues:
If more than one directive used on page, its show spinner for both button instead clicked button, i want it should show only for clicked button
In some scenarios, clicked event bind three times, as there are three span used inside button to show spinner, there is some issue with event propagation
Here is directive code:
(function () {
'use strict';
angular
.module('app.base')
.directive('buttonSpinner', directiveFunction)
.controller('btnController', ControllerFunction);
// ----- directiveFunction -----
directiveFunction.$inject = [];
/* #ngInject */
function directiveFunction() {
var directive = {
restrict: 'E',
scope: {
label: "#",
available: "="
},
controller: 'btnController',
controllerAs: 'vm',
replace: true,
transclude: true,
template:
'<button ng-disabled="vm.isDisabled">'
+ '<span ng-hide="vm.isClicked">{{label}}</span>'
+ '<div class="spinner" ng-show="vm.isClicked">'
+ '<span class="bounce1"></span>'
+ '<span class="bounce2"></span>'
+ '<span class="bounce3"></span>'
+ '</div>' +
'</button>'
};
return directive;
}
// ----- ControllerFunction -----
ControllerFunction.$inject = [ '$scope' ];
/* #ngInject */
function ControllerFunction( $scope ) {
var vm = this;
vm.isClicked = false;
$scope.$on('APICALLED', function(event, data){
vm.isClicked = data.done;
if( data.elem ) {
angular.element(document.getElementById(data.elem))[0].disabled = true;
} else {
vm.isDisabled = data.disable;
}
});
}
})();
How to use:
In View:
<button-spinner class="btn btn-primary btn-block btn-lg" type="submit" ng-click="vm.notifyMe($event)" label="Notify Me" available="vm.notify.is" id="notifyMeBtn"></button-spinner>
In Controller:
to show spinner:
$rootScope.$broadcast('APICALLED', {'done': true, 'disable': true});
to hide spinner:
$rootScope.$broadcast('APICALLED', {'done': false, 'disable': false});
Your problem is related to your $broadcast's so its a pattern problem. $rootScope.$broadcast() isnt a good solution at all. e.g.) You need to destroy $rootScope.$broadcast.$on() bindings manually. Just parse a unique scope var into the directive like loading. This scope param could be handled by the controller itself for each loading procedure:
/* #ngInject */
function directiveFunction() {
var directive = {
restrict: 'E',
scope: {
label: "#",
available: "=",
loading: "="
},
controller: 'btnController',
controllerAs: 'vm',
replace: true,
transclude: true,
template:
'<button ng-disabled="vm.isDisabled">'
+ '<span ng-hide="vm.isClicked">{{label}}</span>'
+ '<div class="spinner" ng-show="vm.loading">'
+ '<span class="bounce1"></span>'
+ '<span class="bounce2"></span>'
+ '<span class="bounce3"></span>'
+ '</div>' +
'</button>'
};
return directive;
}
View
<button-spinner class="btn btn-primary btn-block btn-lg"
type="submit"
ng-click="vm.notifyMe($event)"
loading="vm.isLoading"
label="Notify Me"
available="vm.notify.is"
id="notifyMeBtn"></button-spinner>
I am facing a problem with the pop up modal in Angularjs. I have a button and on clicking I need to show a modal. Here is my code. When I click on Details button for first time, the modal pop up appears but when I click again.. the app.directive doesn't get called. Greatly appreciate the help. Thanks.
JS:
myApp.directive('modal', function () {
return {
template: '<div class="modal fade">' +
'<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">{{ title }}</h4>' +
'</div>' +
'<div class="modal-body" ng-transclude></div>' +
'</div>' +
'</div>' +
'</div>',
restrict: 'E',
transclude: true,
replace:true,
scope:true,
link: function postLink(scope, element, attrs) {
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value){
if(value == true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).on('shown.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = true;
});
});
$(element).on('hidden.bs.modal', function(){
scope.$apply(function(){
scope.$parent[attrs.visible] = false;
});
});
}
};
});
.state("abc"){
controller:function(){
$scope.showModal = false;
$scope.toggleModal = function(){
$scope.showModal = false;
$scope.showModal = !$scope.showModal;
var aclpGuid = $stateParams.aclpGuid;
};
}
}
HTML:
<td>
<button ng-click="toggleModal()">Details</button>
<modal visible="showModal">
<label>Room Details</label>
</modal>
</td>
I guess the issue is the isolate scope which you have here scope:true inside the directive. Using this, the scope inside a directive is seperated from the scope outside. To confirm this I checked the scope variable with and without scope: true. Note the $$watchers here:
Without scope: true
With scope: true
The isolated scope has $$watchers == null. So any changes in the scope variable showDialog doesn't fire the watch event. So you might wanna use the parent scope i.e the controller's scope here.
The event fires once as when compiling the directive for the first time it attaches the $watchers to the element only once and for subsequent calls there is no relation between the outer scope to a directive's inner scope.
With that change and few others I have a working plunk.
controller:
myApp.controller('myCtrl', function($scope) {
$scope.showDialog = false;
$scope.toggleModal = function() {
$scope.showDialog = !$scope.showDialog;
};
});
Setting the showDialog to false, whenever the user closes the modal as #Mistalis suggested.
$(element).bind('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
scope.showDialog = false;
});
});
directive:
myApp.directive('modal', function() {
return {
restrict: 'E',
transclude: true,
replace: true,
templateUrl: 'modal-partial.html',
//scope: true,
link: function postLink(scope, element, attrs) {
scope.showModal = function(visible, elem) {
if (!elem)
elem = element;
if (visible)
$(elem).modal("show");
else
$(elem).modal("hide");
}
scope.title = attrs.title;
scope.$watch(attrs.visible, function(value) {
if (value === true)
$(element).modal('show');
else
$(element).modal('hide');
});
$(element).bind('shown.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = true;
});
});
$(element).bind('hidden.bs.modal', function() {
scope.$apply(function() {
scope.$parent[attrs.visible] = false;
scope.showDialog = false;
});
});
}
};
});
I am trying to make a reusable modal dialog and I would like to load directive template on click directive itself..
function modalDialog() {
var directive = {
restrict: 'A',
link: linkFunc,
template: '<div class="modalBox--blur">' +
'<div class="modalBox">' +
'<h3>' {{title}} '</h3>' +
'<h4>' {{text}} '</h4>' +
'<button ng-click="answer(true)">Cancel</button>' +
'<button ng-click="answer(false)">Ok</button>' +
'</div>' +
'</div>',
scope: {
title: '=dialogTitle',
text: '=dialogTxt'
},
transclude: true
};
return directive;
function linkFunc($scope, element, attrs) {
element.on('click', function () {
$scope.newEl = element.parent();
$scope.newEl.append(...template Here...???);
});
}
}
This is how directive is set in the view:
<button
modal-dialog
dialog-title="modalBox.title"
dialog-txt="modalBox.subText"
type="button"
ng-click="deleteSth()"
class="button">
</button>
I can't figure out how to load template on element click :
element.on('click', function () {
$scope.newEl = element.parent();
$scope.newEl.append(template????);
});
Any tips?
Thank you in advance!
You can get the template with $templateCache
Like $templateCache.get('templateId.html')
Solution was compiling the template:
scope.modal = $compile(' <div class="modalBox--blur">' +
'<div class="modalBox">' +
'<h3>{{title}}</h3>' +
'<h4>{{text}}</h4>' +
'<button ng-click="dialogAnswer(true)">Annuleren</button>' +
'<button ng-click="dialogAnswer(false)">Ok</button>' +
'</div>' +
'</div>')(scope);
element.on('click', function () {
scope.newEl = element.parent();
scope.newEl.append(scope.modal);
I have following code for working with ui bootstrap modal. I am having a input field whose value has to be captured on the controller. But the input field value is not getting reflected on the controller after an value is entered on the modal.
angular.module('myApp')
.controller('mainController', ['$scope', '$modal', function($scope, $modal) {
$scope.openModal = function() {
$modal.open({
templateUrl: 'modal.html',
controller: BoardController
});
};
}])
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h4 ng-bind="dialogTitle"></h4>' +
'</div>' +
'<div class="modal-body" ng-transclude></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ' +
'ng-click="cancel()">Close</button>' +
'<button type="button" class="btn btn-primary" ' +
'ng-click="ok()">Save</button>' +
'</div>' +
'</div>'
};
});
var BoardController = function ($scope, $modalInstance) {
$scope.dialogTitle = 'Create new item';
$scope.placeholder = 'Enter item name';
$scope.inputname = '';
$scope.ok = function () {
console.log($scope.inputname);
$modalInstance.dismiss('cancel');
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
In 'modal.html' i have the following code:
<modal-dialog>
<input type="text" class="form-control" id="enter-name"
ng-model="inputname" placeholder={{placeholder}}>
{{ inputname }}
</modal-dialog>
So, after entering some text into the inputfield when i click the save the following line under $scope.ok() prints blank.
console.log($scope.inputname);
I guess this has something to do with scopes or may be transclusion. But i am not able to figure out whats causing this. I couldnt find the updated value in developer console also.
The problem here is transclusion. ngTransclude directive creates one more scope, but it is a sibling scope. Using transclusion makes it very difficult to access your scope. In your case you could retrieve model value like this:
$scope.ok = function () {
console.log($scope.$$childHead.$$nextSibling.inputname);
$modalInstance.dismiss('cancel');
};
But of course this is terrible. Fortunately, you can control what scope transclusion will use for rendered template if you make transclusion manually. For this you need to use link function with the fifth argument which is transclude function.
Your directive will become (note, that you don't use ng-tranclude directive in template anymore):
.directive('modalDialog', function() {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<div class="modal-content">' +
'<div class="modal-header">' +
'<h4 ng-bind="dialogTitle"></h4>' +
'</div>' +
'<div class="modal-body"></div>' +
'<div class="modal-footer">' +
'<button type="button" class="btn btn-default" ng-click="cancel()">Close</button>' +
'<button type="button" class="btn btn-primary" ng-click="ok()">Save</button>' +
'</div>' +
'</div>',
link: function(scope, element, attrs, controller, transclude) {
transclude(scope, function(clone) {
var modalBody = element[0].querySelector('.modal-body');
angular.element(modalBody).append(clone);
});
}
};
});
Demo: http://plnkr.co/edit/I7baOyjx4pKUJHNkxkDh?p=preview
I am writing an AngularJS directive that makes it easy for me to create swipeable pages (such as the viewpager on Android). I use SwipeJS as library for this.
This is how I use the directive
<ng-swipe ng-if="model.messages">
<div class="page" ng-repeat="message in model.messages">
{{message.title}}
</div>
</ng-swipe>
And the code of the directive looks like this.
var swipe = angular.module('ngSwipe', []);
swipe.directive('ngSwipe', function() {
return {
restrict: 'EA',
replace: false,
transclude: true,
scope: {},
template:
'<div>' +
' <div id="slider" class="swipe">' +
' <div class="swipe-wrap" ng-transclude></div>' +
' </div>' +
' <div class="pagecontrol">' +
' <div class="pagedot" ng-repeat="p in swipe.pages" ng-click="swipe.switchPage($index)"></div>' +
' </div>' +
'</div>',
link: function($scope, $element, $attrs) {
var $model = $scope.swipe = {
pages: [],
switchPage: function(index) {
$model.swipe.slide(index);
}
}
setTimeout(function() {
$model.swipe = new Swipe(document.getElementById('slider'), {
continuous: false,
callback: function(index, elem) {
$model.currentTab = index;
}
});
for(var i=0; i<$model.swipe.getNumSlides(); i++) {
$model.pages.push(i);
}
$scope.$apply();
}, 0);
}
};
});
First of all, the reason I use ng-if in the ng-swipe directive is because the link method should be invoked after my messages are loaded. The messages are being retrieved from the server and it can take up to 2 seconds before they are retrieved. If I don't wait untill the messages are loaded, the new Swipe() object will be created but it won't find pages so it will not work.
But besides that, you can also see that I have a setTimeout() function of 0 milliseconds. If I don't use that one, it just doesn't render the swipe pages.
I made a JSFiddle for this issue. Thanks in advance!
switchPage: function(index) {
$model.swipe.slide(index);
$scope.$apply();
}