I'm using angular-web-notification (https://github.com/sagiegurari/angular-web-notification) and i've built a factory in order to avoid copy-pasting every time I want to display it. My factory is this
module.registerFactory('browserNotification', function($rootScope, webNotification) {
return {
show: function(title, body, callback) {
webNotification.showNotification(title, {
body: body,
icon: 'img/icon.png',
onClick: callback,
autoClose: 5000 //auto close the notification after 4 seconds (you can manually close it via hide function)
}, function onShow(error, hide) {
if (error) {
console.log('Unable to show notification: ' + error.message);
} else {
console.log('Notification Shown.');
setTimeout(function hideNotification() {
console.log('Hiding notification....');
hide(); //manually close the notification (you can skip this if you use the autoClose option)
}, 5000);
}
});
}
}
})
As you can see I pass to the show() 3 variables, one of them is a callback for the onClick function, in order to do stuff when the notification is clicked. The thing is that i want to close that notification once its clicked, but i can't figure out how, because the hide() functions doesn´t exist in the context where the callback function is executed. For example, in my contrller I have this
browserNotification.show('Test title', 'Test body', function() {
hide();
alert('Entro al callback!');
});
There, hide() didn't exist. So, how can I close the notification from my callback function?
This makes the trick!
module.registerFactory('browserNotification', function($timeout,webNotification) {
return {
show: function(title, body, callback) {
var snd = new Audio('audio/alert.mp3');
snd.play();
//the timeout is to sync the sound with the notification rendering on screen
$timeout(function() {
var hideNotification;
webNotification.showNotification(title, {
body: body,
icon: 'img/icon.png',
onClick: function onNotificationClicked() {
callback();
if (hideNotification) {
hideNotification();
}
},
autoClose: 5000 //auto close the notification after 4 seconds (you can manually close it via hide function)
}, function onShow(error, hide) {
if (!error) {
hideNotification = hide;
}
});
}, 150);
}
}
});
Related
I have a directive that handles uploading file and shows it in a list. For this I have a custom button for opening up the explorer. And after user selects a file from the explorer system shows the file name twice in the list. After debugging I realized it's calling the "onClick" method twice, once when the user clicks it (duh) and some mysterious event invokes it again. I think it's the scope.$apply part but can't be sure. Here's my code snippet:
<div data-ng-click="addFile($event)">
<span class="icon-small icon-add"></span>
</div>
Angular JS:
scope.addFile = function (event) {
if (event.originalEvent == null || !(event.originalEvent instanceof MouseEvent)) {
return;
}
if (!hiddenInputElementNode) {
//inject the hidden HtmlInputFile element and bind to the click event
hiddenInputElementNode = angular.element(
"<input accept='application/pdf,audio/*' type='file' class='hidden' multiple />");
hiddenInputElementNode.insertAfter(event.target);
}
//bind to the inputElementNode change event
hiddenInputElementNode.bind('change', function () {
angular.forEach(hiddenInputElementNode[0].files, function (dataFile) {
scope.$apply(
scope.selectedFiles.push({
name: dataFile.name,
data: dataFile
}));
});
this.value = null;
hiddenInputElementNode.unbind('change');
});
$timeout(function () {
if (!!hiddenInputElementNode) {
hiddenInputElementNode.click();
}
}, 0, false);
};
Even weirder this.value = null doesn't nullify the value!
Try by changing your javascript code for this one:
scope.addFile = function (event) {
if (event.target.tagName.toUpperCase() === "DIV") {
if (!hiddenInputElementNode) {
//inject the hidden HtmlInputFile element and bind to the click event
hiddenInputElementNode = angular.element(
"<input accept='application/pdf,audio/*' type='file' class='hidden' multiple />");
hiddenInputElementNode.insertAfter(event.target);
}
//bind to the inputElementNode change event
hiddenInputElementNode.bind('change', function () {
angular.forEach(hiddenInputElementNode[0].files, function (dataFile) {
scope.$apply(
scope.selectedFiles.push({
name: dataFile.name,
data: dataFile
}));
});
this.value = null;
hiddenInputElementNode.unbind('change');
});
$timeout(function () {
if (!!hiddenInputElementNode) {
hiddenInputElementNode.click();
}
}, 0, false);
}
};
I believe that you ng-click event is been fired twice because of the span inside the div (i had a similar problem with IE too).
I've read and searched for this and found variations (for example, on click) but not for the browser reload/refresh that works.
Basically, what I want is for when a user reloads, refreshes or F5 in the browser, instead of the regular alert confirm, the sweetalert dialog popups up asking the user if they want to refresh, losing whatever information they have viewed, with an OK/Cancel.
In my controller, I have this:
$scope.$on('$destroy', function() {
window.onbeforeunload = undefined;
});
window.onbeforeunload = function (e) {
e.preventDefault();
SweetAlert.swal({
title: "I display correctly but....",
text: "before page refreshes and do not wait for user to click ok",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",confirmButtonText: "Ok",
cancelButtonText: "Cancel",
closeOnConfirm: true,
closeOnCancel: true },
function(isConfirm){
if (isConfirm) {
console.log('do something...')
}
});
return undefined;
};
$scope.$on('$locationChangeStart', function( event, toState, toParams, fromState, fromParams) {
SweetAlert.swal({
title: "I display and wait for the user to click but too late",
text: "...after the page refreshes",
type: "warning",
showCancelButton: true,
confirmButtonColor: "#DD6B55",confirmButtonText: "Ok",
cancelButtonText: "Cancel",
closeOnConfirm: true,
closeOnCancel: true },
function(isConfirm){
if (isConfirm) {
console.log('do something...')
}
});
});
The "window.onbeforeunload" part loads at the right time (before the page reloads/refreshes) but does not wait for the user selection.
The "$scope.$on('$locationChangeStart',..." part loads too late (after the page has reloaded/refreshed) but does wait for the user selection.
Besides '$locationChangeStart,' I've also tried '$stateChangeStart' and '$routeChangeStart' to no avail.
What am I missing to make this work? Any help or direction would be greatly appreciated.
You should not override native functions such as alert() or confirm() because they are blocking and they are blocking for a good reason.
But if you really want to do it, it is possible like this
/**
* Definition of global attached to window properties <br/>
*/
(function() {
nalert = window.alert;
Type = {
native: 'native',
custom: 'custom'
};
})();
/**
* Factory method for calling alert().
* It will be call a native alert() or a custom redefined alert() by a Type param.
* This defeinition need for IE
*/
(function(proxy) {
proxy.alert = function () {
var message = (!arguments[0]) ? 'null': arguments[0];
var type = (!arguments[1]) ? '': arguments[1];
if(type && type == 'native') {
nalert(message);
}
else {
// TODO: Call to SweetAlert()
document.write('<h1>I am redefiend alert()<br/>Alert say: '+message+'</h1>');
}
};
})(this);
More on this here
JavaScript: Overriding alert()
I can't seem to work out how to redraw my Angular-Datatable after I delete a record from my database. I don't get any errors, but the table never seems to redraw unless I manually refresh the page. I have been trying to work with many examples from the website documentation.
I have my datatable:
$scope.dtInstance = {};
$scope.selectedItems = [];
$scope.toggleItem = toggleItem;
$scope.reloadData = reloadData;
// Build the User table
$scope.dtOptions = DTOptionsBuilder
.fromFnPromise(function() {
var deferred = $q.defer();
deferred.resolve(users);
return deferred.promise;
})
.withBootstrap() // Style with Bootstrap
.withOption('responsive', true)
.withDisplayLength(15) // Show 15 items initially
.withOption('order', [0, 'asc']) // Sort by the first column
.withOption('lengthMenu', [15, 50, 100]) // Set the length menu items
.withOption('createdRow', function(row, data, dataIndex) {
// Recompiling so we can bind Angular directive to the DT
$compile(angular.element(row).contents())($scope);
})
.withOption('headerCallback', function(header) {
if (!$scope.headerCompiled) {
// Use this headerCompiled field to only compile header once
$scope.headerCompiled = true;
$compile(angular.element(header).contents())($scope);
}
})
.withOption('fnRowCallback', formatCell);
$scope.dtColumns = [
DTColumnBuilder.newColumn(null).withTitle('Username').withClass('col-md-2').renderWith(createUsernameHyperlink),
DTColumnBuilder.newColumn('Email').withTitle('Email'),
DTColumnBuilder.newColumn('Level').withTitle('Role').withClass('col-md-2'),
DTColumnBuilder.newColumn('LastConnected').withTitle('Last Accessed'),
DTColumnBuilder.newColumn('Verified').withTitle('Account Verified').withClass('col-md-2'),
DTColumnBuilder.newColumn(null).withTitle('')
.notSortable()
.renderWith(function(data, type, full, meta) {
return '<input type="checkbox" ng-click="toggleItem(' + data.Id + ')" />';
}).withClass("text-center")
];
// Reload the datatable
function reloadData() {
var resetPaging = false;
$scope.dtInstance.reloadData(callback, resetPaging);
};
function callback(json) {
console.log(json);
};
And then I have my delete function that sits in the same controller. Calling reloadData() on a successful response from the service. I can see from the console.log that it is calling the function correctly, but nothing happens.
$scope.deleteUser = function( selectedItems ) {
swal({
title: 'Are you sure?',
text: 'Are you sure you want to delete the selected account profile(s)? This process cannot be undone...',
type: 'warning',
showCancelButton: true,
confirmButtonText: 'Delete',
confirmButtonColor: "#DD6B55",
closeOnConfirm: false,
allowEscapeKey: true,
showLoaderOnConfirm: true
}, function() {
setTimeout( function() {
// Delete user
UsersService.deleteUser( selectedItems.toString() )
.then(function( data ) {
// Show a success modal
swal({
title: 'Success',
text: 'User has been deleted!',
type: 'success',
confirmButtonText: 'Close',
allowEscapeKey: false
}, function() {
reloadData(); //<== Calls the function but doesn't do anything
//$state.go('users');
});
}, function() {
// Show an error modal
swal({
title: 'Oops',
text: 'Something went wrong!',
type: 'error',
confirmButtonText: 'Close',
allowEscapeKey: true
});
});
}, 1000);
});
};
Just wondering if I have missed some step?
As suggested by #davidkonrad in a previous comment and more so from the Angular-Datatable's author, I was not reloading my content when attempting to redraw my table. Even though I was referencing my data (users) from an injected service, it was never getting updated within the controller and so my table content was never differing.
The author suggested that it is preferable to load the data from a promise that makes a HTTP request, thus allowing further calls to the promise each time the table redraws.
So instead of this:
// Build the User table
$scope.dtOptions = DTOptionsBuilder
.fromFnPromise(function() {
var deferred = $q.defer();
deferred.resolve(users);
return deferred.promise;
})
.withBootstrap() // Style with Bootstrap
I changed it to this:
// Build the User table
$scope.dtOptions = DTOptionsBuilder
.fromFnPromise(function() {
return UsersService.getUsers();
})
.withBootstrap() // Style with Bootstrap
Which now updates my table fine upon each redraw event with a call to $scope.dtInstance.reloadData();
My Github post can be found here
setTimeout function works from outside of the angular digest cycle since it's async. If you want actions you take inside a timeout to apply to the angular digest cycle you should use $timeout instead.
Another option is to use $scope.apply(), but this will just mimic the $timeout function.
Please note that you'll need to inject $timeout to your controller.
I have a small CasperJS test script as below. The website used in the url is build on angular.
var deals;
var url;
var login_url;
casper.test.begin('Paytm test', function(test) {
casper.start(login_url, function() {
test.assertExists('form', 'form is found');
this.fill('form', {
username: 'username',
password: 'password'
}, true);
});
casper.then(function(){
casper.start(url);
casper.waitForSelector(".border-radius.profile1",
// click login/signup button on home page
function success() {
test.assertExists(".border-radius.profile1");
this.click(".border-radius.profile1");
},
function fail() {
test.assertExists(".border-radius.profile1");
});
casper.waitForSelector(deals,
// click on deals
function success() {
test.assertExists(deals);
this.click(deals);
},
function fail() {
test.assertExists(deals);
});
casper.waitForSelector("selector",
// select an item from coupons page
function success() {
test.assertExists("selector");
this.click("selector");
},
function fail() {
test.assertExists("selector");
});
casper.waitForSelector(".discraption button",
// clicked on buy button
function success() {
test.assertExists(".discraption button");
this.click(".discraption button");
},
function fail() {
test.assertExists(".discraption button");
});
casper.waitForSelector("#mpCart div div.order-summary.fr > button",
// clicked on proceed button from cart
function success() {
test.assertExists("#mpCart div div.order-summary.fr > button");
casper.debugHTML("#mpCart div div.order-summary.fr > button");
console.log('clicked on proceed button');
},
function fail() {
test.assertExists("#mpCart div div.order-summary.fr > button");
});
casper.thenEvaluate(function(){
console.log('in then evaluate');
angular.element(document.querySelectorAll('#mpCart div div.order-summary.fr > button')).triggerHandler('click');
}, 'CasperJS');
});
casper.run(function() {
test.done();
});
});
Every waitforSelector is working fine, but the control is not going into the casper.thenEvaluate function where I want to trigger an angular click event.
I have tested the
angular.element(document.querySelectorAll('#mpCart div div.order-summary.fr >
button')).triggerHandler('click');
It works fine from the firebug console, but not here.
Any help would be really appreciated.
I debugged the final url call and now I am doing a this.openUrl('that particular url').
As os this scenario it works.
Thanks all for their responses.
Is there a built in way to stop $broadcast events from going down the scope chain?
The event object passed by a $broadcast event does not have a stopPropagation method (as the docs on $rootScope mention.) However this merged pull request suggest that $broadcast events can have stopPropagation called on them.
Snippets from angularJS 1.1.2 source code:
$emit: function(name, args) {
// ....
event = {
name: name,
targetScope: scope,
stopPropagation: function() {
stopPropagation = true;
},
preventDefault: function() {
event.defaultPrevented = true;
},
defaultPrevented: false
},
// ....
}
$broadcast: function(name, args) {
// ...
event = {
name: name,
targetScope: target,
preventDefault: function() {
event.defaultPrevented = true;
},
defaultPrevented: false
},
// ...
}
As you can see event object in $broadcast not have "stopPropagation".
Instead of stopPropagation you can use preventDefault in order to mark event as "not need to handle this event". This not stop event propagation but this will tell the children scopes: "not need to handle this event"
Example: http://jsfiddle.net/C8EqT/1/
Since broadcast does not have the stopPropagation method,you need to use the defaultPrevented property and this will make sense in recursive directives.
Have a look at this plunker here:Plunkr
$scope.$on('test', function(event) {
if (!event.defaultPrevented) {
event.defaultPrevented = true;
console.log('Handle event here for the root node only.');
}
});
I implemented an event thief for this purpose:
.factory("stealEvent", [function () {
/**
* If event is already "default prevented", noop.
* If event isn't "default prevented", executes callback.
* If callback returns a truthy value or undefined,
* stops event propagation if possible, and flags event as "default prevented".
*/
return function (callback) {
return function (event) {
if (!event.defaultPrevented) {
var stopEvent = callback.apply(null, arguments);
if (typeof stopEvent === "undefined" || stopEvent) {
event.stopPropagation && event.stopPropagation();
event.preventDefault();
}
}
};
};
}]);
To use:
$scope.$on("AnyEvent", stealEvent(function (event, anyOtherParameter) {
if ($scope.keepEvent) {
// do some stuff with anyOtherParameter
return true; // steal event
} else {
return false; // let event available for other listeners
}
}));
$scope.$on("AnyOtherEvent", stealEvent(function (event, anyOtherParameter) {
// do some stuff with anyOtherParameter, event stolen by default
}));