Vuejs send data after close page, browser - vue.js

When close a tab or browser, you need to send the data in the form to Vue. It is made through mounted or created and window.addEventListener("beforeunload", this.unload).
Sending failed. When I tried to check the availability of the function, I got "undefined"
mounted() {
window.addEventListener("beforeunload", this.unload);
}
methods: {
unload () {
console.log("START UNLOAD FUNC");
console.log('start sync');
this.test;
console.log('finish sync');
},
test() {
console.log("START TEST");
},
}
I get the following in the log:
[1006/114208.084:INFO:CONSOLE(1)] "START UNLOAD FUNC"
[1006/114208.084:INFO:CONSOLE(1)] "start sync"
[1006/114208.084:INFO:CONSOLE(1)] "finish sync"
How can data be sent when the browser is closed?

let body = this.body;
let headers = {
type: 'application/json'
};
let blob = new Blob([JSON.stringify(body)], headers);
navigator.sendBeacon('http://127.0.0.1:8000/form/', blob);
This work for me

Related

Vue 2 / Nuxt 2 Emit From Axios With Dialog Confirm

i am using Vue 2 / nuxt to emit from a axios post* call which itself is called from a Buefy dialog confirm. The emit from this component will close the window / panel and then re-load the users.
If I call the axios request from the button, this works without any issues, but once being called from the dialog, it just don't work?
*most likely this will be updated to a delete request, just not gotten to that let
See code below:
removeUser() {
this.$buefy.dialog.confirm({
message: 'Continue on this task?',
onConfirm: () => {
this.removeUserFunc()
}
})
},
removeUserFunc() {
// console.log(that)
const that = this
// Build URL
const EndPoint = '/remove_user/' + this.id
this.$axios.post(EndPoint).then((res) => {
// User Remove Message
UserRemoved(this.$swal)
that.$parent.$emit('completed')
// console.log(this.$emit('complete'))
// // Emit 'completed' Message
console.log(that.$emit('completed'))
console.log(that)
}).catch((res) => {
console.log(res)
// Check For Errors
GeneralError(this.$swal)
})
}
I was thinking it was losing access to the correct this, so i was trying to pass that back in, but not sure that is the case?
I have also tried with await, while that sort of works? I think is firing the emit too fast, as it re-loads the users but it still includes the user that as just been deleted?
removeUser() {
this.$buefy.dialog.confirm({
message: 'Continue on this task?',
onConfirm: async() => {
this.removeUserFunc()
await this.$emit('completed')
}
})
},
The this keyword refers to the object the function belongs to, or the window object if the function belongs to no object.
Try to use .bind and use a ES5 function
removeUser() {
this.$buefy.dialog.confirm({
message: 'Continue on this task?',
onConfirm: function() {
this.removeUserFunc()
}.bind(this)
})
},

How to access Vue component data inside callback

Im trying to integrate Paymentez (a payments processor) into my site. I get "success" or "failure" responses after doing a test transaction but cant change data in the Vue component (want to show a modal/dialog).
data: function() {
return {
success: false,
failure: false
}
},
created() {
this.paymentCheckout = new window._PAYMENTEZ.modal({
client_app_code: "***********", // Client Credentials
client_app_key: "***************", // Client Credentials
locale: "es", // User's preferred language (es, en, pt). English will be used by default.
env_mode: "stg", // `prod`, `stg`, `local` to change environment. Default is `stg`
onOpen: function () {
console.log("modal open");
},
onClose: function () {
console.log("modal closed");
},
onResponse: function (response) {
// The callback to invoke when the Checkout process is completed
/*
In Case of an error, this will be the response.
response = {
"error": {
"type": "Server Error",
"help": "Try Again Later",
"description": "Sorry, there was a problem loading Checkout."
}
}
When the User completes all the Flow in the Checkout, this will be the response.
response = {
"transaction":{
"status": "success", // success or failure
"id": "CB-81011", // transaction_id
"status_detail": 3 // for the status detail please refer to: https://paymentez.github.io/api-doc/#status-details
}
}*/
console.log(response);
document.getElementById("response").innerHTML = JSON.stringify(
response
);
},
});
/* what I want is something like:
if(response.transaction.status == "success") {
this.success = true
}
else if(response.transaction.status == "failure") {
this.failure = true
}
else if (response.error) {
// show error
}
*/
I've added the Paymentez library via CDN and initialized it in a created() hook.
this.success and this.failure remain false.
Cant access this inside the callback
To be able to access the outer this, your callbacks need to be arrow functions. Example:
// ...
onResponse: response => {
this.success = true; // <= works! (changes your component's reactive prop).
console.log('console is your friend', this);
console.log(response);
}
//...
If they're normal functions they overwrite the outer this with their own scope (and that's what this points to inside them). Read more here.

