Trigger other key rather than original key using angularjs - angularjs

I am try to trigger " ` " keyCode = 192 when I press " enter " keyCode = 13. I am trying below code but it is not working in contenteditable div. Any idea?
And how to $apply this?
$scope.pressKey = function(event){
if(event.keyCode == 13){
event.preventDefault();
var e=angular.element.Event('keydown');
console.log(e);
e.which=192;
$('#regularDot').trigger(e);
}
<div id="regularDot" ng-keypress="pressKey($event)" class="wf-loading"
ng-model="regularDot" contenteditable="true" n></div>

Don't use pure JS methods like KeyboardEvent() it has cross browser support issues, use jQuery instead.
Now angular.element has basic support of jquery but it does not support Event() method which you used. So best approach is having jquery library in your codebase (with its script tag attached before angularjs in index.html). Then change your code to as follows:
$scope.pressKey = function(event) {
if (event.keyCode == 13) {
event.preventDefault();
var e = jQuery.Event('keypress');
console.log(e);
e.which = 192;
e.keyCode = 192;
$timeout(function() {
$('#regularDot').trigger(e);
});
} else if(event.keyCode == 192){
console.log("Newly triggered event catched with keyCode " + event.keyCode);
}
}
Here I've made just few changes like adding trigger method inside $timeout so that it'll wait small time to complete the preventDefault of previously triggered event before setting (triggering) new event, and doing $apply also. I've changed newly triggered event type to keypress instead of keydown so that after triggering event you'll be able to handle it using ng-keypress on that element (with id regularDot).
Similar plunker example to refer.

Related

Modal is closed when cursor is released outside the modal after Chrome update (angularjs and bootstrap-ui)

Sometimes when I want to quickly select the entire text of an input (within a modal), I begin selecting from the end of the text and move the mouse to the left until the entire text is selected and then I release.
Sometimes this release will occur outside the modal because the mouse movement is fast.
Picture describing the movement:
The problem is that the modal is closed when I release outside.
Question: how can I prevent the modal from closing when releasing outside?
I'm okay with the modal being closed with a click outside. But not okay with the release event.
I'm using:
angularjs 1.5.8
angular-bootstrap 2.5.0 (aka bootstrap-ui)
bootstrap 3.3.7 (only css!!! not js, because js is provided by the above)
Update:
I've created a plunkr and a GIF:
https://plnkr.co/edit/mxDLAdnrQ4p0KKyw?p=info
<div class="modal-body">
<div class="form-group">
<label for="">Foo</label>
<input type="text" class="form-control input-sm" ng-model="foo">
<p>Do this: select the text from right to left and release the mouse outside the modal.</p>
</div>
</div>
GIF:
Update 2
I have new information! This started happening after the last Goole Chrome update! I tried with another computer that had the previous version of Chrome and the modal doesn't close.
//prevent modal close when click starts in modal and ends on backdrop
$(document).on('mousedown', '.modal', function(e){
window.clickStartedInModal = $(e.target).is('.modal-dialog *');
});
$(document).on('mouseup', '.modal', function(e){
if(!$(e.target).is('.modal-dialog *') && window.clickStartedInModal) {
window.preventModalClose = true;
}
});
$("#modal").on("hide.bs.modal", function (e) {
if(window.preventModalClose){
window.preventModalClose = false;
return false;
}
});
The original repository has been archived and no contributions are accepted.
I forked a version and added my fixes for those who are interested:
https://github.com/peteriman/bootstrap
The comparison below:
https://github.com/angular-ui/bootstrap/compare/master...peteriman:modal-patch
= // moved from template to fix issue #2280
- element.on('click', scope.close);
+ var ignoreClick = false;
+ element.on('mousedown', function(evt1) {
+ element.one('mouseup', function(evt2) {
+ if (evt1.target !== evt2.target)
+ ignoreClick = true;
+ });
+ });
+ element.on('click', function(){
+ if (ignoreClick) ignoreClick = false;
+ else scope.close.apply(this, arguments);
+ });
As mousedown and mouseup events trigger before click event, the code checks if mousedown and mouseup are on the same element. If on different elements, it sets ignoreClick=true for the click event to not trigger.
Maintains backward compatibility for click event for existing codes that calls element.click() programmatically.
Original problem:
https://plnkr.co/edit/mxDLAdnrQ4p0KKyw?p=info&preview
Solution by me: (plkr, modal.js, line 103-114)
https://plnkr.co/edit/V42G9NcTUnH9n9M4?p=info&preview
I updated only the code referring to "Modal.js" in bootstrap.js and bootstrap.min.js
Corrected version:
* Bootstrap: modal.js v3.4.1
* https://getbootstrap.com/docs/3.4/javascript/#modals
bootstrap.js print
Yes, this started happening again after the last Goole Chrome update Version 74.0.3729.169, is this a bug with Chrome we can't fix and that we'll just have to wait for a Chrome update for it to be resolved?
or a bootstrap maintainer will update the code for fixing this?
Issue url: https://github.com/twbs/bootstrap/issues/28844
This problem is not recent is already mentioned on github
https://github.com/angular-ui/bootstrap/issues/5810
the following solution works very well with small improvements if necessary.
$rootScope.$watch(() => document.querySelectorAll('.modal').length, val => {
//everytime the number of modals changes
for (let modal of document.querySelectorAll('.modal')) {
if ($uibModalStack.getTop().value.backdrop !== 'static') { // Testing if the
modal is supposed to be static before attaching the event
modal.addEventListener('mousedown', e => {
if (e.which === 1) {
$uibModalStack.getTop().key.dismiss()
}
})
modal.querySelector('.modal-content').addEventListener('mousedown', e => {
e.stopPropagation()
})
}
}
if (val > 0) {
$uibModalStack.getTop().value.backdrop = 'static'
}
})
Another solution on the same principle that keeps the draggrable footer and header of the modal
$rootScope.$watch(function () {
return $document.find('.modal').length;
}, function (val) {
if(openedWindows.top() ) {
var modal = $document.find('.modal');
angular.forEach(modal, function(value) {
if ($modalStack.getTop().value.backdrop !== 'static') {
value.addEventListener('mousedown', function (e) {
if (value === e.target && e.which === 1 && openedWindows.top()) {
$modalStack.getTop().key.dismiss();
}
});
}
});
if (val>0) {
$modalStack.getTop().value.backdrop = 'static';
}
}
});
I'm using Bootstrap v3.0.0 and ran into the same problem. In the end, I had to change a click event to a mousedown event.
In my bootstrap.js file, under the modal.js section, I changed this.$element.on('click.dismiss.modal', $.proxy(function (e) to this.$element.on('mousedown.dismiss.modal', $.proxy(function (e). and everything appears to be working. You may also have to change this in the bootstrap.min.js file.
Note, this will immediately close the modal on mouse down of backdrop so if for some reason you want a user to be able to click down on the backdrop, then drag the mouse and release on the modal, this will not work.
Have you tried using backdrop: 'static'. I think that should do the trick. It is present in the documentation here
Add css padding around modal window and resize it larger. Click outside still works but releasing mouse while dragging over the edge won't close it.
I had a similar situation with range slider. leaving click during slide outside the modal closes it. so I removed data-toggle="modal" and data-target="#mymodal" and added a click event with extra parameters
jQuery('button#modal_toggler').click(function(){
jQuery('#myModal').modal({
backdrop: 'static',
keyboard: false
})
})
backdrop to disable modal close on clicking outside
keyboard this is for my scenario, to disable keyboard entry for closing modal
I have figured out different way to solve the problem, idk if it will cause a problem later but anyway it works, so basically, I put modal-dialog to another <div> object (I call it modal-helper) and then put it to modal. The modal-helper element width and height are inherited (100%) as default but there is small space on top so you can use some margin and padding to close it.
<div class="modal fade" id="login-modal" tabindex="-1" aria-labelledby="loginModalLabel" style="display: none;" aria-hidden="true">
<div id="modal-helper" style="pointer-events: auto;">
<div class="modal-dialog">
...
</div>
</div>
</div>
Then I have used some JS to hide modal when modal-helper (as backdrop) is clicked (by the 'clicked' I mean when pointerup event triggered after pointerdown event on modal-helper).
The code below sets the value of isPointerDownToModalHelper true when pointerdown event triggered on modal-helper, then when the pointerup event triggered on any object it hides the modal and sets the value of isPointerDownToModalHelper back to false:
var login_modal_helper = document.getElementById('modal-helper')
var isPointerDownToModalHelper = false;
addEventListener('pointerdown', (event) => {
var objectID = event['path']['0']['id'];
if (objectID === login_modal_helper.id) { // if pointer was over modal-helper
isPointerDownToModalHelper = true;
}
});
addEventListener('pointerup', (event) => {
if (isPointerDownToModalHelper === true) {
isPointerDownToModalHelper = false;
$('#login-modal').modal('hide'); // hide the modal
}
});
It seems to work fine for now, I hope it can help someone :).

fireEvent('click') not working in IE11 - Extjs

I am planning to trigger a click event programatically when user presses a spacebar key. I have been used fireEvent('click') it is working for chrome, but It is not working for IE-11. I also tried, dispatchEvent for IE but it is throwing an error: "element does not support method or property dispatchEvent". Please follow below code.
onCustomRender: function(thisEl, args){
var fetchTrigger = thisEl.getTrigger('fetchID
fetchTrigger.el.on('keydown',function(e,thisEl){
if (e.keyCode === 32) {
//fetchTrigger.el.fireEvent('click'); //this is working in chrome //not working in IE and did not throwing error.
var evObj = document.createEvent('MouseEvents');
evObj.initEvent('click', true, false);
fetchTrigger.el.dispatchEvent(evObj);
}
});
}
Please Help
Thanks in Advance
First of all you have syntax error in the code you provided...
Calling the function directly is easiest (as Evan said) but if you want to keep the logic in the controller you can fire events. Else you need some ugly code to get the controller first, then call the method...
I don't like fireing events and attaching listeners on the 'Ext.dom.Element' if you don't have to. Just use your ext elements..
Here is what I would do:
onCustomRender: function(thisEl, args){
var fetchTrigger = thisEl.getTrigger('fetchID');
fetchTrigger.on('keydown',function(trigger, e){
if (e.keyCode === 32) {
trigger.fireEvent('click', trigger);
}
});
}
Or even better, use the controller:
implement listeners
init: function () {
this.control({
'#fetchID': { //you can optimize the componentQuery
keydown: this.onTriggerKeyDown,
click: this.onTriggerClick
}
});
},
and methods like this:
onTriggerKeyDown: function(trigger, e){
if (e.keyCode === 32) {
this.onTriggerClick(trigger);
}
},
onTriggerClick: function(trigger) {
//do you thing!
}

Preventing line breaks - ENTER keypresses. Instead - fire custom event

I'd like to use Aloha to WYSIWYG-edit single-line headlines.
For that I need to intercept ENTER keypress (to prevent line-break) and fire custom save event.
Is there a way to do that?
Thanks!
Here's what worked for me on "hotfix" branch:
Aloha.bind('aloha-smart-content-changed', function(e, data) {
value = data.editable.getContents();
// save it to database.
});
Aloha.bind('aloha-command-will-execute', function(e, data) {
if (data.commandId == 'insertlinebreak') {
data.preventDefault = true;
}
});
More info here:
https://github.com/alohaeditor/Aloha-Editor/issues/1502

angular ui-grid/ng-grid filterChanged firing too frequently

I'm trying to implement a grid in angular, using server-side sorting, server-side pagination and server-side filtering. Using ui-grid (unstable), I added ui.grid.paging and got everything up and running. Very nice, kudos to the developers.
However.... $scope.gridApi.core.on.filterChanged is firing for every key pressed, so when I search for "Patrick" in the givenname column, seven events are fired and seven get-requests hit my server. Even worse, since it is a large set I'm filtering, this operation is pretty expensive and the results my even overtake each other, like the most specific filter getting the fastest result, triggering success before a less specific result is processed.
I'd like to slow it down, like "fire after entry has stopped". I'm pretty much new to javascript and REST, this is my first real-world-project. I would really appreciate any ideas how to handle this issue. It feels like a common scenario, so there might be some standard solutions or best practices I'm missing.
Yours,
Patrick.
If you wanna go "all angular" I would suggest to use a $timeout inside the on.filterChanged event handler:
if (angular.isDefined($scope.filterTimeout)) {
$timeout.cancel($scope.filterTimeout);
}
$scope.filterTimeout = $timeout(function () {
getPage();
}, 500);
where 500 is the time (in milliseconds) you would like to wait between each on.filterChanged event before going to the server and getPage() is the function that actually goes to the server and retrieves the data.
Don't forget to cancel your $timeout on the controller's 'destroy' event:
$scope.$on("$destroy", function (event) {
if (angular.isDefined($scope.filterTimeout)) {
$timeout.cancel($scope.filterTimeout);
}
});
I was dealing with the same problem and I came up with another solution which IMHO is more "Angular-friendly". I used the ngModelOptions directive introduced in Angular 1.3.
I replaced uiGrid's default filter template ("ui-grid/ui-grid-filter") by a custom one, and configured the ngModelOptions directive on the input with a default debounce value of 300 ms and 0 ms for blur.
This is a sample template based on ui-grid 3.0.5 original template where I also changed default CSS classes by Bootstrap classes:
$templateCache.put('ui-grid/ui-grid-filter-custom',
"<div class=\"ui-grid-filter-container\" ng-repeat=\"colFilter in col.filters\" ng-class=\"{'ui-grid-filter-cancel-button-hidden' : colFilter.disableCancelFilterButton === true }\">" +
"<div ng-if=\"colFilter.type !== 'select'\"><input type=\"text\" class=\"input-sm form-control\" ng-model=\"colFilter.term\" ng-model-options=\"{ debounce : { 'default' : 300, 'blur' : 0 }}\" ng-attr-placeholder=\"{{colFilter.placeholder || ''}}\" aria-label=\"{{colFilter.ariaLabel || aria.defaultFilterLabel}}\"><div role=\"button\" class=\"ui-grid-filter-button\" ng-click=\"removeFilter(colFilter, $index)\" ng-if=\"!colFilter.disableCancelFilterButton\" ng-disabled=\"colFilter.term === undefined || colFilter.term === null || colFilter.term === ''\" ng-show=\"colFilter.term !== undefined && colFilter.term !== null && colFilter.term !== ''\"><i class=\"ui-grid-icon-cancel\" ui-grid-one-bind-aria-label=\"aria.removeFilter\"> </i></div></div>" +
"<div ng-if=\"colFilter.type === 'select'\"><select class=\"form-control input-sm\" ng-model=\"colFilter.term\" ng-attr-placeholder=\"{{colFilter.placeholder || aria.defaultFilterLabel}}\" aria-label=\"{{colFilter.ariaLabel || ''}}\" ng-options=\"option.value as option.label for option in colFilter.selectOptions\"><option value=\"\"></option></select><div role=\"button\" class=\"ui-grid-filter-button-select\" ng-click=\"removeFilter(colFilter, $index)\" ng-if=\"!colFilter.disableCancelFilterButton\" ng-disabled=\"colFilter.term === undefined || colFilter.term === null || colFilter.term === ''\" ng-show=\"colFilter.term !== undefined && colFilter.term != null\"><i class=\"ui-grid-icon-cancel\" ui-grid-one-bind-aria-label=\"aria.removeFilter\"> </i></div></div>" +
"</div>"
);
The final step for this to work is to set this template in every columnDef where you enable filtering:
columnDefs: [{
name: 'theName',
displayName: 'Whatever',
filterHeaderTemplate: 'ui-grid/ui-grid-filter-custom'
}]
Unfortunately, I couldn't find any way to define this template globally, so I had to repeat the filterHeaderTemplate everywhere... That's the only drawback, but on the other hand, you can also add more filters to your custom template if you need to.
Hope it helps!
I'd recommend that you take a look at reactive-extensions for Javascript. Reactive is built for precisely these kinds of scenarios. There is a method called .Throttle(milliseconds) which you can attach to an observable that will call a handler (to hit your API) after x milliseconds have passed for the user not entering anything.
Rx-Js and angular play well together
Here is an example from one of my projects of doing something similar:
observeOnScope($scope, 'tag', true)
.throttle(1000)
.subscribe(function (data) {
if (data.newValue) {
$http({
url: api.endpoint + 'tag/find',
method: 'GET',
params: {text: data.newValue}
}).then(function (result) {
$scope.candidateTags = result.data;
})
}
});
This code takes $scope.tag and turns it into an observable. The .throttle(1000) means that after 1 second of $scope.tag not changing the subscribe function will be called where (data) is the new value. After that you can hit your API.
I used this to do typeahead lookup of values from my API so hitting the backend everytime a letter changed obviously wasn't the way to go :)
JavaScript setTimeout function can also be used for giving delay as given below.
$scope.gridApi.core.on.filterChanged( $scope, function() {
if (angular.isDefined($scope.filterTimeout)) {
clearTimeout($scope.filterTimeout);
}
$scope.filterTimeout = setTimeout(function(){
getPage();
},1000);
});

keydown and keypress events not working in angular

I have a directive where I am trying to bind the keypress and keydown events but for some reason they will not register.
element.bind("keydown keypress", function (event) {
console.log("here");
});
Any clues why this could be ?
If you look at the file selectMe.js in this plnkr you can see the binding.
Try adding tabindex="0" to the <table>. With this you make the <table> focusable with a click or pressing tab. However you may be interested on starting listening to key events as soon as the page is loaded. Then you should bind the event to document:
angular.element(document).bind(...)
If you bind to the table element, you won't get key events because the table doesn't have focus. See this question for info on which elements can have focus.
The events do bubble up however, so you can either add an invisible input or anchor element and get the keyboard events when they bubble up, or just listen on your document element to get any unhandled events. You can also check the ctrlKey flag on the key event instead of tracking the control key yourself:
var doc = angular.element(document);
doc.on('keyup', function(e) {
if (e.ctrlKey) {
if (e.keyCode === cKey) {
$scope.valueToBeCopied = e.target.value;
console.log('copy: valueToBeCopied: ' + $scope.valueToBeCopied);
}
if (e.keyCode === vKey) {
e.target.value = $scope.valueToBeCopied;
console.log('paste: valueToBeCopied: ' + $scope.valueToBeCopied);
}
}
});
I don't know how much that helps though with what you seem to be attempting. You'll have to track which element has the virtual 'focus' some other way.

Resources