Angular, resume event propagation - javascript

When user click a button there is a directive that catches this event and stops it. Then an modal is opened witch asks for user confirmation. If user confirms then I need to resume previously event.
How do I resume stopped event?
example:
markAsSeen($event) : void {
// pause whatever user wanted to click
$event.stopPropagation();
// open modal and ask user for confirmation
let modalInstance = this.modalService.openConfirmationModal();
// on modal close, if positive event continue whatever user clicked
modalInstance.onClose((response) => {
if(response) {
// this line should resume $event
$event.originalEvent(); // how to achieve this?
}
})
}

For me this is two different events, the first one is here to openModal but looks useless (why don't you just open a modal ?), the second one to confirm when the user clicked Confirm.
For me that's the easiest way : if you need the first event emitter, then only open the modal, the second one start the confirmation process if positive. The other way could be to add a "status" variable in your confirmation -1 for not started (= modal closed), 1 for positive confirmation, 0 in progress.
Finally to avoid user to click away, use something like
onClick(event) {
if (!this.element.nativeElement.contains(event.target)) {
closeModal(); // or not
}
}
Where event.target is the clicked target
Edit : onClick must be added to #Component
#Component({selector..., host: {
'(document:click)': 'onClick($event)',
}});

Related

In SvelteKit, is there a way to cancel a popstate event if user state isn't saved?

I have a component that allows a user to edit their state. This triggers a notSaved variable. I have a beforeunload event handler to handle reload and exiting the page to remind the user to save their state, but using SvelteKit, using the back button in the browser doesn't seem to trigger the beforeunload event. I also have a popstate event handler, because that is triggered when the back button is clicked, but I can't figure out how to prevent the window.history from changing if the notSaved variable is true.
Is there a way in SvelteKit to trigger a Changes you made may not be saved. popup similar to the one that is triggered on a beforeunload event when the back or forward button is pressed?
You can use the beforeNavigate function to be notified of a navigation intent in order to potentially block it, for example:
<script>
import { beforeNavigate } from '$app/navigation';
let unsavedWork = false;
let value = 0;
beforeNavigate(({ from, to, cancel }) => {
if (unsaved) {
cancel();
showUnsavedWorkPopup();
}
});
function onChange() {
...
unsavedWork = true;
}
async function save() {
await ...
unsavedWork = false;
}
</script>
<input type="number" bind:value on:change={onChange}>

Prevent user to click anywhere on page if changed value is not submitted

in my app, the user has multiple inputs, and I use #hostListener for click outside, because I have some complicated inputs (multiple checkboxes where I first need to check everything and after call submits on click outside, IMG-MAP, etc....).
--- TEXT INPUT ---
*add some value...*
-> click outside -> Submit
--- RADIO BUTTON ---
*change option...*
-> click outside -> Submit
...etc
how to in angular 9+ prevent users to click anywhere (menu, another input ...)? If the user change the value and click anywhere or on any other part of the app ( any button, menu item, another input) submit method for changed input needs to be executed instead of that where he clicked.
I try to add event.PreventDefault(), but if we click on the menu or on another button it is not triggered submit for changed value, it is a triggered method for clicked item (menu item...).
#HostListener('document:click', ['$event'])
clickOut(event) {
event.preventDefault(); // if change val in text input and click directlly on radio button option, radio button option i changed and trigered submit for text input, and that is not ok, option in radio butoon can't be changed
this.submit()
}
why not use (blur)?
Another idea can be get the event.target as HTMLElement; and check if is inner to your form to not execute event.PreventDefault()
#ViewChild('myForm',{read:ElementRef}) formElement:ElementRef
#HostListener('document:click', ['$event'])
clickOut(event) {
const clickTarget = event.target as HTMLElement;
if (this.formElement.nativeElement.contains(clickTarget))
{
console("inside myForm")
}
else
{
console("outside myForm")
}
}
NOTE: I don't check the code (I'm in hurry), but I imagine work

Prevent tab change on PrimeVue TabView component