Can't get click_action to work on FCM notifications with web app / PWA

I'm trying to get my "click_action" to take users to specific URLs on notifications that I'm sending to clients, but whatever I do it either does nothing (desktop) or just opens the PWA (android). The messages are coming through fine (checked in Chrome console) but clicking just doesn't seem to work.
I have the following in my service worker, cribbed from various places including other answers provided on this site:
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.14.3/firebase-messaging.js');
// importScripts('/__/firebase/init.js');
/* An empty service worker! */
self.addEventListener('fetch', function(event) {
/* An empty fetch handler! */
});
var firebaseConfig = {
//REDACTED
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
notificationTitle = payload.notification.title;
notificationOptions = {
body: payload.notification.body,
icon: payload.notification.icon,
click_action: payload.notification.click_action
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
self.addEventListener('notificationclick', function(event) {
let url = event.notification.click_action;
// I've also added a data.click_action field in my JSON notification, and have tried using that
// instead, but that didn't work either
console.log('On notification click: ', event.notification.tag);
event.notification.close(); // Android needs explicit close.
event.waitUntil(
clients.matchAll({ includeUncontrolled: true, type: 'window' }).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (clients.openWindow) {
return clients.openWindow(url);
}
})
);
});
self.onnotificationclick = function(event) {
let url = event.notification.click_action;
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({ includeUncontrolled: true, type: 'window' }).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == url && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow(url);
}));
};
The notifications come through fine on both android (installed PWA) and chrome, and the message payload in the developer console is well formatted and received fine. In the message I'm sending from the server I have a URL with a custom parameter on the end (e.g. https://[domain]/list.php?userid=123) but, as above, clicking on the notification doesn't do anything on windows/chrome, and on the android it opens the PWA successfully but then doesn't go to the URL in the payload, it just goes to wherever the PWA was when last open. The "userid" changes depending on the message trigger.
Sample JSON of message payload:
{data: {…}, from: "xxx", priority: "high", notification: {…}, collapse_key: "do_not_collapse"}
collapse_key: "do_not_collapse"
data: {gcm.notification.badge: "[logo URL]", click_action: "https://[URL]/list.php?userid=33"}
from: "xxx"
notification:
body: "'5' has just been added"
click_action: "https://[URL]/list.php?userid=33"
icon: "https://[logo URL]"
title: "alert "
I also saw something about "webpush": { "fcm_options": { "link": "https://dummypage.com"}} on https://firebase.google.com/docs/cloud-messaging/js/receive but couldn't figure out if that was relevant or needed also.
Am very surprised just providing a URL in the click_action doesn't seem to just do that action when you click the notificaiton! Is anything needed in the service worker at all?!?!
Could one of the problems be that the PWA doesn't update the SW regularly, and so if my code above should work (a big if!) then i just need to wait for the SW to update on the installed android app? If so, is there a way to speed up its updating?!?
Thanks so much in advance for any assistance. Am tying myself in knots here!
I spent a lot of time looking for a solution for the same problem. Maybe this can help :
if you send notification with firebase messaging, you can use webpush field. firebase messaging client library execute self.registration.showNotification() ... No more need messaging.onBackgroundMessage in your service worker.
// firebabse-coud-function.js
app.messaging().send({
webpush: {
notification: {
title: notification?.title || "Default title",
icon: notification?.icon || "/icon.png",
badge: notification?.icon || "/icon.png",
},
fcmOptions: {
link: `${BASE_URL || ""}${notification?.clickAction || "/"}`,
}
},
data: {
userID: notification.userID,
link: notification?.clickAction || "/",
},
topic
});
Most importantly, in your service worker add a 'notificationclick' event listener before calling firebase.messaging()
so my service worker looks like:
// firebase-messaging-sw.js
// ...
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = event.notification?.data?.FCM_MSG?.data?.link;
// ...
})
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
// received others messages
})
For me, clicking on the event does not go to the correct url. So i add this:
// background client - service worker
const channel = new BroadcastChannel('sw-messages');
self.addEventListener('notificationclick', function (event) {
console.debug('SW notification click event', event)
const url = event.notification?.data?.FCM_MSG?.data?.link;
channel.postMessage({
type: 'notification_clicked',
data: {
title: event.notification.title,
clickAction: url
}
});
})
// foreground client
const channel = new BroadcastChannel('sw-messages');
channel.addEventListener("message", function (event) {
// go the page
})
I hope this helps someone.
This question and other answers seems to be related to the legacy FCM API, not the v1.
In those case, I needed the SW to open any url sent by FCM, which is by default not possible because host differs (see here).
Also, the notification object as changed, and the url for the webpush config is there now: event.notification.data.FCM_MSG.notification.click_action
So adapting others answers to get the correct field and open the url by only editing the firebase-messaging-sw.js:
importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.10/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
...
})
self.addEventListener('notificationclick', function(event) {
event.notification.close();
// fcp_options.link field from the FCM backend service goes there, but as the host differ, it not handled by Firebase JS Client sdk, so custom handling
if (event.notification && event.notification.data && event.notification.data.FCM_MSG && event.notification.data.FCM_MSG.notification) {
const url = event.notification.data.FCM_MSG.notification.click_action;
event.waitUntil(
self.clients.matchAll({type: 'window'}).then( windowClients => {
// Check if there is already a window/tab open with the target URL
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
// If so, just focus it.
if (client.url === url && 'focus' in client) {
return client.focus();
}
}
// If not, then open the target URL in a new window/tab.
if (self.clients.openWindow) {
console.log("open window")
return self.clients.openWindow(url);
}
})
)
}
}, false);
const messaging = firebase.messaging();
(register the addEventListener before initializing messaging)
Just add addeventlistner notification click event before calling firebase.messaging()
Everything will work fine.
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.4.1/firebase-messaging.js');
self.onnotificationclick = function(event) {
console.log('On notification click: ', event.notification.tag);
event.notification.close();
// This looks to see if the current is already open and
// focuses if it is
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == '/index' && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow('/index');
}));
};
var firebaseConfig = {
apiKey: "xcxcxcxcxcxc",
authDomain: "xcxcxc.firebaseapp.com",
projectId: "fdsfdsdfdf",
storageBucket: "dfsdfs",
messagingSenderId: "sdfsdfsdf",
appId: "sdfsdfsdfsdfsdfsdf"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();

VueJS data doesnt change on URL change

My problem is that when I go from one user page to another user page the info in component still remains from first user. So if I go from /user/username1 to /user/username2 info remains from username1. How can I fix this ? This is my code:
UserProfile.vue
mounted() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
And this is from actions.js file to get user:
const getUserProfile = async ({
commit
}) => {
try {
const response = await API.get('/user/' + router.currentRoute.params.username);
if (response.status === 200 && response.data.user) {
const data = {
success: true,
user: response.data.user,
invites: response.data.invites
}
return data;
} else {
return console.log('Something went wrong.');
}
} catch (error) {
console.log(error);
}
};
Should I add watch maybe instead of mounted to keep track of username change in url ?
You can use watch with the immediate property, you can then remove the code in mounted as the watch handler will be called instead.
watch: {
'$route.params.username': {
handler: function() {
this.$store.dispatch('getUserProfile').then(data => {
if(data.success = true) {
this.username = data.user.username;
this.positive = data.user.positiverep;
this.negative = data.user.negativerep;
this.createdAt = data.user.createdAt;
this.lastLogin = data.user.lastLogin;
data.invites.forEach(element => {
this.invites.push(element);
});
}
});
},
deep: true,
immediate: true,
},
}
Your page is loaded before the data is retrieved it seems, you need put a "loading" property in the data and have a v-if="!loading" for your component then it will only render once the display is updated. Personally I would avoid watch if I can it is not great for performance of for fine grained handling.
Yes you should add wach on statement that contain user info.(you may have a problem to watch on object, so you can save user info in json, but im not sure). When user changing - call action, after recived response call mutation that should change a state, then watch this state.
And you might use better syntax to receive data from store. That is really bad idea call dispatch directly from your mouted hook, use vuex documentation to make your code better.

Why success callback is not called in extjs form submission?

I'm trying to upload a file using Ext JS forms and in case of success or failure, show appropriate messages. But I'm not able to get the desired result. I'm not able to make success or failure callbacks work in form.submit action.
What I've done till now is:
Creating a form with this script:
new Ext.FormPanel({
fileUpload: true,
frame: true,
url: '/profiler/certificate/update',
success: function() {
console.log(arguments);
},
failure: function() {
console.log(arguments);
}
}).getForm().submit()
​/*
The response Content-Type is text/html (with charcode=utf8);
The response JSON is: { "success": true }
*/​​
Setting the response Content-Type to text/html based on this answer.
Sending an appropriate JSON result back, based on Ext JS docs. The response captured via Fiddler is:
{"success":false}
or
{"success":true}
I even set the response Content-Type to application/json. But still no success.
I've read links like this and this, but none of them helped. Please note that I also tried another script which creates a form, with an upload field in it, and a save button, and I submitted the form in the handler of the save button. But still no callback is fired.
Here's a working example - Javascript code:
Ext.onReady(function () {
Ext.define('ImagePanel', {
extend: 'Ext.form.Panel',
fileUpload: true,
title: 'Upload Panel',
width: 300,
height: 100,
onUpload: function () {
this.getForm().submit({
url: 'upload.php',
scope: this,
success: function (formPanel, action) {
var data = Ext.decode(action.response.responseText);
alert("Success: " + data.msg);
},
failure: function (formPanel, action) {
var data = Ext.decode(action.response.responseText);
alert("Failure: " + data.msg);
}
});
},
initComponent: function () {
var config = {
items: [
{
xtype: 'fileuploadfield',
buttonText: 'Upload',
name: 'uploadedFile',
listeners: {
'change': {
scope: this,
fn: function (field, e) {
this.onUpload();
}
}
}
}
]
};
Ext.apply(this, Ext.apply(this.initialConfig, config));
this.callParent(arguments);
}
});
var panel = Ext.create('ImagePanel', {
renderTo: Ext.getBody()
});
});
And PHP code:
<?php
if (isset($_FILES)) {
$temp_file_name = $_FILES['uploadedFile']['tmp_name'];
$original_file_name = $_FILES['uploadedFile']['name'];
echo '{"success": true, "msg": "'.$original_file_name.'"}';
} else {
echo '{"success": false, "msg": "No Files"}';
}
I have been struggling with this for quite some time now as well. Here's my code:
Ext.getCmp('media-upload-form').getForm().doAction('submit', {
url: './services/recordmedia/upload',
method: 'post',
waitMsg: 'Please wait...',
params: {
entityId: this.entityId,
},
failure: function(form, action){
alert(_('Error uploading file'));
this.fireEvent('file-upload');
this.close();
},
success: function(form, action){
this.fireEvent('file-upload');
this.close();
},
scope: this
})
The response was always wrapped in <pre> tags by the browser, what caused the Extj lib not to call the callbacks. To fix this:
make sure your server returns the correct json: {"success":true}
make sure that the content-type is set to text/html
Actually, this is well covered by docs for Ext.form.Panel and Ext.form.Basic. The problem with your code not working is that there are no config options "success", "failure" for the form panel. You should put them in the config object passed to the submit action. So your code should look like:
new Ext.FormPanel({
fileUpload: true,
frame: true
}).getForm().submit({
url: '/profiler/certificate/update',
success: function() {
console.log(arguments);
},
failure: function() {
console.log(arguments);
}
});
Note the difference: In Ext 4, there is a form component (Ext.form.Panel) which is basically a view component concerned with how you form looks, and then there is the underlying form class (e.g. Ext.form.Basic) concerned with the functionality. Form submissions are handled by Ext.form.Basic (or whatever returned by your form.getForm()).