Can't get data from api with axios in Nuxt components - axios

<template>
<div id="post">
<p>{{ data }}</p>
</div>
</template>
<script>
export default {
data () {
return {
data: ''
}
},
async asyncData({$axios}) {
const res = await $axios.get('/v1/posts')
.catch( error => {
console.log("response error", error)
return false
})
return {
data: res
}
},
}
</script>
At first, I tried to get the data with the above code, it worked in pages/post.vue but not in components/post.vue.
Then, I realized that I can't use asyncData in the nuxt components and changed the code as follows.
<template>
<div id="post">
<p>{{ data }}</p>
</div>
</template>
<script>
export default {
data () {
return {
data: ''
}
},
mounted () {
this.asyncData()
},
asyncData() {
await axios.get('/v1/posts')
.then(res => {
this.data = res.data
})
},
}
</script>
Then, I got a syntax error "Unexpected reserved word 'await'".
How can I get data via api in Nuxt components?
===================================
I read https://nuxtjs.org/docs/features/data-fetching#accessing-the-fetch-state and changed the code as below.
<script>
export default {
data () {
return {
data: ''
}
},
async fetch() {
this.data = await fetch('/v1/posts')
.then(res => res.json())
},
}
</script>
And now, I'm stacking with another error 'Error in fetch(): SyntaxError: Unexpected token < in JSON at position 0'.

This code worked.
<script>
export default {
data () {
return {
data: '',
}
},
async fetch() {
const res = await this.$axios.get('/v1/posts')
this.data = res.data
},
}
</script>

Glad that found a solution to your issue.
You can even use this.$axios.$get directly if you don't want to have to write .data afterwards.

Related

How to convert axios API call to vuex action dispatch?

I'm new with Vue3JS and I am developing a todo list built from several components like that :
I need to retrieve data to display all tasks, TaskInfo component is used to display informations.
I try to convert my local component TaskInfo works well with API call (method async loadTask()) into Vuex store action/mutation, but I didn't quite understand how to process in my case, because in component TaskInfo data are always the same.
I try to load data from current taskId with :
mounted() {
this.loadTask(this.taskId);
},
But, only last item loaded, with id 26, display data loaded by getTaskinfo computed variable loaded this.$store.state.taskInfo.
Any suggestion to load component data correctly when is mouted ?
TaskInfo.vue
<template>
<div class="task-container mb-4 ml-2 rounded-lg flex flex-grow flex-col flex-nowrap items-left shadow-test border border-solid border-gray-dark">
<div class="task--labels flex p-4 border-1 border-solid border-b border-gray-dark">
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2"> </span>
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2">label 2</span>
<div class="font-bold absolute right-1">
<button
v-if="this.taskId"
#click="this.$store.dispatch('removeTask', this.taskId)" >
<svg width="24px" height="24px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="removeIconTitle" stroke="#EF5350" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#000000">
<title id="removeIconTitle">Remove</title>
<path d="M17,12 L7,12"/>
<circle cx="12" cy="12" r="10"/>
</svg>
</button>
</div>
</div>
<div
class="task--title p-4 title text-m font-normal"
:class="[ getTaskinfo.finish === true ? 'finished' : '' ]"
>
{{ getTaskinfo.title }}
</div>
<div class="task-time p-4 text-xs text text-gray1 flex items-center">
<span class="pt-1"></span>
<span class="ml-1">11h - 12h</span>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { mapActions } from 'vuex'
axios.defaults.baseURL = 'http://localhost:3000';
export default {
name: 'taskInfo',
props: ['taskId'],
data() {
return {
error: '',
taskinfo: []
};
},
mounted() {
this.loadTask(this.taskId);
},
methods: {
...mapActions([
'loadTask' // map `this.incrementBy(this.taskId)` to `this.$store.dispatch('loadTask', this.taskId);`
]),
// Old API call replaced by mapActions, it's works fine.
async loadTask() {
try {
if(!this.taskId) return;
const taskInfo = await axios.get(`/task/${this.taskId}`);
if (!taskInfo.data) {
console.log('no taskInfo');
}
else {
this.taskinfo = taskInfo.data;
}
} catch (error) {
console.log('no taskInfo error');
}
},
},
computed: {
getTaskinfo() {
return this.$store.state.taskInfo;
},
}
}
</script>
Store
import { createStore } from 'vuex'
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
export default createStore( {
state: {
taskInfo: [],
error: '',
},
// Mutations have to be synchronous.
mutations: {
loadTask(state, taskInfo) {
state.taskInfo = taskInfo;
},
updateError(state, error) {
state.error = error;
},
},
actions: {
setError({ commit }, error) {
commit('updateError', error);
},
// LOAD task info.
async loadTask({ commit }, taskId) {
try {
if(!taskId) commit('updateError', 'taskId empty');
const taskInfo = await axios.get(`/task/${taskId}`);
if (!taskInfo.data) {
commit('updateError','Aucune info sur la tache');
// Set an empty task list.
commit('loadTask', []);
}
else {
console.log('task : ' + taskId);
commit('loadTask', taskInfo.data);
}
} catch (error) {
commit('updateError', error);
}
},
},
});

