How to access vue component data from socket.io listener function? - vue.js

I have this code on my server,
socket.on('join-room', (roomId, passcode, nickname) => {
room = rooms.find((e) => e.id == roomId)
if (!room) {
socket.emit('join-room-error', 'Room not found!',)
return
}
if (room.users.has(nickname)) {
socket.emit('join-room-error', 'Nickname has been used!',)
return
}
if (!room.passcode) {
JoinRoomWithNickname(room, nickname)
return
}
if (room.passcode != passcode) {
socket.emit('join-room-error', 'Wrong passcode!')
return
}
JoinRoomWithNickname(room, nickname)
})
and I want to listen to the error from socket.io. So in the created function I add some listener but it's seem in the listener function I can't access the vue component data
created() {
this.$socket.removeAllListeners()
this.$socket.on('join-room-success', function() {
store.state.roomId = this.id
router.push('/room')
})
this.$socket.on('join-room-error', function(err) {
this.errorLog = err
// I can't access this.errorLog from there
})
},
data: function() {
return {
code: '',
nickname: '',
wasWrongPasscode: false,
isEnteringPasscode: true,
errorLog: null
}
},

You should use arrow functions when you want to pass the global context (this) to the function being invoked.
this.$socket.on('join-room-error', (err) => {
this.errorLog = err
})

Related

How to get the cookie value and put it into the Vuex store after refreshing page in vue

