it's my first time working with Vue and I'm trying to make a simple crud using Spring as backend. My problem is that when I send the actor with the post request, and I try to make a get request to get the new actors list, it doesn't get updated until I refresh the page.
This is part of the html that I want to display. Just a form and the list of actors that I bring from my DB.
<v-flex>
<v-text-field
v-model="newActor.firstName"
label="Nombre"
prepend-icon="person"
></v-text-field>
</v-flex>
<v-flex>
<v-text-field class="ml-5"
v-model="newActor.lastName"
label="Apellido"
> </v-text-field>
</v-flex>
<v-flex>
<v-btn :class="['white--text','green']" #click="addActor">Enviar</v-btn>
</v-flex>
<li v-for="actor in actors" v-bind:key="actor.id" :class="['mt-5']">
id = {{actor.actorId}}
<br>
name = {{actor.firstName}}
<br>
apellido = {{actor.lastName}}
<br>
lastUpdate = {{actor.lastUpdate}}
</li>
This is my vue script.
export default {
data() {
return {
alert: false,
alertMessage: '',
alertType: 'success',
urlBase: 'http://localhost:8081/',
newActor:
{
firstName: '',
lastName: '',
lastUpdate: ''
},
background: 'yellow',
actors: []
}
},
methods:
{
getActors: function()
{
this.actors = []
let self = this
fetch(this.urlBase+'actors/all')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
for (var variable of myJson) {
self.actors.push(variable);
}
});
},
addActor: function()
{
if (this.newActor.firstName != '' && this.newActor.lastName != '')
{
let self = this
fetch(this.urlBase+'actors/add', {
method: 'POST',
body: JSON.stringify(this.newActor),
headers:{
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));
this.alert = true;
this.alertMessage = "Usuario agregado con éxito";
this.getActors(); //Here is where I tried to get the actors again.
console.log(this.actors);
}
else
{
this.alert = true;
this.alertType = 'error';
this.alertMessage = "Usuario no registrado";
}
}
},
created: function()
{
this.getActors()
}
}
It is because this.getActors(); is called before the fetch method is done. That's the way javascript works.
You can try putting this.getActors(); in the then.
Example :
if (this.newActor.firstName != '' && this.newActor.lastName != '')
{
let self = this
fetch(this.urlBase+'actors/add', {
method: 'POST',
body: JSON.stringify(this.newActor),
headers:{
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => {
console.log('Success:', response);
this.alert = true;
this.alertMessage = "Usuario agregado con éxito";
this.getActors(); //Here is where I tried to get the actors again.
console.log(this.actors);
})
}
Related
I am looking to download an image stored on a server into my React Native app.
I had a function that looked like this:
public function image(Request $request, $id)
{
$company = Company::find($id);
$filePath = storage_path() . '/app/' . $company->image;
return response()->file($filePath);
}
And it returned nothing I could read within the app when I tried the following function:
setCompany = async () => {
let company = await AsyncStorage.getItem('currentCompany');
company = JSON.parse(company);
if (company.image !== null) {
let image = await getCompanyPicture({company_id: company.id});
console.log('Here: ', image);
// This is blank, react native returns a warning about data not being of a readable type
}
this.setState({company});
};
I am able to get the image in base_64 using this method:
public function image(Request $request, $id)
{
$company = Company::find($id);
$file_path = storage_path('/app/' . $company->image);
if (file_exists($file_path)) {
$fileData = file_get_contents($file_path);
$fileEncode = base64_encode($fileData);
return response()->json(['status' => 'success', 'data' => ['file' => $fileEncode, 'file_path' => $file_path]]);
}
return response()->json(['status' => 'failure', 'data' => ['file' => null, 'file_path' => $file_path]]);
}
Here is my Axios method too just in case:
export const sendRequest = async (url, data, token, method) => {
let headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Method': 'POST, GET, DELETE, PUT',
};
if (typeof token !== 'undefined' && token !== 'undefined' && token.length) {
headers.Authorization = 'Bearer ' + token;
}
if (method === 'get' && data) {
url +=
'?' +
Object.keys(data)
.map((value) => {
return value + '=' + data[value];
})
.join('&');
data = null;
}
return await axios({
headers: headers,
method: method ? method : 'post',
url: url,
data: data,
})
.then((response) => {
return response;
})
.then((json) => {
return json.data;
})
.catch((error) => {
console.error(error);
if (
error.message !== 'Network Error' &&
error.response.status !== 500 &&
error.response.status !== 413
) {
return error.response.data;
} else if (error.message === 'Network Error') {
return {
status: 'error',
message: 'Unable to connect to server',
};
} else if (error.response.status === 500) {
return {
status: 'error',
message: 'Internal Server Error',
};
} else if (error.response.status === 413) {
return {
status: 'error',
message: 'The file(s) size is too large',
};
} else {
return {
status: 'error',
message: error.message,
};
}
});
};
If anyone could comment on the performance impact of using base_64 instead of the straight file download that would also be helpful
But ultimately I would like a solution for handling the Laravel response()->file() if possible (which I'll use if base_64 is less efficient)
I'm not sure about RN code syntax, but I've ready code with jQuery+poorJS, which looks like this:
$.ajax({
url: "load-image-url", // URL FOR GET REQUEST
cache:false,
xhr: function() { // ACTUALLY THIS PART CAN BE USED AND CUSTOMIZED BY YOU
let xhr = new XMLHttpRequest();
xhr.responseType= 'blob'
return xhr;
},
success: function(data) {
let url = window.URL || window.webkitURL;
$('#image').attr('src', url.createObjectURL(data));
},
error: function(err) {
// console.log(err);
}
}).fail(function() {
$('#ss_product_image').attr('src', "default-image-url.jpg");
});
In my example I've used GET request (but you can try to modify it and test if you want, honestly IDK about that).
This is the back-end part:
public function image(Request $request, $id)
{
// HERE YOU NEED TO GET YOUR IMAGE (using $id or/and $request params) CONTENT FROM SOMEWHERE YOU WANT
$content = <CONTENT>;
return response()->make($content, 200, [
'Content-Type' => (new \finfo(FILEINFO_MIME))->buffer($content),
'Content-length' => strlen($content),
]);
}
I was able to solve this issue by using rn-blob-fetch.
The files are downloaded into a temp cache which can then be accessed for previewing and saving.
this is my function now:
downloadFiles = async (isReply) => {
let {enquiry, reply} = this.state;
this.setState({isLoading: true});
const token = await AsyncStorage.getItem('userToken');
let filePaths = [];
let fileCount = 0;
let files = enquiry.files;
if (isReply) {
files = reply.files;
}
const dirToSave =
Platform.OS == 'ios'
? RNFetchBlob.fs.dirs.DocumentDir
: RNFetchBlob.fs.dirs.DownloadDir;
new Promise((resolve, reject) => {
for (var i = 0; i < files.length; i++) {
var id = files[i].file_id;
var name = files[i].file.file_name;
var ext = extension(name);
const configOptions = Platform.select({
ios: {
appendExt: ext,
fileCache: true,
title: name,
path: `${dirToSave}/${name}`,
},
android: {
useDownloadManager: true,
notification: true,
mediaScannable: true,
fileCache: true,
title: name,
path: `${dirToSave}/${name}`,
},
});
var mime = content(ext);
let headers = {
'Content-Type': mime,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Method': 'POST, GET, DELETE, PUT',
Authorization: 'Bearer ' + token,
};
RNFetchBlob.config(configOptions)
.fetch('GET', BASE_API + '/enquiries/files/download/' + id, headers)
.then(async (response) => {
RNFetchBlob.fs.writeFile(
configOptions.path,
response.data,
'base64',
);
filePaths.push({
title: configOptions.title,
path: configOptions.path,
ext: extension(configOptions.title),
mime,
});
fileCount++;
if (fileCount >= files.length) {
resolve('Download Successful!');
}
})
.catch((error) => {
console.log('File Download Error: ', error.message);
reject('Download Failed');
});
}
})
.then((data) => {
this.setState({isLoading: false, filePaths});
})
.catch((error) => {
console.log('Download Promise Error: ', error);
this.setState({isLoading: false});
});
};
previewDocument = (id) => {
let {filePaths} = this.state;
if (Platform.OS == 'ios') {
RNFetchBlob.ios.openDocument(filePaths[id].path);
} else if (Platform.OS == 'android') {
RNFetchBlob.android.actionViewIntent(
filePaths[id].path,
filePaths[id].mime,
);
}
};
I try to do a single post request to upload multiple files. Now I have a functionally method that works for multiple files. But I want a single request.
submitFile(){
this.contract_file.forEach((file) =>{
let formData = new FormData();
formData.append('file', file.file);
axios.post('contracts/uploadfile/' + this.id_contract,
formData,
{
headers: {
'Content-Type': 'multipart/form-data',
}
}
).then(function(){
//
})
.catch(function(){
//
});
})
},
public function uploadFile(Request $request, Contract $contract)
{
$filename = $request->file('file')->getClientOriginalName();
$path = $request->file('file')->store($contract->id,'uploads');
$contractFile = new ContractFile();
$contractFile->fill([
'contract_id' => $contract->id,
'name' => $filename,
'path' => $path,
])->save();
}
Update:
This is what I changed,but..
let formData = []
this.contract_file.forEach((file,index) =>{
formData[index] = new FormData();
formData[index].append('file', file.file);
})
foreach($request->file('file') as $file){
//same code but I use $fille
}
Message:
Missing boundary in multipart/form-data POST data in Unknown
Update2:
<file-upload
class="btn btn-primary"
:multiple="true"
:drop="true"
:drop-directory="true"
v-model="files"
#input-filter="inputFilter"
#input-file="inputFile"
ref="upload">
<i class="fa fa-plus"></i>
Select files
</file-upload>
My answer is not properly tested since I had to adapt my code. Let me know if it doesn't work or if I'm missing something.
Basically, I built my own FormData to be more flexible and easier to reuse.
Form.vue
<template>
<div>
<input #change="upload($event)"
type="file"
name="picture"
id="new-file"
class="custom-file-input"
aria-label="picture"
multiple
>
<label class="custom-file-label" for="new-file">
<span>File...</span>
<span class="btn-primary">Browse</span>
</label>
<button #click="submit" type="button" >Submit</button>
</div>
<template>
<script>
import MyFormData from "./MyFormData";
export default {
data() {
return {
form: new MyFormData({contract_id: 5, files: []})
}
},
methods: {
upload(event) {
for (let file of event.target.files) {
try {
let reader = new FileReader();
reader.readAsDataURL(file); // Not sure if this will work in this context.
this.form.files.push(file);
} catch {}
}
},
submit(){
this.form.post('/my-url')
.catch(errors => {
throw errors;
})
.then((response) => location = response.data.redirect);
}
}
}
</script>
MyFormData.js
export default class MyFormData {
constructor(data, headers) {
// Assign the keys with the current object MyFormData so we can access directly to the data:
// (new FormData({myvalue: "hello"})).myvalue; // returns 'hello'
Object.assign(this, data);
// Preserve the originalData to know which keys we have and/or reset the form
this.originalData = JSON.parse(JSON.stringify(data));
this.form = null;
this.errors = {};
this.submitted = false;
this.headers = headers || {}
}
// https://stackoverflow.com/a/42483509/8068675
// It will build a multi-dimensional Formdata
buildFormData(data, parentKey) {
if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File) && !(data instanceof Blob)) {
Object.keys(data).forEach(key => {
this.buildFormData(data[key], parentKey ? `${parentKey}[${key}]` : key);
});
} else {
const value = data == null ? '' : data;
this.form.append(parentKey, value);
}
}
// Returns all the new / modified data from MyFormData
data() {
return Object.keys(this.originalData).reduce((data, attribute) => {
data[attribute] = this[attribute];
return data;
}, {});
}
post(endpoint) {
return this.submit(endpoint);
}
patch(endpoint) {
return this.submit(endpoint, 'patch');
}
delete(endpoint) {
return axios.delete(endpoint, {}, this.headers)
.catch(this.onFail.bind(this))
.then(this.onSuccess.bind(this));
}
submit(endpoint, requestType = 'post') {
this.form = new FormData();
this.form.append('_method', requestType);
this.buildFormData(this.data());
return axios.post(endpoint, this.form, {
headers: {
'Content-Type': `multipart/form-data; boundary=${this.form._boundary}`,
}
})
.catch(this.onFail.bind(this))
.then(this.onSuccess.bind(this));
}
onSuccess(response) {
this.submitted = true;
this.errors = {};
return response;
}
onFail(error) {
console.log(error);
this.errors = error.response.data.errors;
this.submitted = false;
throw error;
}
reset() {
Object.assign(this, this.originalData);
}
}
Edit Based on your note specifying you're using vue-upload-component
Your submit method should look like this
submitFile(){
let files = this.contract_file.map((obj) => obj.file));
let form = new MyFormData({files: files});
form.post('contracts/uploadfile/' + this.id_contract)
.then(function(){
//
})
.catch(function(){
//
});
},
In your controller
public function uploadFile(Request $request, Contract $contract) {
if($request->hasFile('files')){
$files = $request->file('files');
foreach ($files as $file) {
$filename = $file->getClientOriginalName();
$path = $file->store($contract->id,'uploads');
$contractFile = new ContractFile();
$contractFile->fill([
'contract_id' => $contract->id,
'name' => $filename,
'path' => $path,
])->save();
}
}
}
Adding the boundary to the Content-Type header fixed my problem. You can do it like below. Just change only submitFile() function.
submitFile(){
this.contract_file.forEach((file) =>{
let formData = new FormData();
formData.append('file', file.file);
axios.post('contracts/uploadfile/' + this.id_contract,
formData,
{
headers: {
'Content-Type': 'multipart/form-data;boundary=' + Math.random().toString().substr(2),
}
}
).then(function(){
//
})
.catch(function(){
//
});
})
},
i'm working in Laravel. i need to upload file with Vuejs. but it's not working. I add this code:
Blade (File upload):
<input class="form-control" type="file" >
Script Vuejs :
var app = new Vue({
el: '#app',
data: {
person: {
id: 0,
user_name:'',
position_id:'',
image:'',
},
},
methods: {
addPerson: function () {
axios.post('/addperson', this.person)
.then(response => {
console.log(response.data);
if (response.data.etat) {
this.person = {
id: 0,
user_name: response.data.etat.user_name,
position_name: response.data.etat.position_id,
image: response.data.etat.image
};
}
})
.catch(error => {
console.log('errors: ', error)
})
},
Controller:
public function addPerson(Request $request){
$person = new Person;
$person->user_name=$request->user_name;
$person->position_id=$request->position_id;
if($request->hasFile('photo')){
$person->image= $request->image->store('image');
}
$person->save();
return back()->with('success', 'New Position added successfully.');
My Axios post function is working without the image upload line code. I just don't know how to add the upload code.
Thank you if someone can help.
In your blade file
<input type="file" #change="onFileChange" name="id_image" id="id_image" class="inputFile">
In your vue.js file, under methods:
onFileChange(e) {
let files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files[0]);
},
createImage(file) {
let reader = new FileReader();
reader.onload = (e) => {
this.person.image = e.target.result;
};
reader.readAsDataURL(file);
},
That should allow your axios code to upload the image. Note, that it uploads in base64, so if you need validators you will have to create a custom Validator for base64 images.
I struggled to find out how to do this, but I've now found a way. Hopefully this makes someones life easier(I have the uploadUserImage method in a mixin):
HTML:
<input type="file" #change="uploadImage($event)">
JS:
uploadImage (e) {
this.file = e.currentTarget.files[0]
let formData = new FormData()
formData.append('img', this.file)
this.uploadUserImage(formData)
}
uploadUserImage: function (formData) {
axios.post('http://snowdon-backend.local:8000/api/users/img', formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(function (response) {
console.log(response)
})
}
Make sure file is set in the data method as well:
data () {
return {
file: ''
}
}
I'm creating a live search input in ionic 3
inside a form group
inside my ts file, I'm using
getsubelements(selectedValue) {
if(selectedValue){
this.VisitesProvider.getEcolesLive(selectedValue).then((result) =>{
this.responseData = result;
});
}
}
and my provider look like :
getEcolesLive(query){
var data = {
"q" :query
}
return new Promise((resolve, reject) =>{
let headers = new HttpHeaders();
this.http.post(ecoleliveurl, JSON.stringify(data), {headers: headers}).
subscribe(res =>{
resolve(res);
console.log(res);
}, (err) =>{
reject(err);
});
});
}
I'm getting the expected result in json format from mu api server:
[{"name":"Ecole EC","id":"22"}]
I want to populate the names in a dropdown list and on click on the desired one of should replace the search input.
You can create a dropdown menu in the component template like this.
<select [ngModel]="selectedItem" (ngModelChange)="onChange($event)" name="sel2">
<option [value]="i" *ngFor="let i of responseData">{{i.name}}</option>
</select>
In your component.
onChange(newValue) {
console.log(newValue);
this.selectedItem = newValue;
}
this is the html
<ion-item>
<ion-label>Ecole</ion-label>
<ion-input type="text" placeholder="Search" formControlName="ecole" (ionChange)="getsubelements($event._value)" ng-focus="focus=true"></ion-input>
</ion-item>
<div id="search-results" ng-show="focus">
<div class="search-result" *ngFor="let i of responseData" (click)="onChange(i.name)">{{i.name}}</div>
</div>
this is the js part
the form groupe :
visitData = {"name":"", "startdate":"","linkedto":"","ecole":"","selectedItem":""};
constructor(public navCtrl: NavController, public navParams: NavParams,private formBuilder: FormBuilder,public VisitesProvider:VisitesProvider) {
this.visiteFormGroup = this.formBuilder.group({
name: ['', Validators.required],
startdate: ['', Validators.required],
ecole:['', Validators.required],
});
}
the two functions :
getsubelements(selectedValue) {
if(selectedValue){
this.VisitesProvider.getEcolesLive(selectedValue).then((result) =>{
this.responseData = result;
var x = document.getElementById("search-results");
x.style.display="block"
});
}
}
onChange(newValue) {
console.log("called")
console.log(newValue.name)
this.visiteFormGroup.controls["ecole"].setValue(newValue);
var x = document.getElementById("search-results");
x.style.display="none"
}
and the provider function
getEcolesLive(query){
var data = {
"q" :query
}
return new Promise((resolve, reject) =>{
let headers = new HttpHeaders();
this.http.post(ecoleliveurl, JSON.stringify(data), {headers: headers}).
subscribe(res =>{
resolve(res);
console.log(res);
}, (err) =>{
reject(err);
});
});
}
i hope it will help somone :)
So im trying to update a comment on my database using a "PUT" method in react
this is my function to fetch the api :
updateComment = () =>{
const comments = this.state.updateComment;
const i = this.state.selectedTicket.id;
fetch(apiurl + "/Comment/" + i +"/update", {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
comments:comments,
})
})
.then((response) => response.json())
.then((responseJson) => {
if (responseJson.status === "SUCCESS") {
alert("Successfully updated comment")
} else {
alert("Could not update comment.")
}
})
}
this is my function to track the change in the editor
changeUpdateComment(event) {
this.setState({updateComment: event});
}
and this is where i have the input for the editor
<button onClick={this.showEditor}>comment</button>
{this.state.showEditor && (
<Editor
editorState={this.state.updateComment}
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onEditorStateChange={this.changeUpdateComment}
/>
)}
<button onClick={this.updateComment}>comment</button>
I keep on getting an Error exception of "Array to String" when trying to update the comment, any solutions?