How to access local component variable from a callback in vue? - vue.js

I am trying to set my components variable using an api rest command. I wanted to handle all responses through a function in its own file called handleResponse() which is below.
// api/tools/index.js
function handleResponse (promise, cb, cbError) {
var cbErrorRun = (cbError && typeof cb === "function")
promise.then(function (response) {
if (!response.error) {
cb(response)
}
else if (cbErrorRun) {
cbError(response)
}
}).catch(function (error) {
console.log(error)
if (cbErrorRun) {
var responseError = {
"status": 404,
"error": true,
"message": error.toString()
}
cbError(responseError)
}
})
}
export {handleResponse}
In my component file I have this
.... More above....
<script>
import { fetchStock } from '#/api/stock'
export default {
data () {
return {
stock: {},
tabs: [
{
title: 'Info',
id: 'info'
},
{
title: 'Listings',
id: 'listings'
},
{
title: 'Company',
id: 'company'
}
],
}
},
validate ({params}) {
return /^\d+$/.test(params.id)
},
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
fetchStock(
params,
function(response) { //on successful data retrieval
this.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
}
</script>
The current code gives me this error: "Uncaught (in promise) TypeError: Cannot set property 'stock' of undefinedAc". I think this happens because I no longer have access to 'this' within the callback I pass in the fetchStock function. How would I fix this without changing the current handleResponse layout.

You can try this trick
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
var self = this;
fetchStock(
params,
function(response) { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}

You can either use an arrow function for you callback since arrow functions maintain and use the this of their containing scope:
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
fetchStock(
params,
(response) => { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
(responseError) => { //on error
console.log(responseError)
}
)
}
Or you can assign const vm = this n the beginning of your method before the callbacks like so.
vm stands for "View Model"
created: function() {
var params = {'id': this.$route.params.stockId}
//this.$route.params.stockId}
const vm = this;
fetchStock(
params,
function(response) { //on successful data retrieval
self.stock = response.data.payload // payload = {'name': test123}
console.log(response)
},
function(responseError) { //on error
console.log(responseError)
}
)
}
I advise using the const as opposed to var in the vm declaration to make it obvious the value of vm is a constant.

Related

How to encode and parse / decode a nested query string Javascript

I'm sending form data from React Hook Form to Netlify via their submission-created function. I don't have any problem with encoding individual form field values, but now I'm trying to encode an array of objects.
Here is an example of my form data:
{
_id: "12345-67890-asdf-qwer",
language: "Spanish",
formId: "add-registration-form",
got-ya: "",
classType: "Private lessons",
size: "1",
days: [
{
day: "Monday",
start: 08:00",
end: "09:30"
},
{
day: "Wednesday",
start: "08:00",
end: "09:30"
}
]
}
The only problem I have is with the "days" array. I've tried various ways to encode this and this is the function I've currently been working with (which isn't ideal):
const encode = (data) => {
return Object.keys(data).map(key => {
let val = data[key]
if (val !== null && typeof val === 'object') val = encode(val)
return `${key}=${encodeURIComponent(`${val}`.replace(/\s/g, '_'))}`
}).join('&')
}
I tried using a library like qs to stringify the data, but I can't figure out how to make that work.
And here is the function posting the data to Netlify:
// Handles the post process to Netlify so I can access their serverless functions
const handlePost = (formData, event) => {
event.preventDefault()
fetch(`/`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: encode({ "form-name": 'add-registration-form', ...formData }),
})
.then((response) => {
if(response.status === 200) {
navigate("../../")
} else {
alert("ERROR!")
}
console.log(response)
})
.catch((error) => {
setFormStatus("error")
console.log(error)
})
}
Finally, here is a sample of my submission-created file to receive and parse the encoded data:
const sanityClient = require("#sanity/client")
const client = sanityClient({
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
dataset: process.env.GATSBY_SANITY_DATASET,
token: process.env.SANITY_FORM_SUBMIT_TOKEN,
useCDN: false,
})
const { nanoid } = require('nanoid');
exports.handler = async function (event, context, callback) {
// Pulling out the payload from the body
const { payload } = JSON.parse(event.body)
// Checking which form has been submitted
const isAddRegistrationForm = payload.data.formId === "add-registration-form"
// Build the document JSON and submit it to SANITY
if (isAddRegistrationForm) {
// How do I decode the "days" data from payload?
let schedule = payload.data.days.map(d => (
{
_key: nanoid(),
_type: "classDayTime",
day: d.day,
time: {
_type: "timeRange",
start: d.start,
end: d.end
}
}
))
const addRegistrationForm = {
_type: "addRegistrationForm",
_studentId: payload.data._id,
classType: payload.data.classType,
schedule: schedule,
language: payload.data.language,
classSize: payload.data.size,
}
const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
}
callback(null, {
statusCode: 200,
})
}
So, how do I properly encode my form data with a nested array of objects before sending it to Netlify? And then in the Netlify function how do I parse / decode that data to be able to submit it to Sanity?
So, the qs library proved to be my savior after all. I just wasn't implementing it correctly before. So, with the same form data structure, just make sure to import qs to your form component file:
import qs from 'qs'
and then make your encode function nice and succinct with:
// Transforms the form data from the React Hook Form output to a format Netlify can read
const encode = (data) => {
return qs.stringify(data)
}
Next, use this encode function in your handle submit function for the form:
// Handles the post process to Netlify so we can access their serverless functions
const handlePost = (formData, event) => {
event.preventDefault()
fetch(`/`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: encode({ "form-name": 'add-registration-form', ...formData }),
})
.then((response) => {
reset()
if(response.status === 200) {
alert("SUCCESS!")
} else {
alert("ERROR!")
}
console.log(response)
})
.catch((error) => {
console.log(error)
})
}
Finally, this is what your Netlify submission-created.js file should look like more or less:
const sanityClient = require("#sanity/client")
const client = sanityClient({
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
dataset: process.env.GATSBY_SANITY_DATASET,
token: process.env.SANITY_FORM_SUBMIT_TOKEN,
useCDN: false,
})
const qs = require('qs')
const { nanoid } = require('nanoid');
exports.handler = async function (event, context, callback) {
// Pulling out the payload from the body
const { payload } = JSON.parse(event.body)
// Checking which form has been submitted
const isAddRegistrationForm = payload.data.formId === "add-registration-form"
// Build the document JSON and submit it to SANITY
if (isAddRegistrationForm) {
const parsedData = qs.parse(payload.data)
let schedule = parsedData.days
.map(d => (
{
_key: nanoid(),
_type: "classDayTime",
day: d.day,
time: {
_type: "timeRange",
start: d.start,
end: d.end
}
}
))
const addRegistrationForm = {
_type: "addRegistrationForm",
submitDate: new Date().toISOString(),
_studentId: parsedData._id,
classType: parsedData.classType,
schedule: schedule,
language: parsedData.language,
classSize: parsedData.size,
}
const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
}
callback(null, {
statusCode: 200,
})
}

Why am I getting data undefined when I can see the data?

I have been staring at these action creators:
import * as types from './constants';
import * as endpoints from 'endpoints';
import * as requester from 'services/Requester';
import * as helpers from 'account-settings/helpers/data-helpers';
export function fetchPrefences({Key}) {
return dispatch => {
const url = `${endpoints.v2.INDIVIDUALS}/${Key}/preferences`;
requester.sendGet(url).then(data => {
const payload = helpers.sortPreferences(data);
dispatch({
type: types.SET_USER_PREFERENCES,
payload,
});
});
};
}
export function fetchTopics() {
return dispatch => {
requester.sendGet(endpoints.TOPICS_OF_CONCERN).then(data => {
dispatch({
type: types.SET_USER_TOPICS,
payload: data.Items,
});
});
};
}
export function handleStateChange(payload) {
return {
type: types.SET_NEW_PREFERENCES,
payload,
};
}
export function handleUpdateTopics({topics, involved}, updateBoth = false) {
return dispatch => {
return requester
.sendPut(endpoints.TOPICS_OF_CONCERN, {
Items: topics,
})
.then(data => {
dispatch({
type: types.SET_USER_TOPICS,
payload: data.Items,
});
if (updateBoth) {
dispatch(handleUpdatePreferences({involved}));
}
});
};
}
export function handleUpdateGetInvoved({involved}) {
return (dispatch, getState) => {
const {auth} = getState();
//prettier-ignore
const url = `${endpoints.v2.INDIVIDUALS}/${auth.user.Key}/preferences`;
return requester
.sendPut(url, {
Items: involved,
})
.then(data => {
const payload = helpers.sortPreferences(data);
dispatch({
type: types.SET_USER_PREFERENCES,
payload,
});
});
};
}
And it's clear I am getting data as undefined with this message:
What is not clear is why? When I do a curl, the data is there:
{"items":[{"category":"None","key":"2883040c-88b8-4899-bd47-114a560d085b","displayText":"Energy
Costs","isSelected":false,"order":1},{"category":"None","key":"a745a3d6-0f64-4595-8734-6082d9c914f7","displayText":"Regulations","isSelected":false,"order":7},{"category":"None","key":"51797a61-8016-4817-a46e-72dee3d8239a","displayText":"Minimum
Wage","isSelected":false,"order":5},{"category":"None","key":"381e24d0-2668-4a69-a993-7d5e1ecaec3b","displayText":"Taxes","isSelected":false,"order":8},{"category":"None","key":"dfaf22cb-111a-46f3-bce3-93fbf4a91490","displayText":"Unemployment
Insurance","isSelected":false,"order":9},{"category":"None","key":"c55b5d2a-a0f3-4c35-bf59-b433259b2059","displayText":"Workers
Compensation","isSelected":false,"order":10},{"category":"None","key":"d4b787d4-550b-4866-a5cc-c6a2de61a91a","displayText":"Healthcare","isSelected":false,"order":4},{"category":"None","key":"c2557854-421d-4b2f-810f-caadf938cded","displayText":"Government
Spending","isSelected":false,"order":3},{"category":"None","key":"cf91f638-c5fa-4252-be01-dce504ae369d","displayText":"Private
Property
Rights","isSelected":false,"order":6},{"category":"None","key":"0eae5ccf-2ba5-41bd-9111-efe7acafa512","displayText":"Finding
Qualified Employees","isSelected":false,"order":2}]}%
In Swagger, I check, the data is there:
{
"items": [
{
"category": "None",
"key": "2883040c-88b8-4899-bd47-114a560d085b",
"displayText": "Energy Costs",
"isSelected": false,
"order": 1
},
{
"category": "None",
"key": "a745a3d6-0f64-4595-8734-6082d9c914f7",
"displayText": "Regulations",
"isSelected": false,
"order": 7
},
{
"category": "None",
"key": "51797a61-8016-4817-a46e-72dee3d8239a",
"displayText": "Minimum Wage",
"isSelected": false,
"order": 5
},
{
"category": "None",
"key": "381e24d0-2668-4a69-a993-7d5e1ecaec3b",
"displayText": "Taxes",
"isSelected": false,
"order": 8
},
{
"category": "None",
"key": "dfaf22cb-111a-46f3-bce3-93fbf4a91490",
"displayText": "Unemployment Insurance",
"isSelected": false,
"order": 9
},
{
"category": "None",
"key": "c55b5d2a-a0f3-4c35-bf59-b433259b2059",
"displayText": "Workers Compensation",
"isSelected": false,
"order": 10
},
{
"category": "None",
"key": "d4b787d4-550b-4866-a5cc-c6a2de61a91a",
"displayText": "Healthcare",
"isSelected": false,
"order": 4
},
{
"category": "None",
"key": "c2557854-421d-4b2f-810f-caadf938cded",
"displayText": "Government Spending",
"isSelected": false,
"order": 3
},
{
"category": "None",
"key": "cf91f638-c5fa-4252-be01-dce504ae369d",
"displayText": "Private Property Rights",
"isSelected": false,
"order": 6
},
{
"category": "None",
"key": "0eae5ccf-2ba5-41bd-9111-efe7acafa512",
"displayText": "Finding Qualified Employees",
"isSelected": false,
"order": 2
}
]
}
I noticed that in the code the items property was written as Items, I tried to change it to items to match the data property, that did nothing.
A colleague suggested the issue could be in the requester object, I do have a question about it too:
import axios from 'axios';
import LocalStorage from './LocalStorage';
import env from 'env';
import * as appcenter from 'utils/appcenterLogger';
import * as titlesHelper from 'utils/titleCaser';
let expired = false;
export const instance = axios.create({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
NFIBAppID: env.NFIBAppID,
},
});
let preHeaders = {};
async function mergeConfig(config) {
try {
const access = await LocalStorage.get('access');
preHeaders = access;
return {...config, headers: {...access}};
} catch (error) {
return {...config};
}
}
export async function sendGet(url, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.get(url, await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendPost(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.post(url, titlesHelper.lowerCaser(data), await mergeConfig(config))
.then(response => {
console.log(response);
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendPut(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.put(url, titlesHelper.lowerCaser(data), await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendPatch(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.patch(url, data, await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export async function sendDelete(url, data, config = {}) {
if (expired) {
return;
}
const now = new Date();
return instance
.delete(url, await mergeConfig(config))
.then(response => {
return saveHeaders(response, now, url);
})
.catch(error => {
return catchErros(error, now, url);
});
}
export function saveHeaders({data, headers}, timeSent, url) {
try {
if (headers && headers.authorizationtoken) {
LocalStorage.save('access', {
AuthorizationToken: headers.authorizationtoken,
});
}
const timeReceived = new Date();
LocalStorage.save('lastTimeRequestSent', timeReceived);
appcenter.trackRequestTiming(timeSent, timeReceived, headers, url, false);
return titlesHelper.toTitleCase(data);
} catch (_e) {
return false;
}
}
function catchErros({error, timeSent}, url) {
try {
const timeReceived = new Date();
LocalStorage.save('lastTimeRequestSent', timeReceived);
appcenter.trackRequestTiming(timeSent, timeReceived, error, url, true);
if (error && error.response) {
saveHeaders({
headers: preHeaders,
});
const {data} = error.response;
const message = data.message || data.Message;
if (message.includes('TokenExpired')) {
expired = true;
}
}
return Promise.reject(titlesHelper.toTitleCase(error.response.data));
} catch (_e) {
return error;
}
}
export function resetTokenExpired() {
expired = false;
}
I am seeing Promise syntax being mixed with async/await syntax, could this be causing an issue?
I tried to see if perhaps the issue was with the authorization token, so I console logged it:
let preHeaders = {};
async function mergeConfig(config) {
try {
const access = await LocalStorage.get('access');
console.log(access);
preHeaders = access;
return {...config, headers: {...access}};
} catch (error) {
return {...config};
}
}
but I am successfully getting that back:
{AuthorizationToken: "<bunch-o-numbers>"}
What I know at this point is that without the logic inside of the saveHeaders() function, a registered users' password will return undefined.
To complicate things, this application uses action helpers, which I have never implemented, but I see that Items property in there all over the place, keep in mind that the original warning read Items, but I changed it everywhere to items to make it match the JSON item property in the hope that would be the fix.
However, I have now come across these action helper files with the following code, action-helpers.js:
import * as endpoints from 'endpoints';
import * as requester from 'services/Requester';
import compareDesc from 'date-fns/compare_desc';
export async function fetchTransaction() {
try {
const response = await requester.sendGet(endpoints.TRANSACTIONS);
const {Items = []} = response;
return Items.sort((a, b) => compareDesc(a.DateTime, b.DateTime));
} catch (error) {
return [];
}
}
and in data-helpers.js:
export function sortPreferences(data) {
const sorted = data.Items.sort((a, b) => a.Order - b.Order);
const communications = sorted.filter(
p => p.Category === 'CommunicationPreferences'
);
const privacy = sorted.filter(p => p.Category === 'MemberPrivacy');
const involved = sorted.filter(p => p.Category === 'GetInvolved');
const format = data.EmailFormatType === 'HTML' ? 'HTML' : 'Plain Text';
return {
communications,
privacy,
involved,
emailFormatType: format,
isEmailAllowed: data.IsEmailAllowed,
isPhoneAllowed: data.IsPhoneAllowed,
};
}
Most probably you are not getting the expected response from your requester function.
Try logging the response from the requester and see the output. You might have to use response.json() to resolve the promise correctly. This is assuming your requester class/function works like that.

Back4app Parse-sever : Find just get just the id not all data

Im on a find situacion because the result of the Parse.query is just the id and I had other columns :(
Response of find:
[{"_objCount":0,"className":"DivorceCase","id":"8Ab15ASBX3"}]
Actual Code:
// Your corresponding keys
Parse.initialize("xxx", "xxx");
// For back4app applications
Parse.serverURL = 'https://parseapi.back4app.com'
var app = new Vue({
el: "#AttorneyCases",
data: {
DivorceCase :{},
},
mounted:function(){
new Parse.Query("DivorceCase").descending("createdAt").find()
.then((DivorceCaseResponse) => {
this.DivorceCase = DivorceCaseResponse;
})
}
})
to get the value of the other columns field, you need to use the code below:
var columnName = DivorceCaseResponse.get("columnName");
You can check more about retrieving objects here.
Thanks, nataliec!
The final code is:
mounted: function() {
var Case = Parse.Object.extend("DivorceCase");
var query = new Parse.Query(Case);
query.find()
.then((Ids) => {
Ids.forEach(element => {
var Case = Parse.Object.extend("DivorceCase");
var query = new Parse.Query(Case);
query.get(element.id)
.then((DivorceCaseResponse) => {
// The object was retrieved successfully.
this.DivorceCases.push(DivorceCaseResponse.get("BasicInfo"));
},
(e) => {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
swal({
position: 'center',
type: 'error',
title: e.message,
customClass: 'swal-wide'
})
});
});
},
(e) => {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and message.
swal({
position: 'center',
type: 'error',
title: e.message,
customClass: 'swal-wide'
})
});
}

Vuex - Normalizr doesn't work as expected

I am creating a simple chat app. I have three entities: rooms, messages and users.
I have a fake API that returns a response like this:
[{
id: 1,
name: 'room1',
avatar: 'some img url',
messages: [
{
id: 1,
text: 'some text',
user: {
id: 1,
username: 'Peter Peterson',
avatar: 'some img url'
}
]
}]
And my action looks like this:
getAllRooms({ commit }) {
commit(GET_ALL_ROOMS_REQUEST);
return FakeApi.getAllRooms()
.then(
rooms => {
const { entities } = normalize(rooms, room);
console.log(entities);
commit(GET_ALL_ROOMS_SUCCESS, {
rooms: entities.rooms, byId: rooms.map(room => room.id)
});
commit(GET_ALL_MESSAGES_SUCCESS, { messages: entities.messages });
commit(GET_ALL_USERS_SUCCESS, { users: entities.users });
},
err => commit(GET_ALL_ROOMS_ERROR)
)
}
And my mutations look like this:
[GET_ALL_ROOMS_REQUEST](state) {
state.loading = true;
},
[GET_ALL_ROOMS_SUCCESS](state, payload) {
state.rooms = payload.rooms;
state.byId = payload.byId;
state.loading = false;
},
[GET_ALL_ROOMS_ERROR]() {
state.error = true;
state.loading = false;
}
And my component calls the action like this:
{
mounted() {
this.getAllRooms();
}
}
These are my schema definitions:
const user = new schema.Entity('users');
const message = new schema.Entity('messages', {
user: user
});
const room = new schema.Entity('rooms', {
messages: [message]
})
when i check the response in then method after FakeApi.getAllRooms() every object is wrapped in some weird Observer, and I pass it like that to normalize and normalize returns some weird response.
What am I doing wrong?
The problem wasn't with vuejs, it was with the way I made the normalizr schemas. Because my response is an array at the root I should have had a new rooms array schema, like so:
const user = new schema.Entity('users');
const message = new schema.Entity('messages', {
user: user
});
const room = new schema.Entity('rooms', {
messages: [message]
});
const roomsSchema = [room];
And then use it like this: normalize(rooms, roomsSchema)

Vee-validate (VueJS) - evaluating a condition asynchronously

Can I make a custom validation rule that returns true/false based on a AJAX request? the problem is that the validate call has finished running when the AJAX call completes.
Do I need to have the rule set/unset a boolean variable based on which the field is valid/invalid?
const isValidNameRule = {
getMessage(field)
{
return "The name must be unique."
},
validate(validatingName)
{
var formData = new FormData();
formData.append("validatingName", validatingName);
this.$http.post("/api/isValid?name=" + validatingName, formData)
.then(function (response) {
// success
return true;
}, function (response) {
// error
return false;
});
}
};
Didn't know how to work with Promises.
Eventually got it working by extending one of the official samples:
const customRule = {
getMessage(field, params, data) {
return (data && data.message) || 'Something went wrong';
},
validate(aValue) {
return new Promise(resolve => {
var formData = new FormData();
formData.append("nameFilter", aValue);
$.ajax({
type: "POST",
url: url,
data: {
action: "validate",
value: aValue,
}
}).done(function (data) {
if (!ok)
{
resolve({
valid: false,
data: {message: "Condition not met"}
});
}
else
{
resolve({
valid: !! aValue,
data: undefined
});
}
});
});
}
};