I'm building a webapp in Nuxt.js and it's growing quite a bit.
I have a page which does two things: one when i'm creating a task and one when managing that task.
This page has a lot of methods, divided for when i create the task and when i manage the task.
How can i split these modules in two files and then import then only when I need them?
These methods need also to access the component's state and other function Nuxt imports such as axios.
async create() {
if (this.isSondaggioReady()) {
try {
await this.getImagesPath()
const o = { ...this.sondaggio }
delete o.id
o.questions = o.questions.map((question) => {
delete question.garbageCollector
if (question.type !== 'checkbox' && question.type !== 'radio') {
delete question.answers
delete question.hasAltro
} else {
question.answers = question.answers.map((answer) => {
delete answer._id
delete answer.file
delete answer.error
if (answer.type !== 'image') delete answer.caption
return answer
})
}
if (question.hasAltro) {
question.answers.push({
type: 'altro',
value: ''
})
}
return question
})
console.log('TO SEND', JSON.stringify(o, null, 2))
this.$store.commit('temp/showBottomLoader', {
show: true,
message: 'Crezione del sondaggio in corso'
})
const { data } = await this.$axios.post('/sondaggi/admin/create', o)
this.sondaggio.id = data
const s = {
_id: data,
author: this.$auth.user.email.slice(0, -13),
title: this.sondaggio.title
}
this.$store.commit('temp/pushHome', { key: 'sondaggi', attr: 'data', data: [...this.$store.state.temp.home.sondaggi.data, s] })
this.$store.dispatch('temp/showToast', 'Sondaggio creato correttamente')
this.$router.replace('/')
} catch (e) {
console.log(e)
this.$store.dispatch('temp/showToast', this.$getErrorMessage(e))
} finally {
this.$store.commit('temp/showBottomLoader', {
show: false,
message: 'Crezione del sondaggio in corso'
})
}
}
},
Here there's an example of what a method does. It calls an async functions which relies on HTTP axios requests:
async getImagesPath() {
this.sondaggio.questions.forEach((question, i) => {
question.answers.forEach((answer, j) => {
if (answer.file instanceof File || answer.value.includes('data:image')) {
this.uploadingImages.push({
coords: [i, j],
percentage: 0,
file: answer.file || answer.value
})
}
})
})
const requests = []
this.uploadingImages.forEach((img) => {
const temp = new FormData()
temp.append('img', img.file)
const req = this.$axios.post('/sondaggi/admin/images/add/' + this.sondaggio.title.replace(/\s+/g, ''), temp, {
onUploadProgress: function (progressEvent) {
img.percentage = Math.round(((progressEvent.loaded * 100) / progressEvent.total) * 90 / 100)
},
onDownloadProgress: function (progressEvent) {
img.percentage = 90 + Math.round(((progressEvent.loaded * 100) / progressEvent.total) * 10 / 100)
},
headers: { 'Content-Type': 'multipart/form-data' }
})
.catch((err) => {
console.log(err)
img.percentage = 150
})
requests.push(req)
})
try {
const response = await Promise.all(requests)
response.forEach(({ data }, i) => {
this.sondaggio.questions[this.uploadingImages[i].coords[0]].answers[this.uploadingImages[i].coords[1]].value = data[0]
})
this.$set(this.sondaggio, 'hasImages', this.uploadingImages.length > 0)
this.uploadingImages = []
await Promise.resolve()
} catch (e) {
console.log('handling gloval err', e)
await Promise.reject(e)
}
},
As you can see axios requests modify the component's state
Related
I just want to update state outside of component after server give me failed response.
the project is react native .
After a lot of online search on website like this , I learn that i need to import my store and use dispatch method. but when i import storage and dispatch it, it give me this error: Property 'crypto' doesn't exist .
this is my feachService.js code:
import { URL } from 'react-native-url-polyfill';
import { store } from '../store/index'
import { errorSliceActions } from '../store/slices/global/errorSlice'
export default (url, isGet) => {
let headers = {
Referer: new URL(url).origin,
}
headers['X-Requested-With'] = 'XMLHttpRequest'
return fetch(
url,
{
method: (isGet ? 'GET' : 'POST'),
headers: headers
})
.then(function (res) {
return res.json();
})
.then(function (res)
{
if(res && res.isSuccess == false && res.message) {
store.dispatch(errorSliceActions.add(res.message));
}
return {json: function() { return res; }};
})
.catch(function (error) { console.log(error.message); })
};
this is my storage js file:
import { configureStore } from '#reduxjs/toolkit';
import errorSliceReducer from './slices/global/errorSlice';
export const store = configureStore({
reducer: {
errorSlice: errorSliceReducer
}
});
this is my error slice.js file:
import { createSlice } from "#reduxjs/toolkit"
function uuidv4() {
return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
const errorSlice = createSlice({
name: "errorSlice",
initialState: {
errors: [
]
},
reducers:
{
add: (state, action) => {
const newItem = {
id: uuidv4(),
message: action.payload
};
return [...state, newItem];
},
remove: (state, action) => {
return { errors: state.errors.filter(function(item) { return item.id != action.payload; }) }
}
}
})
export const errorSliceActions = errorSlice.actions;
export default errorSlice.reducer
and this is my code for executing fetch request
async function loadInfo (inputSearch, inputPage) {
if(config.dataurl) {
console.log(inputSearch);
setIsLoading(true);
const hasQuestionMark = config.dataurl.indexOf('?') > 0;
let targetUrl = '/test/test' + config.dataurl + (!hasQuestionMark ? '?' : '&');
targetUrl += 'page=' + inputPage;
targetUrl += '&search=' + inputSearch;
console.log(targetUrl);
const response = await feachService(appJSon.baseUrl + targetUrl , true);
const responseData = await response.json();
setIsLoading(false);
if(responseData.pagination)
setPagination(responseData.pagination);
if(!responseData.results)
setItems([]);
else
setItems(responseData.results);
}
}
useEffect(() => {
loadInfo('', 1).catch(function (error) { console.log(error.message); });
}, [])
what am i doing wrong ?
I am testing if a function works,
The function doesn't return anything but infact changes the data in the component
This is the test I am trying to write
describe('ModalAddCollectionCSV', () => {
it('Test load csv function', () => {
const localVue = createLocalVue()
const wrapper = shallowMount(AddCSV, {
localVue,
propsData: {
visible: true,
},
})
var fileDict = [{ file: new Blob([wrongDateFormat]) }]
wrapper.vm.loadcsv(fileDict)
expect(wrapper.html()).toContain('error')
})
})
I am expecting some changes to the data of the component
data() {
return {
collections: [],
inlineVisibility: false,
inlineTitle: '',
inlineSubtitle: '',
inlineKind: 'info',
loading: false,
}
},
This is the function I am testing in unit test, as you see it doesn't return anything just changes the data at the end of it
async loadcsv(element) {
const reader = new FileReader()
const file = element[0].file
this.toggleLoading()
reader.onload = async (e) => {
try {
//Normalising headers
const results = e.target.result
let resultSplit = results.split('\n')
const header = resultSplit[0]
.toLowerCase()
.replace(/[^a-zA-Z,]/g, '')
.trim()
resultSplit[0] = header
let table = resultSplit.join('\n')
const rows = await d3.csvParse(table)
await Promise.all(
rows.map(async (row) => {
if (!(this.getSerial(row).length == 9)) {
if (!this.getName(row)) {
throw `Please enter Person of interest`
}
if (!row.country) {
throw `Country for ${this.getName(
row,
)} is empty`
}
}
)
this.inlineVisibility = false
this.$emit('rowEmit', {
collections: this.row,
})
this.toggleLoading()
this.collections = []
this.$refs.modal.hide()
} catch (err) {
this.collections = []
this.inlineNotification('error', 'Unable to process CSV', `${err}.`)
}
}
reader.readAsText(file)
},
Is there a way to check emit action or error action?
I tried wrapper.text()
Wrapper.html()
but no difference
I'm trying to make an update and I managed to do it in the firebase, but in the store is not updating. Here is my code
editCar() {
let result = this.balmUI.validate(this.formData);
let { valid, message } = result;
this.message = message;
console.log(`Vrei sa editezi masina: ${this.formData.vehicle}`);
console.log(utils.url);
if (valid) {
let data = {
vehicle: this.formData.vehicle,
color: this.formData.color,
fuel: this.formData.fuel,
status: this.formData.status,
price: this.formData.price,
};
let requestParameters = { ...utils.globalRequestParameters };
let token = window.localStorage.getItem("token");
requestParameters.headers.Authorization = "Bearer " + token;
requestParameters.method = "PUT";
requestParameters.body = JSON.stringify(data);
fetch(utils.url + "cars/" + this.formData.id, requestParameters)
.then((res) => res.json())
.then((res) => {
console.log(res.message);
if (
res.message === "Decoding error!" ||
res.message === "Your token expired!"
) {
console.log("nu ai voie!");
} else {
data.id = res.id;
this.$store.dispatch("editCar", data);
this.$router.push("/");
}
});
}
This is the index from store, which contais my mutation and action. Anything else is working properly
import { createStore } from 'vuex'
export default createStore({
state: {
cars: [],
isAuthentif: false
},
getters: {
cars: state => {
return state.cars
}
},
mutations: {
SET_AUTH: (state, status) => {
state.isAuthentif = status
},
SET_CARS: (state, cars) => {
state.cars = cars
},
ADD_CAR: (state, car) => {
state.cars.push(car)
},
DELETE_CAR: (state, id) => {
var index = state.cars.findIndex(car => car.id == id)
state.cars.splice(index, 1);
},
EDIT_CAR: (state, car) => {
state.cars.forEach(c => {
if(c.id === car.id) {
c = car;
}
})
}
},
actions: {
login: ({ commit }, payload) => {
commit('SET_AUTH', payload)
},
fetchCars: ({ commit }, payload) => {
commit('SET_CARS', payload)
},
addCar: ({ commit }, payload) => {
commit('ADD_CAR', payload)
},
deleteCar: ({ commit }, payload) => {
commit('DELETE_CAR', payload)
},
editCar: ({ commit }, payload) => {
commit('EDIT_CAR', payload)
}
},
modules: {
}
})
EDIT_CAR is the problem, I think.
What is wrong? Why it is not updating on the screen.
I've also tried to use this https://vuex.vuejs.org/guide/mutations.html#object-style-commit
like this c = {...c, car} but is not working
Your problem is not in the mutation. The problem is in your editCar() in here this.$store.dispatch("editCar", data);
You put data, and with vehicle, color, fuel, status and price and then in your mutation you verify ID. You didn't pass any id. You cand make a new object if you don't want you id, like this:
editCar() {
let result = this.balmUI.validate(this.formData);
let { valid, message } = result;
this.message = message;
console.log(`Vrei sa editezi masina: ${this.formData.vehicle}`);
console.log(utils.url);
if (valid) {
let data = {
vehicle: this.formData.vehicle,
color: this.formData.color,
fuel: this.formData.fuel,
status: this.formData.status,
price: this.formData.price,
};
let requestParameters = { ...utils.globalRequestParameters };
let token = window.localStorage.getItem("token");
requestParameters.headers.Authorization = "Bearer " + token;
requestParameters.method = "PUT";
requestParameters.body = JSON.stringify(data);
fetch(utils.url + "cars/" + this.formData.id, requestParameters)
.then((res) => res.json())
.then((res) => {
console.log(res.message);
if (
res.message === "Decoding error!" ||
res.message === "Your token expired!"
) {
console.log("nu ai voie!");
} else {
let newData = {
id: this.formData.id,
vehicle: data.vehicle,
color: data.color,
fuel: data.fuel,
status: data.status,
price: data.price,
};
this.$store.dispatch("editCar", newData);
this.$router.push("/");
}
});
}
},
Also in your mutation cand make something like this:
EDIT_CAR: (state, car) => {
Object.assign(state.cars[state.cars.findIndex(c => c.id === car.id)], car);
}
Can you try to change your EDIT_CAR mutation to:
const index = state.cars.findIndex(x => x.id === car.id)
state.cars.splice(index, 1, car)
If you haven't done already, place a console.log(car) at the beginning of the mutation so you make sure it gets called and the car payload is what you expect.
When I update using raw JSON, it's working but when I use the form data it is not updating. the request body when using form data is an empty object. Why is this happening?
Here's my update code:
exports.updateProgram = catchAsync(async (req, res, next) => {
console.log('req ko body',req.body)
let doc = await Program.findByIdAndUpdate(req.params.id, req.body, { runValidators: true, new: true })
if (!doc) {
return next(new AppError('No document found with that ID', 404))
}
res.status(200).json({
status: 'success!',
data: { doc }
})
})
In Postman:
I am using multer, I actually pass the photo in req.body. Here's the code:
let multerStorage = multer.memoryStorage()
let multerFilter = (req, file, cb) => {
if (file.mimetype.split('/')[0] == 'image') {
cb(null, true)
} else {
cb(new AppError('Not an image!', 400), false)
}
}
let upload = multer({
storage: multerStorage,
fileFilter: multerFilter
})
exports.uploadPhotos = upload.fields([
{ name: 'abcd', maxCount: 10 },
{ name: 'photos', maxCount: 10 },
{name: 'photos3', maxCount: 10}
])
exports.resizePhotos = catchAsync(async (req, res, next) => {
// if (!req.files.photos || !req.files.abcd) return next()
if(req.files.abcd) {
req.body.abcd = []
await Promise.all(req.files.abcd.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.abcd.push(filename)
})
)} else if(req.files.photos3) {
req.body.photos3 = []
await Promise.all(req.files.photos3.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/arpit/${filename}`)
req.body.photos3.push(filename)
})
)}
else if(req.files.photos) {
// console.log('codee here')
// } else if(req.body.photos) {
req.body.photos = []
console.log('req.files>>>', req.files)
await Promise.all(req.files.photos.map(async (file, i) => {
let filename = `tour-${Date.now()}-${i + 1}.jpeg`
await sharp(file.buffer)
.resize(500,500)
.toFormat('jpeg')
.jpeg({ quality: 90 })
.toFile(`public/img/programs/${filename}`)
req.body.photos.push(filename)
})
)
}
return next()
})
I'm importing in the routes file
Express (bodyParser) can't handle multipart form-data and that's why your code isn't working.
Take a look at multer, an express package. It is a middleware which provides the functionality you're looking for.
var cpUpload = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }]);
app.post('/cool-profile', cpUpload, function (req, res, next) {
// req.files is an object (String -> Array) where fieldname is the key, and the value is array of files
//
// e.g.
// req.files['avatar'][0] -> File
// req.files['gallery'] -> Array
//
// req.body will contain the text fields, if there were any
})
This might be help you. Quoted from https://www.npmjs.com/package/multer#readme
I am developing a mobile app, which uses React-Native, Redux and React-Native-Contacts.
In an action creator, I am trying to call a function (from React-Native-Contacts), which has callbacks (rather than promises). Following is my code.
I want the action "acContactImport" to wait for the helper function "fContactImport" to finish before proceeding to the "dispatch" statement. It does not wait for it. How can I make it wait for it?
// Action Creator:
import Contacts from 'react-native-contacts';
import { PermissionsAndroid } from 'react-native';
export const acContactImport = (userID) => {
return async (dispatch) => {
let contactList = [];
try {
// The following statement should be executed asynchronously.
// However, it is not. How can I make it asynchronous?
contactList = await fContactImport();
dispatch({
type: CONTACT_IMPORT,
payload: { contactList: contactList },
});
}
catch (err) {
console.log("acContactImport - catch: ", err);
}
};
};
// Helper Function 1
export const fContactImport = async () => {
let contactList = [];
if (Platform.OS === "android") {
PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.READ_CONTACTS, {
title: "Contacts",
message: "This app would like to view your contacts."
})
.then(() => {
contactList = _loadContacts();
});
} else {
contactList = _loadContacts();
}
}
// Helper Function 2
export const _loadContacts = () => {
Contacts.getAll((err, data2) => {
if (err) {
return [];
}
else {
let candidates = [];
for (let i = 0; i < data2.length; i++) {
let candidateObject = { name: candidateRecord.givenName + " " + candidateRecord.middleName + " " + candidateRecord.familyName };
candidates.push(candidateObject);
}
return contactList;
}
});
}
Apparently, the problem is NOT in the action creator, but in the helper functions, which do not support the new style of promises (async/wait). So, here is the solution...
// Action Creator:
// No changes are required. Original code is good.
// Helper Function 1
export const fContactImport = async () => {
let contactList = [];
if (Platform.OS === "android") {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.READ_CONTACTS,
{
title: "Contacts",
message: "This app would like to view your contacts."
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
contactList = await _loadContacts();
} else {
console.log('Contacts access denied');
}
} catch (err) {
console.warn(err);
}
} else {
contactList = await _loadContacts();
}
return contactList;
}
// Helper Function 2
export const _loadContacts = () => {
return new Promise((resolve, reject) => {
Contacts.getAll((err, data2) => {
if (err) {
reject([]);
}
else {
let candidates = [];
for (let i = 0; i < data2.length; i++) {
let candidateObject = { name: candidateRecord.givenName + " " + candidateRecord.middleName + " " + candidateRecord.familyName };
candidates.push(candidateObject);
}
resolve(candidates);
}
});
});
}