I am using the TabView component from PrimeVue (Vue 3), and I want to stop the tab change if any changes are made by the user, the problem is that I don't know how. I've already tried passing the event and using preventDefault and stopPropagation but seems that it doesn't work and click event is still happening.
The procedure should be:
If any changes are made, user press the tab and a dialog appears.
If user clicks 'No', I should prevent the tab change and stop the click event
Here is the demo of what I'm trying to archive, should be simple https://codesandbox.io/s/aged-wave-yzl1k?file=/src/App.vue:0-1753
If a flag is true I want to show a confirm dialog and prevent the tab change if user dismiss it.
The component that I'm using for the TabView: https://primefaces.org/primevue/showcase/#/tabview
Thanks in advance,
From the docs it looks like that internally the component will first switch tabs and then emit "tab-click", which explains the issue you're seeing. The exception is if the tab is disabled, in which case it won't change tabs but will emit "tab-click".
It took a bit to figure out, but there is a way to get the functionality you need with only a small adjustment. It requires a change in your main.js as well as in your App.vue file.
// main.js
/*
* Put this after you import TabView.
* This will prevent automatic tab switching but still emits
* the event to your application.
*/
TabView.methods.onTabClick = function(event, i) {
this.$emit('tab-click', {
originalEvent: event,
index: i
});
}
// App.vue
const onTabClick = (event) => {
if (changes.value) {
confirm.require({
message:
"Are you sure that you want to leave this tab? You'll lose your changes",
icon: "fal fa-exclamation-triangle",
acceptLabel: "Yes",
rejectIcon: "No",
accept: () => {
alert("here we should allow tab change");
activeIndex.value = event.index; // manually set activeIndex
},
reject: () => {
alert("stop tab change");
},
});
}
};
These changes modify what the onTabClick library method to only emit the event, without automatically switching. Then in your app you can check the index property of the event to determine what should be set to active.

Nested event.subscribe solution

I'm using Angular dragula drag and drop, which allow me to move bootstrap cards all around the view.
When you "drop" some item, it triggered this.dragulaService.drop.subscribe(), inside that I can cancel or roll back item dragged before the move is done.
I want to use my modal in there. which looks like this :
let subscription = this.modalService.onSubmit.subscribe((val) => {
That modal subscription keep thread waiting for user clicking on OK or CANCEL in modal, which says "Are you sure you want to move this item?"
The problem, is that otter event subscription (dragula) does not wait for modal event subscription to finish, dragula just ends before modal confirmation and it's a problem for me. Because when I try to call drake.cancel, it does not exist any more.
CODE :
this.dragulaService.drop.subscribe(
value => {
let modal: ModalComponent = this.modalService.GetModal();
let subscription = this.modalService.onSubmit.subscribe((val) => {
//Here if user clicks OK, we do nothing and drag-drop finish successfuly
//If user clicks Cancel, we call this.dragulaService.find('bag-deals').drake.cancel(true);
You can use a concatMap in a pipe, here is an interesting article about Higher-Order RxJS Mapping Operators
I used it in your case in that way:
let dropValue;
this.dragulaService.drop
.pipe(concatMap(value => {
dropValue = value;
const modal: ModalComponent = this.modalService.GetModal();
return this.modalService.onSubmit;
})).subscribe(value => {
//Here if user clicks OK, we do nothing and drag-drop finish successfuly
//If user clicks Cancel, we call this.dragulaService.find('bag-deals').drake.cancel(true);
});

$ionicPlatform onHardwareBackButton runs multiple times

I have an issue with the back button running more than once.
currently I'm in my "messages" $state, and if I press the back button the following code works as normal.
var messageIsClosed = true;
$ionicPlatform.onHardwareBackButton(function(event){
event.stopPropagation();
handleBackButton();
})
var handleBackButton = function(){
if(messageIsClosed){
$state.go("dash");
} else {
messageIsClosed = false;
}
}
however, if I go to another $state (say, "dash") and then return to "messages", pressing the back button will make the above code run twice. Then if I go back to "messages" again it runs 3 times, then 4. For each time I visit the "messages" view/controller the back button code will run an extra time
I have no idea why
The onHardwareBackButton will run multiple times and this is normal in your case. This is because you are registering the event every time you visit the messages state.
To avoid the multiple registration of the event you could useoffHardwareBackButton() and de-register the event when moving away from the current state.
Example code:
This is the callback
var hardwareBackButtonHandler = function() {
// add you back button logic here
console.log('Hardware back button pressed');
}
Register the back button event like that:
$ionicPlatform.onHardwareBackButton(hardwareBackButtonHandler);
Then when moving away from the current state you can un-register like that:
$ionicPlatform.offHardwareBackButton(hardwareBackButtonHandler);

Categories