nextjs errors with form handling

I'm learning react and nextjs, the page works perfectly without the integration of a UI library, since i installed react suite my page with the login form doesn't work anymore, it returns me an error: TypeError: Cannot read property 'value 'of undefined
My code is:
import React, { useState } from "react"
import { setCookie, parseCookies } from "nookies"
import { useRouter } from "next/router"
import { Form, FormGroup, FormControl, ControlLabel, HelpBlock, Button, Input } from 'rsuite';
const Login = () => {
// set initial const
const router = useRouter()
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
// handle event submit
const handleSubmit = async (e) => {
e.preventDefault();
// fetch user
const response = await fetch( process.env.urlHeadless + '/api/cockpit/authUser', {
method: "post",
headers: {
"Content-Type": "application/json",
'Cockpit-Token': process.env.loginKey,
},
body: JSON.stringify({
user: username,
password: password
})
})
// if user exists
if (response.ok) {
const loggedUser = await response.json()
// set cookie with api_key
setCookie("", "tokenId", loggedUser._id, {
maxAge: 30 * 24 * 60 * 60,
path: "/"
})
// redirect to dashboard
return (router.push("/dashboard"))
} else if (response.status === 412) {
alert('compila il form')
} else if (response.status === 401) {
alert('credenziali di accesso errate')
}
}
return (
<>
<Form>
<FormGroup>
<ControlLabel>Username</ControlLabel>
<FormControl type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
<HelpBlock>Required</HelpBlock>
</FormGroup>
<FormGroup>
<ControlLabel>Password</ControlLabel>
<FormControl type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
</FormGroup>
<FormGroup>
<Button appearance="primary" onClick={handleSubmit}>Submit</Button>
</FormGroup>
</Form>
</>
)
}
// async function
export async function getServerSideProps(context) {
// get cookie value
const cookies = parseCookies(context).token;
// if cookie has value
if (cookies) {
return {
// redirect to dashboard
redirect: {
destination: '/dashboard',
permanent: false,
},
}
}
return { props: {} };
}
export default Login;
I also tried using useRef instead of useState, but it didn't work ... where am I wrong? thank you in advance
Looks like rsuite components might have a different onChange implementation, as described in their docs
onChange (formValue:Object, event:Object) => void Callback fired when data changing
If you want the event, try changing the onChange callback in your code to take the second parameter:
<FormControl
...
onChange={(value, e) => setUsername(e.target.value)}
/>

MERN stack binary image is just a black square

