Currently I am having an ion-button with click event which calls a method.
<ion-button expand="full" color="primary" (click)="sendMsg()">Tap</ion-button>
sendMsg method contains the statements to push the objects to an array and opens modal on some condition.
sendMsg = () =>{
// statements to push an objects to an array(this is an array displays on chat page);
this.openModal();
}
async openModal() {
const myModal = await this.modalController.create({
component: ModalPage,
componentProps: {
firstAction: this.firstAction,
secondAction: this.secondAction,
thirdAction: this.thirdAction
},
cssClass: 'modal-css',
backdropDismiss: false
});
It's a chat page where we get the messages on click of TAP button and while tapping in between we show an ion modal . The issue here is when we tap super fast and modal comes up in one of the click event and since we are clicking fast I could see the messages displaying which are suppose to display after the modal comes up..
To avoid this , I thought of adding debounceTime which can have some time delay and considers the latest click event and this was working in normal angular world.
I have followed https://coryrylan.com/blog/creating-a-custom-debounce-click-directive-in-angular but it didn't work under ionic..
Any thoughts are really appreciated..
use a subject as event emitting source and control the click rate from there
const openModalAction=new Subject()
sendMsg = () =>{
// statements to push an objects to an array(this is an array displays on chat page);
openModalAction.next()
}
const openModal=defer(()=>from(this.modalController.create({
component: ModalPage,
componentProps: {
firstAction: this.firstAction,
secondAction: this.secondAction,
thirdAction: this.thirdAction
},
cssClass: 'modal-css',
backdropDismiss: false
})))
const openModalAction.pipe(dounceTime(1000),switchMap(_=>openModal)
Related
In my simple ToDo app i am using useQuery() to fetch ToDo's from the server as well as useMutation() to create, update and delete ToDo's. When a mutation is successful, i invalidate the query so it gets refetched.
If i press the delete button of a ToDo item, i want the corresponding button to show a loading spinner until the mutation is done and the new ToDo's have been fetched. For that purpose i am using the useIsFetching() hook in my components, which works fine. However, here is the problem:
If i now execute a mutation, every button (meaning the "Delete" button as well as the "Submit" and "Save changes" button to post or update a ToDo) will show the loading spinner instead of just the one that i pressed. This makes sense since they all depend on the same value of useIsFetching(). I need a way to figure out which mutation led to the refetching of the query so i can conditionally render the loading spinner for the appropriate button. This seems to be a very common problem for me yet i cannot find a (not overcomplicated) solution for it. Is there something i'm missing?
The solution Ahmed Sbai said above is good (you can use state instead of local variables), and here is another approach for you.
You can check condition based on isLoading in the object returned from useMutation().
Updated: As written in this TkDodo's blog, the "magic" is here:
If you want your mutation to stay in loading state while your related queries update, you have to return the result of invalidateQueries() from the callback.
Therefore, you won't need to use the useIsFetching() hook, too.
function App() {
const addMutation = useMutation({
mutationFn: (newTodo) => {
return axios.post('/todos', newTodo)
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: ['todos'],
})
}
})
const updateMutation = useMutation({
mutationFn: (id, data) => {
return axios.patch(`/todos/${id}`, data)
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: ['todos'],
})
}
})
const deleteMutation = useMutation({
mutationFn: (id) => {
return axios.delete(`/todos/${id}`)
},
onSuccess: () => {
return queryClient.invalidateQueries({
queryKey: ['todos'],
})
}
})
return (
<div>
{/* ... */}
<button
onClick={() => addMutation.mutate(...)}
loading={addMutation.isLoading}
>
Submit
</button>
<button
onClick={() => updateMutation.mutate(...)}
loading={updateMutation.isLoading}
>
Save changes
</button>
<button
onClick={() => deleteMutation.mutate(...)}
loading={deleteMutation.isLoading}
>
Delete
</button>
</div>
)
}
If you want any further information, please read more in Docs.
You can simply create a variable var loadingType = 0 and update its value each time the user click on a button for example if the delete button is clicked then loadingType = 1 if update button loadingType = 2, etc. Then based on the value of loadingType you know which loading spinner you have to use.
I am trying to add a click listener to a button in a leaftlet popup in my ionic app.
Here I am creating the map & displaying markers, also the method I want called when the header tag is clicked is also below:
makeCapitalMarkers(map: L.map): void {
let eventHandlerAssigned = false;
this.http.get(this.capitals).subscribe((res: any) => {
for (const c of res.features) {
const lat = c.geometry.coordinates[0];
const lon = c.geometry.coordinates[1];
let marker = L.marker([lon, lat]).bindPopup(`
<h4 class="link">Click me!</h4>
`);
marker.addTo(map);
}
});
map.on('popupopen', function () {
console.log('Popup Open')
if (!eventHandlerAssigned && document.querySelector('.link')) {
console.log('Inside if')
const link = document.querySelector('.link')
link.addEventListener('click', this.buttonClicked())
eventHandlerAssigned = true
}
})
}
buttonClicked(event) {
console.log('EXECUTED');
}
When I click this header, Popup Open & Inside if are printed in the console, so I know I'm getting inside the If statement, but for some reason the buttonClicked() function isn't being executed.
Can someone please tell me why this is the current behaviour?
I just ran into this issue like 2 hours ago. I'm not familiar with ionic, but hopefully this will help.
Create a variable that keeps track of whether or not the content of your popup has an event handler attached to it already. Then you can add an event listener to the map to listen for a popup to open with map.on('popupopen', function(){}). When that happens, the DOM content in the popup is rendered and available to grab with a querySelector or getElementById. So you can target that, and add an event listener to it. You'll have to also create an event for map.on('popupclose', () => {}), and inside that, remove the event listener from the dom node that you had attached it to.
You'd need to do this for every unique popup you create whose content you want to add an event listener to. But perhaps you can build a function that will do that for you. Here's an example:
const someMarker = L.marker(map.getCenter()).bindPopup(`
<h4 class="norwayLink">To Norway!</h4>
`)
someMarker.addTo(map)
function flyToNorway(){
map.flyTo([
47.57652571374621,
-27.333984375
],3,{animate: true, duration: 5})
someMarker.closePopup()
}
let eventHandlerAssigned = false
map.on('popupopen', function(){
if (!eventHandlerAssigned && document.querySelector('.norwayLink')){
const link = document.querySelector('.norwayLink')
link.addEventListener('click', flyToNorway)
eventHandlerAssigned = true
}
})
map.on('popupclose', function(){
document.querySelector('.norwayLink').removeEventListener('click', flyToNorway)
eventHandlerAssigned = false
})
This is how I targeted the popup content and added a link to it in the demo for my plugin.
So yes you can't do (click) event binding by just adding static HTML. One way to achieve what you want can be by adding listeners after this new dom element is added, see pseudo-code below:
makeCapitalMarkers(map: L.map): void {
marker.bindPopup(this.popUpService.makeCapitalPopup(c));
marker.addTo(map);
addListener();
}
makeCapitalPopup(data: any): string {
return `` +
`<div>Name: John</div>` +
`<div>Address: 5 ....</div>` +
`<br/><button id="myButton" type="button" class="btn btn-primary" >Click me!</button>`
}
addListener() {
document.getElementById('myButton').addEventListener('click', onClickMethod
}
Ideally with Angular, we should not directly be working with DOM, so if this approach above works you can refactor adding event listener via Renderer.
Also I am not familiar with Leaflet library - but for the above approach to work you need to account for any async methods (if any), so that you were calling getElementById only after such DOM element was successfully added to the DOM.
What should happen when users click over back button of phone? In case when modal opens.
Registered a back button:
// To prevent interference with ionic's own backbutton handling
// you can subscribe with a low priority instead
this.platform.backButton.subscribe(() => {
// code that is executed when the user pressed the back button
// and ionic doesn't already know what to do (close modals etc...)
self.modalController.dismiss();
});
The problem with the code:
It closes/dismiss modal is fine!
But it also pushed back the page from where the modal is opened. Means it pop the page behind modal.
This should not happen the page should not pop - only modal should close.
Check the image gif added ->
Click here to see the problem
You may consider using platform.backButton.subscribeWithPriority() with a high priority (ex: 9999).
Then checking if there is a opened modal with modalController.getTop().
constructor(private modalCtrl: ModalController, private nav: NavController) {
}
ngOnInit() {
this.platform.backButton.subscribeWithPriority(9999, () => {
this.closeModalOrPage();
});
}
async closeModalOrPage(){
let modal = await this.modalCtrl.getTop();
if (modal){
modal.dismiss();
} else {
this.nav.pop();
}
}
How can I prevent ionic keyboard from hiding when I press a specific button in my Ionic 1 app?
This solution doesn't work for me, the keyboard remains open wherever I click.
A possible solution can be found here (the same link sent by Sahil Dhir). I also had this problem and this solution worked for me.
The directive is:
angular.module('msgr').directive('isFocused', function($timeout) {
return {
scope: { trigger: '#isFocused' },
link: function(scope, element) {
scope.$watch('trigger', function(value) {
if(value === "true") {
$timeout(function() {
element[0].focus();
element.on('blur', function() {
element[0].focus();
});
});
}
});
}
};
});
Its usage is:
<input type="text" is-focused="true">
What it basically does is to watch the focus of the input and whenever the input loses focus (when you press a button on the screen outside the keyboard, for example) it rapidly assigns the focus back to it. So the keyboard doesn't have time to hide.
Hope it works for you too!
Given the following demo:
jQuery File Upload Basic Plus demo
I have this working in a project as per the demo, but I'd like to remove the "Upload" button on each image and just add an "Upload All" button at the top. For the life of me I can't work out how to do it and the documentation is pretty thin...
I've tried to create a handle to the fileupload object e.g. var fileUpload = $('#fileupload').fileupload({ and call something like fileUpload.send(); but I just get "object doesn't contain a method 'send'"
The working solution is here: Start Upload all in jquery file upload blueimp
The key is unbinding the click event in the "done" option and not in the "add" option as other articles here suggest.
done: function (e, data) {
$("#uploadBtn").off('click')
$.each(data.result, function (index, file) {
$('<p/>').text(file.name).appendTo(document.body);
});
},
add: function (e, data) {
$("#uploadBtn").on('click',function () {
data.submit();
});
}
Another option is to give the individual upload buttons a class, hide them from view by setting their css display to none and then binding their click to the upload_all click:
//Put this button code next to your button (or span mimicking button) that adds files to the queue.
<button id="upload_all" type="submit" class="btn btn-primary start">
<i class="glyphicon glyphicon-upload"></i>
<span>Start upload</span>
</button>
//Give your buttons a class and set their display to none.
var uploadButton = $('<button/>', {
class:"upload",
style:"display:none"
})
.on('click', function () {
var $this = $(this),
data = $this.data();
data.submit().always(function () {
$this.remove();
});
});
//Bind their click to the click of your upload_all button.
$('#upload_all').on('click', function() {
$('.upload').click();
});
You can push all the data into an array and have your external button call a function that loops through the array and call .submit() on each.
var fileDataArray = [];
// Inside "add" event
fileDataArray.push(data);
// Inside your onClick function for your button
for (var i = 0; i < fileDataArray.length; i++) {
var data = fileDataArray[i];
data.submit();
}