I have a product component and I have an I am adding products into cart there:
addToCart: function () {
this.amount = this.itemsCount !== "" ? this.itemsCount : 1;
if(this.variationId != null) {
this.warningMessage = false;
cartHelper.addToCart(this.product.id, this.variationId, parseInt(this.amount), (response) => {
this.$store.dispatch('addProductToCart', {
cart: response.data,
})
});
} else {
this.warningMessage = true;
}
},
And I also have cart helper where I am making my API calls and store the cart_guid in the cookie:
let cartHelper = {
cartCookieName: "_cart",
cookieValue: "",
getCart: function (callback = undefined) {
return apiHelper.getRequest(
"/carts",
(response) => {
document.cookie = `${this.cartCookieName}=${response.data.attributes.cart_guid};`;
this.cookieValue = response.data.attributes.cart_guid;
if (callback) { callback(response); }
}
)
},
addToCart: function (product, variation_id, amount, callback = undefined) {
if(this.cookieValue == "") {
this.getCart(() => {
this._addToCart(product, variation_id, amount, callback);
});
} else {
this._addToCart(product, variation_id, amount, callback)
}
},
_addToCart(product, variation_id, amount, callback = undefined) {
return apiHelper.postRequest(
`/carts/${this.cookieValue}/add-item`,
(response) => {
document.cookie = `${this.cartCookieName}=${response.data.attributes.cart_guid};`;
if (callback) { callback(response); }
},
{
product_id: product,
variation_id: variation_id,
amount: amount,
}
)
},
export default cartHelper;
(I didnt write the code where I am storing the cart_guid in the cookie. I dont think it is necessary, it is basically cookieValue)
So when I add the product into the cart, I am storing this data in Vuex. For this my action:
export const addProductToCart = ({commit}, {cart}) => {
commit('ADD_TO_CART', {cart});
}
my mutation:
export const ADD_TO_CART = (state, {cart}) => {
state.cart = cart;
}
and my state:
export default {
cart: {
"attributes": {
"items": [],
}
}
}
What I am trying to do when I refresh the page, the values in Vuex are lost but since there is still a cookie with the value cart_guid, I should basically make this call and fill the Vuex again with the cart_guid. But I am quite new in Vuex, so I don't know where I should put the logic. I would be really glad if you give me any hint or code.
There is a onMounted lifecycle where the code inside will run whenever the vue component has been successfully mounted onto the DOM. You can put your function where it retrieves the value of your cookie in there so it will after mounted.

VueJS $set not making new property in array of objects reactive

In my VueJS 2 component below, I can add the imgdata property to each question in the area.questions array. It works - I can see from the console.log that there are questions where imgdata has a value. But despite using $set it still isn't reactive, and the imgdata isn't there in the view! How can I make this reactive?
var componentOptions = {
props: ['area'],
data: function() {
return {
qIndex: 0,
};
},
mounted: function() {
var that = this;
that.init();
},
methods: {
init: function() {
var that = this;
if (that.area.questions.length > 0) {
that.area.questions.forEach(function(q) {
Util.HTTP('GET', '/api/v1/photos/' + q.id + '/qimage').then(function(response) {
var thisIndex = (that.area.questions.findIndex(entry => entry.id === q.id));
var thisQuestion = (that.area.questions.find(entry => entry.id === q.id));
thisQuestion.imgdata = response.data;
that.$set(that.area.questions, thisIndex, thisQuestion);
})
});
}
console.log("area.questions", that.area.questions);
},
Since area is a prop, you should not be attempting to make changes to it within this component.
The general idea is to emit an event for the parent component to listen to in order to update the data passed in.
For example
export default {
name: "ImageLoader",
props: {
area: Object
},
data: () => ({ qIndex: 0 }), // are you actually using this?
mounted () {
this.init()
},
methods: {
async init () {
const questions = await Promise.all(this.area.questions.map(async q => {
const res = await Util.HTTP("GET", `/api/v1/photos/${encodeURIComponent(q.id)}/qimage`)
return {
...q,
imgdata: res.data
}
}))
this.$emit("loaded", questions)
}
}
}
And in the parent
<image-loader :area="area" #loaded="updateAreaQuestions"/>
export default {
data: () => ({
area: {
questions: [/* questions go here */]
}
}),
methods: {
updateAreaQuestions(questions) {
this.area.questions = questions
}
}
}
Here that variable has a value of this but it's bound under the scope of function. So, you can create reactive property in data as below :
data: function() {
return {
qIndex: 0,
questions: []
};
}
Props can't be reactive so use :
that.$set(this.questions, thisIndex, thisQuestion);
And assign your API output to directly questions using this.questions.

useMutation not mutating the local state

I'm getting this error while trying to mutate the local state in apollo.
errInvariant Violation: Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql
Initial state
registration: {
__typename: 'Registration',
tempMerchantId: '',
authorizeProfile: {
__typename: 'AuthorizePersonProfile',
nid_front: '',
nid_back: '',
authorized_person_photo: ''
}
}
My mutation
export const setAuthorizePersonQuery = gql`
mutation setAuthorizePersonProfileInfo($authorizePerosnData: Object!){
setAuthorizePersonProfileInfo(authorizePersonData: $authorizePerosnData) #client
}
`;
My resolver
export const setAuthorizePersonProfileInfo = (
_, { authorizePersonData }, { cache }
) => {
try {
const prevData = cache.readQuery({ getAuthorizePersonProfileQuery });
cache.writeQuery({
getAuthorizePersonProfileQuery,
data: {
registration: {
__typename: 'Registration',
authorizeProfile: {
__typename: 'AuthorizePersonProfile',
...prevData.registration.authorizeProfile,
...authorizePersonData
}
}
}
});
} catch (e) {
console.log(`err${e}`);
}
return null;
};
I'm trying to mutate the local state on button press, the function is
const handlePressedNext = () => {
Promise.all([
setAuthorizePersonProfileInfo({
variables: { authorizePersonData: generateNidData() }
})
])
.then(() => {
navigation.navigate('Photograph');
});
};
generateNidData function is like bellow
const generateNidData = () => ({
nid_front: nidFrontImage,
nid_back: nidBackImage
});
I'm new to apollo client. I can not understand what I'm doing wrong. Can anyone help me figure out the problem?
getAuthorizePersonProfileQuery is not a valid option for readQuery. Presumably, you meant use query instead.

Why my vue js data member is not getting updated?

Data part
data () {
return {
containsAd: true
}
},
Method that manipulates the data member containsAd
updated () {
let _this = this
window.googletag.pubads().addEventListener('slotRenderEnded', function (event) {
if (event.slot.getSlotElementId() === 'div-gpt-ad-nativead1') {
_this.containsAd = !event.isEmpty // this is false
console.log('Ad Exists? ', _this.containsAd)
}
})
},
Just to check if the value has changed or not.
check () {
let _this = this
setTimeout(function () {
console.log('Current Value', _this.containsAd)
}, 5000)
}
Resulting Output
I think doing the event listener in the mounted hook will sort your issue.
data() {
return {
containsAd: true
};
},
mounted() {
window.googletag.pubads().addEventListener('slotRenderEnded', event => {
if (event.slot.getSlotElementId() === 'div-gpt-ad-nativead1') {
this.containsAd = ! event.isEmpty // this is false
console.log('Ad Exists?', this.containsAd);
}
});
}
Also using es6 shorthand function will avoid you having to set _this.

subscribed events in durandaljs are not unsubscribed

This is the flow of the application:
The shell.js loads the schoolyeardialog.js which contains the schoolyearbrowser.js which loads the schoolyearwizard.js via create or edit button.
When I repeat these steps multiple times: click create/edit button then I do multiple
requests in my SchoolyearWizard to this:
$.when(service.editSchoolyear(schoolyearId))
The reason is that the subscribed events are not correctly unsubscribed in my opinion.
I have tried different ways of unsubscribing in the SchoolyearDialog.js file.
Either the events did not fire - when I did the subscription.off(); after the app.on(...)
or It was unsubscribed at the wrong position.
Where should I unsubscribe correctly?
If you guys need a sample repo as visual studio solution I can provide this if it helps or maybe you see clearly and immediately the error?!
I have also thought about unsubscribing from the 2 events create/edit when the SchoolyearDialog module is "unloaded" because then both events could/would be unsubscribed not only the create OR edit subscription as it is now when I either click the add or edit button... how would I do that?
SHELL
define(['plugins/router', 'durandal/app', 'viewmodels/SchoolyearDialog', 'knockout'], function (router, app, schoolyearDialog, ko) {
self.schoolyearIsLoaded = ko.observable(false);
var saveTimeTableSubscription = app.on('savedTimeTable').then(function (options) {
// after coming the 2nd time here
if (!self.schoolyearIsLoaded()) {
router.map([{ route: 'lessonplanner', moduleId: 'viewmodels/lessonplanner', title: 'lesson planner', nav: true },
{ route: 'documentbrowser', moduleId: 'viewmodels/documentbrowser', title: 'document browser', nav: true }])
.buildNavigationModel();
self.schoolyearIsLoaded(true);
}
router.navigate("lessonplanner", true);
});
return {
router: router,
activate: function () {
router.map([{ route: '', moduleId: 'viewmodels/SchoolyearDialog', nav: true, title: 'Schoolyearbrowser' }
]).buildNavigationModel();
return router.activate('SchoolyearDialog');
}
};
});
SchoolyearDialog
define(['durandal/app', 'knockout', 'plugins/router', 'viewmodels/SchoolyearWizard'],
function (app, ko, router, wizard) {
var ctor = function () {
debugger;
var self = this;
self.createSubscribe = ko.observable();
self.editSubscribe = ko.observable();
self.activeScreen = ko.observable('viewmodels/SchoolyearBrowser'); // set the schoolyear browser as default module
var createWizardSubscription = app.on('createWizard').then(function () {
self.createSubscribe().off();
self.createSubscribe(null);
self.activeScreen(new wizard('create'));
}, self);
self.createSubscribe(createWizardSubscription);
var editWizardSubscription = app.on('editWizard').then(function (schoolyearId) {
self.editSubscribe().off();
self.editSubscribe(null);
self.activeScreen(new wizard('edit', schoolyearId));
}, self);
self.editSubscribe(editWizardSubscription);
}
return ctor;
});
SchoolyearBrowser
define(['durandal/app', 'plugins/dialog', 'knockout', 'services/dataservice', 'plugins/router'],
function (app, dialog, ko, dataservice, router) {
var SchoolyearBrowser = function () {
var self = this;
self.schoolyears = ko.observableArray();
$.when(dataservice.getSchoolyears())
.done(function (schoolyearModels) {
self.schoolyears(schoolyearModels);
});
self.create = function () {
app.trigger('createWizard');
}
self.edit = function () {
app.trigger('editWizard', 1);
}
};
return SchoolyearBrowser;
});
SchoolyearWizard
define(['durandal/activator', 'viewmodels/step1', 'viewmodels/step2', 'knockout', 'durandal/app', 'services/dataservice', 'viewmodels/CreateEditSchoolyearViewModel'],
function (activator, Step1, Step2, ko, app, service, CreateEditSchoolyearViewModel) {
var ctor = function (viewMode, schoolyearId) {
debugger;
// depending on the mode I could setup 2 different step modules for create and edit ? and the Wizard has one property called content
if (viewMode === 'edit') {
$.when(service.editSchoolyear(schoolyearId))
.done(function (response) {
debugger;
self.viewModel(new CreateEditSchoolyearViewModel(response));
}).fail(function (error) {
alert(error);
});
}
else if (viewMode === 'create') {
$.when(service.createSchoolyear())
.done(function (response) {
debugger;
self.viewModel(new CreateEditSchoolyearViewModel(response));
}).fail(function (error) {
alert(error);
});
}
var self = this;
var steps = [new Step1(viewMode), new Step2(viewMode)];
var step = ko.observable(0); // Start with first step
self.activeStep = activator.create();
var stepsLength = steps.length;
self.viewModel = ko.observable();
this.hasPrevious = ko.computed(function () {
return step() > 0;
});
self.caption = ko.observable();
this.activeStep(steps[step()]);
this.hasNext = ko.computed(function () {
if ((step() === stepsLength - 1) && self.activeStep().isValid()) {
// save
self.caption('save');
return true;
} else if ((step() < stepsLength - 1) && self.activeStep().isValid()) {
self.caption('next');
return true;
}
});
this.isLastStep = function () {
return step() === stepsLength - 1;
}
this.next = function () {
if (this.isLastStep()) {
var vm = this.activeStep(); //.viewModel;
$.when(service.saveCreateSchoolyear({ schoolyearId: 1 })).done(function () {
app.trigger('savedTimeTable', { isSuccess: true });
}).fail(function (e) {
alert(e);
});
}
else if (step() < stepsLength) {
step(step() + 1);
self.activeStep(steps[step()]);
}
}
this.previous = function () {
if (step() > 0) {
step(step() - 1);
self.activeStep(steps[step()]);
}
}
}
return ctor;
});
This helped me greatly:
activator.deactivate function allows the previous object to execute custom deactivation logic."
SchoolyearDialog.js
self.deactivate = function () {
self.createSubscribe().off();
self.editSubscribe().off();
}
When the schoolyearDialog is deactivated both events are unsubscribed independing wether button create/edit is clicked. This is for me a clean solution :)
I agree with your solution but I'd recommend not to use plain .off() without parameters as this will cause to deregister all the events in the application.
Rather pass the event name as a parameter to your off method:
self.createSubscribe().off('savedTimeTable');