I'm building a full-stack app with a MERN stack (mongo db, Express, React and Node.js)
I'm currently able to cave my image to the mongo db as a binary file.
The the mongo shell database output is
"img" : BinData(0,"QzpcZmFrZXBhdGhcc2FtcGxlLnBuZw=="),
so I would assume that the upload is fine.
When it comes to converting it back to an image on the front end I am using
src={`data:image/png;`+btoa(`${Buffer.from(img.data).toString('base64')}`)}
which gives me a string that looks like this...
data:image/png;UXpwY1ptRnJaWEJoZEdoY2MyRnRjR3hsTG5CdVp3PT0=
However, on the front-end it shows a broken image icon and when I go to this link it just shows
a teeny tiny black square.
I have tried a lot of combinations of concatenating the string but can't seem to get it to display.
Thanks in advance !
Ok, so after many differnet strategies I managed to combine a few bit of code here and there plus some improvisation to build this...
ImageUpload.js
import React from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
class ImageUpload extends React.Component {
constructor(props) {
super(props);
this.state = {
image: {
name: "",
img: ""
},
submitted: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const { name, value } = event.target;
const { image } = this.state;
this.setState({
image: {
...image,
[name]: value,
}
});
}
onSelectChange = (e) => {
const values = [...e.target.selectedOptions].map(opt => opt.value);
this.props.onChange(values);
};
handleSubmit(event) {
event.preventDefault();
console.log(this.state);
this.setState({ submitted: true });
const { image } = this.state;
if (image.name) {
this.props.register(image);
}
}
_onChange = (e) => {
const file = this.refs.uploadImg.files[0]
const reader = new FileReader();
reader.onloadend = () => {
this.setState({
image: {
img: reader.result
}
})
}
if (file) {
reader.readAsDataURL(file);
this.setState({
image: {
img: reader.result
}
})
}
else {
this.setState({
image: {
img: ""
}
})
}
}
render() {
const { registering } = this.props;
const { image, submitted } = this.state;
console.log(this.handleChange);
render() {
const { registering } = this.props;
const { image, submitted } = this.state;
console.log(this.handleChange);
return (
<div className="col-md-9 col-md-offset-3">
<form name="form" onSubmit={this.handleSubmit}>
<div className={'form-group' + (submitted && !image.img ? ' has-error' : '')}>
<label htmlFor="img">Image</label>
<input
ref="uploadImg"
type="file"
name="selectedFile"
onChange={this._onChange}>
{/* value={toString(window.form.elements[0].nextElementSibling.src)}> */}
</input>
<img src={this.state.image.img} />
{submitted && !image.img &&
<div className="help-block">Image is required</div>
}
</div>
<div className="form-group">
<button className="btn btn-primary">Register Building</button>
{registering && }
<Link to="/home" className="btn btn-link">Cancel</Link>
</div>
</form>
</div>
The mongoose Schema is just a simple string:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
Apartment = require('../apartments/apartment.model')
const schema = new Schema(
name: { type: String },
img: { type: String }
}
);
schema.set('toJSON', { virtuals: true });
module.exports = mongoose.model('image', schema);
Hope it helps anyone else. Also if anyone wouldn't mind explaining the potential flaws in uploading extrememly looong strings for images (and any solutions to this) that'd be a big help too. Would there be a way to enter this as a Buffer base64 and convert it? would the db read it this way ?
Thanks !

Trying to display a values from multiple API fetch

Currently I'm reading some stock/share names from my DB (Mongo) and trying to display their price by calling an API for each one of them on my component, but I'm getting only the last one.
HTML
<div class="posts-container">
<div class="post" v-for="post in posts"
v-bind:key="post['2. Symbol'] " >
<p class="text">{{ post['2. Symbol'] }} </p>
</div>
</div>
JS
data(){
return {
posts: [],
error: '',
text: ''
}
},
async created() {
try {
const acoes = await APIService.getPosts();
acoes.map(data => {
fetch(`https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=${data.name}.SA&interval=15min&outputsize=compact`)
.then(res => res.json())
.then(data => {
this.posts = { ...this.posts, ...data}
});
})
console.log(this.posts);
} catch (err) {
this.error = err.message;
}
},
So as I have multiple values on my const acoes I expected to generate data for each one of them, but what I got is just for the last object.
What am I missing here?
There's a smarter way of doing this instead of calling the API on the component?
You can use Promise.all to combine all your result.
const allFetches = acoes.map(
data => fetch(...)
.then(response => response.json()
)
Promise.all(allFetches)
.flatMap(result => result)
.then(posts => {
this.posts = posts;
});

Uploading an image to mongodb using Multer with Express and Axios

I am basically trying to make a small application which allows an admin user to enter a name, price and image of a product which can then be viewed on another page. The details will be sent to a mongo database which will be performed via an axios post from the front end. I can send the name and the price no problem which can be seen on the front end dynamically, however, I am unable to send image to the mongo database which i've been trying to achieve now for quite some time.
I am using multer and axios to try and sent the file over as the application is a react app. I think the problem is to do with the "req.file" within the back end of the application. The code below is my endpoint:
api.js
var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors')
var app = express();
var mongodb = require('mongodb');
var path = require('path');
var fsextra = require('fs-extra');
var fs = require('fs')
var util = require('util')
var multer = require('multer')
var upload = multer( {dest: __dirname + '/uploads'} )
var ejs = require('ejs')
const MongoClient = require('mongodb').MongoClient;
app.use(express.static(path.resolve(__dirname, '../react', 'build')));
app.get('*',(req,res)=>{
res.sendFile(path.resolve(__dirname, '../react', 'build', 'index.html'));
});
console.log(__dirname)
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', __dirname);
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
var db;
mongodb.MongoClient.connect('mongodb://<mydbdetails>', (err, database) => {
if (err) {
console.log(err)
process.exit(1);
}
db = database;
console.log('Database connection is ready')
});
var server= app.listen(process.env.PORT || 8082, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
app.post('/api/submitImage', upload.single('inputForm'), function(req,res){
var file = req.body.file
if (file == null) {
// If Submit was accidentally clicked with no file selected...
//res.render('admin', { title:'Please select a picture file to submit!'});
res.send({success: false, message: "dsfdsg"})
console.log('There is no file present')
console.log(req.file,'file')
}
else{
// read the img file from tmp in-memory location
var newImg = fs.readFileSync(req.files.path);
console.log(newImg,'details of the new image')
// encode the file as a base64 string.
var encImg = newImg.toString('base64');
console.log(encImg,'kdfjndodj')
// define your new document
var newItem = {
description: req.body.description,
contentType: req.file.mimetype,
size: req.files.size,
img: Buffer(encImg, 'base64')
};
db.collection('products').insert(newItem, function(err, result){
if(err) {
console.log(err)
}
var newoid = new ObjectId(result.ops[0]._id);
fs.remove(req.file.path, function(err) {
if (err) { console.log(err) };
res.render('./src/components/adminContainer.js', {title:'Thanks for the Picture!'});
});
})
}
})
The next code is the how I am trying to send it over using Axios:
import axios from 'axios';
class ProductsApi {
static submitProduct(name,prices,callback){
axios.post('http://localhost:8082/api/submitProduct', {name: name, prices: prices})
.then( response => {
callback(response)
})
}
static viewName(callback){
axios.post('http://localhost:8082/api/retrieveName')
.then( response => {
return callback(response)
})
}
static viewPrice(callback){
axios.post('http://localhost:8082/api/retrievePrice')
.then( response => {
return callback(response)
})
}
static viewProducts(callback){
axios.post('http://localhost:8082/api/retrieveProducts')
.then( response => {
return callback(response)
})
}
static submitImages(image,callback){
axios.post('http://localhost:8082/api/submitImage',{image: image})
.then( response => {
return callback(response)
console.log('response has been made,', image,'has been recieved by axios')
})
}
}
export default ProductsApi;
The last file is how I am trying to send the file to the database using react with event handlers:
import React, { Component } from 'react'
import '../App.css'
import AppHeader from './appHeader.js'
import ProductsApi from '../api/axios.js'
const AdminContainer = () => {
return(
<div>
<AppHeader />
<FormContainer />
</div>
)
}
class FormContainer extends Component{
constructor(props){
super(props);
this.state={
file: '',
inputName: '',
inputPrice: '',
image: ''
};
this.handleNameChange = this.handleNameChange.bind(this);
this.handlePriceChange = this.handlePriceChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.sendName = this.handleSubmit.bind(this);
}
handleNameChange(e){
console.log(e.target.value)
this.setState({
name : e.target.value,
})
}
handlePriceChange(e){
console.log(e.target.value)
this.setState({
prices : e.target.value
})
}
sendName(e){
this.setState({
inputName: e.target.value,
inputName:e.target.value
})
}
handleSubmit(e){
e.preventDefault();
console.log('attempting to access axios...')
ProductsApi.submitProduct(this.state.name, this.state.prices, resp => {
console.log('response has been made', resp)
//if error message, add to state and show error message on front end
this.setState({
inputName:this.state.name,
inputPrice:this.state.prices
},function(){
console.log(resp,'this is resp')
console.log('Axios has send ',this.state.name,' to the database')
});
})
console.log(this.state.prices,'This is the new price')
console.log(this.state.name,'This is the new name')
ProductsApi.submitImages(this.state.image, response => {
console.log('axios has been notified to submit an image...')
this.setState({
image: this.state.image
},function(){
console.log('Image submission axios response details are as follows: ', response)
console.log(this.state.image, ': has been sent to the db')
})
})
}
render(){
return(
<div>
<h2>Add a new product to the Shop</h2>
<div className='formWrapper'>
<div className='center'>
<form name='inputForm' encType='multipart/form-data' method='post'>
<label>
Name:
<input value = {this.state.name} onChange={this.handleNameChange} type="text" placeholder='Name' /><br />
Price:
<input value = {this.state.prices} onChange={this.handlePriceChange} type='text' /><br />
</label>
<label>
Choose an Image:
<input className='imgInsert' name ='inputForm' type='file'/>
</label>
<div>
<img className = 'previewImage' value={this.state.image}/>
</div>
<button className='btn updateBtn' onClick={(e) => this.handleSubmit(e)}>Submit</button>
</form>
</div>
</div>
</div>
)
}
}
export default AdminContainer
Common errors I am getting when trying debug it is
TypeError: Cannot read property 'path' of undefined."
and "file" being undefined.
When using multer to save images you need to make sure that the image comes to the server as form data. this is because multer requires the multipart/form-data encoding which you do not get when submitting a form with an ajax request unless if you specifically do something to make it happen.
You can do this by using the FormData object. Here is an example of this being used. I hope this helps.