Redirect route and display message - remix.run

I am wondering if there is a way to redirect a route or return a Response with a data and fetch it at another page with the loader function.
Basically I am trying to create a new object with a form and redirect to another page where I wanted to display a creation success message.
Here is a form page example:
I am trying to send the message in the Response body.
import { ActionFunction, Form } from "remix";
export const action: ActionFunction = async ({ request }) => {
// const formData = await request.formData();
return new Response(JSON.stringify({ message: "Hello world!" }), {
status: 303,
headers: {
Location: "/new-page",
},
});
};
export default function Index() {
return (
<div>
<Form method="post">
<input type="text" id="name" name="name" />
<button type="submit">Submit</button>
</Form>
</div>
);
}
And at the NewPage I needed to know if there is a way to get the message on the redirect response.
import { ActionFunction } from "remix";
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData();
// Get message here
return {
message: "",
};
};
export default function NewPage() {
return <div>New Page</div>;
}

It's a good use case for session flash message 😎
https://remix.run/docs/en/v1/api/remix#sessionflashkey-value
The documentation provides a good example, but the idea behind that is :
Get your form data in Index's action
Store the stringify data in a session cookie flash message
Return a response, using redirect function (helper imported from remix, that make a Response redirect for you)
In NewPage's loader, read the session cookie message and return it. Don't forget to commit your session, it'll delete this flash message for you
Use useLoaderData hook in your component to get the loader's return data
//sessions.server.ts
import { createCookieSessionStorage } from "remix";
// https://remix.run/docs/en/v1/api/remix#createcookiesessionstorage
const { getSession, commitSession, destroySession } =
createCookieSessionStorage({
cookie: {
name: "__session",
secrets: ["r3m1xr0ck5"], // should be a process.env.MY_SECRET
sameSite: "lax",
},
});
import { ActionFunction, Form } from "remix";
import { getSession, commitSession } from "./sessions";
export const action: ActionFunction = async ({ request }) => {
// const formData = await request.formData();
// Get session
const session = await getSession(
request.headers.get("Cookie")
);
session.flash("myMessageKey", "Hello world!");
return redirect("/new-page", {
headers: {
"Set-Cookie": await commitSession(session),
},
});
};
export default function Index() {
return (
<div>
<Form method="post">
<input type="text" id="name" name="name" />
<button type="submit">Submit</button>
</Form>
</div>
);
}
import { LoaderFunction } from "remix";
import { getSession, commitSession } from "./sessions";
export const loader: LoaderFunction = async ({ request }) => {
const formData = await request.formData();
// Get message here
const session = await getSession(
request.headers.get("Cookie")
);
const message = session.get("myMessageKey") || null;
return json(
{ message },
{
headers: {
"Set-Cookie": await commitSession(session), //will remove the flash message for you
// "Set-Cookie": await commitSession(session, { maxAge: SESSION_MAX_AGE }), //re set max age if you previously set a max age for your sessions.
},
}
);
};
export default function NewPage() {
const { message } = useLoaderData();
return <div>New Page {message}</div>;
}

Related

How to show a with axios loaded image in vue?

I have a GET request with axios and get a .png file back and want to show this inside my template. I can't use a path url, because the image is each time differently.
This is my fastapi route.
from io import BytesIO
from fastapi.responses import Response
#app.get("/image", response_class=Response)
def load_image():
...
buffer = BytesIO()
img.save(buffer, format="PNG")
return Response(content=buffer.getvalue(), media_type="image/png")
This is the vue component:
<script>
export default {
name: "Example",
data() {
return {
image: null;
};
},
methods: {
async loadImage() {
const url = "/image";
const response = await $axios.get(url, { responseType: "arraybuffer" });
if (response.status == 200) {
const base64string = btoa(String.fromCharCode(...new Uint8Array(response.data)));
console.log(base64string); // -> this is a empty string
this.image = 'data:image/png;base64,' + base64string;
}
},
mounted() {
this.loadImage();
},
};
</script>
<template>
<div>
<img :src="image" title="Image" />
</div>
</template>
You can...
get the data as a blob by passing { responseType: "blob" } to axios
convert the blob to base64 with FileReader (used blobToData function from https://stackoverflow.com/a/63372663/197546)
use the base64 data as the image src
example:
const app = Vue.createApp({
data() {
return {
imageSrc: null,
};
},
methods: {
async loadImage() {
const url =
"https://images.unsplash.com/photo-1664300067908-84e8beb52a8f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNXx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60";
const response = await axios.get(url, { responseType: "blob" });
if (response.status == 200) {
const base64data = await blobToData(response.data);
this.imageSrc = base64data;
}
},
},
mounted() {
this.loadImage();
},
});
app.mount("app");
function blobToData(blob) {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = () => resolve(reader.result)
reader.readAsDataURL(blob)
})
}
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/axios#0.27.2/dist/axios.min.js"></script>
<app>
<img :src="imageSrc" v-if="imageSrc"/>
</app>
As Chris pointed out, you can also...
get the data as an array buffer by passing { responseType: "arraybuffer" } to axios
convert array to base64 data using btoa(String.fromCharCode(...new Uint8Array(response.data)))
build the src data by adding prepending the content type to the base64 data
example:
const app = Vue.createApp({
data() {
return {
imageSrc: null,
};
},
methods: {
async loadImage() {
const url =
"https://images.unsplash.com/photo-1664300067908-84e8beb52a8f?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwyNXx8fGVufDB8fHx8&auto=format&fit=crop&w=500&q=60";
const response = await axios.get(url, { responseType: "arraybuffer" });
if (response.status == 200) {
const b64 = btoa(String.fromCharCode(...new Uint8Array(response.data)));
const imgData = "data:" + response.headers['content-type'] + ";base64," + b64;
this.imageSrc = imgData;
}
},
},
mounted() {
this.loadImage();
},
});
app.mount("app");
<script src="https://unpkg.com/vue#next/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/axios#0.27.2/dist/axios.min.js"></script>
<app>
<img :src="imageSrc" v-if="imageSrc"/>
</app>

Vue Form component in Laravel getting 422 error on submission

I am trying to add a Vue form component to my Laravel application so I can reuse it in a few places throughout the app. But when I submit the form I get a 422 error saying that the route is not found.
Form component:
<template>
<form #submit.prevent="mail" method="POST">
</form>
</template>
<script>
import FormMixin from '../FormMixin';
export default {
mixins: [ FormMixin ],
data() {
return {
'action': 'submit',
}
}
}
</script>
Form Mixin
export default {
data() {
return {
fields: {},
errors: {},
success: false,
loaded: true,
action: '',
}
},
methods: {
mail() {
if (this.loaded) {
this.loaded = false;
this.success = false;
this.errors = {};
axios.post(this.action, this.fields).then(response => {
this.fields = {}; //Clear input fields.
this.loaded = true;
this.success = true;
}).catch(error => {
this.loaded = true;
if (error.response.status === 422) {
this.errors = error.response.data.errors || {};
}
});
}
},
},
}
Controller
public function mail(NewConctactRequest $contact) {
Mail::to('example#example.com')->send(new NewContact($contact));
return redirect()->route('thank you');
return response()->json(null, 200);
}
Web Routes
Route::get('/home', 'HomeController#index')->name('home');
Route::get('adventures', 'PageController#adventures')->name('adventures');
Route::get('crew', 'PageController#crew')->name('crew');
Route::get('events', 'PageController#events')->name('events');
Route::get('gallery', 'PageController#gallery')->name('gallery');
Route::get('thank_you', 'PageController#thank_you')->name('thank you');
Route::get('contact', 'ContactController#show')->name('contact');
Route::post('submit', 'ContactController#mail')->name('mail contact');
I have Axios installed already and the CSRF token is set in the head pf the document. When I use the form as just a standard form (not using Vue) it submits properly.

How to upload multiple files from Vuejs and Vuex to Laravel?

Im a using Vuetifyjs, and im trying to create a message system with attachments.
i use the Vuex async await for state management and sending http request.
however im sending json format request not multipart/form-data, would it be possible?
//html input
<input type="file" multiple ref="attachments" #change="fileOnChange" />
data() {
return {
form: new Form({
receiver: "",
subject: "",
content: "",
attachments: []
})
}
}
//method
fileOnChange() {
let files = this.$refs.attachments.files;
if (!files.length) {
return false;
}
for (let i = 0; i < files.length; i++) {
this.form.attachments.push(files[i]);
}
},
sendMessage() {
this.addMessage(this.form)
}
and when the action is triggered this is the Vuex code
async addMessage({ commit }, message) {
const config = { headers: { "Content-Type": "multipart/form-data" } };
const response = await axios.post("api/message", message, config);
},
what is the best approach on this to store the files in the LARAVEL storage and save it in the database?

Showing data after hard refresh

im working with vue & laravel.i have a edit profile page with some forms in it(name,email,...)
the default value of this form not showing for the first time, but if user refresh the page everything will be work!!!
<template>
<label>Name:</label>
<input type="text" v-model="name">
<label>Email:</label>
<input type="email" v-model="email">
<template>
<script>
export default {
data () {
return {
name:'',
email:'',
}
},
mounted : function(){
this.getVueItems();
},
methods: {
getVueItems: function(){
axios.get('./api/auth/me').then(response => {
var vm = this;
vm.name = response.data.name;
vm.email = response.data.email;
});
},
getAuthUser () {
this.user = this.$store.getters.currentUser
},
updateAuthUser () {
this.submiting = true,
axios.put('./api/auth/update', {
name:this.name,
email:this.email,
})
.then(response => {
// this.submiting = false;
location.reload(true);
// success();
})
.catch(error => {
this.submiting = false;
})
},
}
}
</script>
whats is the problem?
As you are using arrow function this keyword is already accessible inside the function.
And for this you should first check in console if you are getting proper response value from api in console.
Hence change your function as below and check once.
async getVueItems() {
await axios.get('./api/auth/me').then(response => {
console.log(response);
this.name = response.data.name;
this.email = response.data.email;
});

Angular 2 Multipart AJAX Upload

I'm using Angular 2 with Spring MVC. I currently have an Upload component that makes an AJAX call to the Spring backend and returns a response of parsed data from a .csv file.
export class UploadComponent {
uploadFile: function(){
var resp = this;
var data = $('input[type="file"]')[0].files[0];
this.fileupl = data;
var fd = new FormData();
fd.append("file", data);
$.ajax({
url: "uploadFile",
type: "POST",
data: fd,
processData: false,
contentType: false,
success: function(response) {
resp.response = response;
},
error: function(jqXHR, textStatus, errorMessage) {
console.log(errorMessage);
}
});
};
}
This works, I get a valid response back; however, is there a more angular 2 way to pass this file to Spring and receive a response? I've been looking into creating an injectible service and using subscribe, but I've been struggling to get a response back
I ended up doing the following:
import { Component, Injectable } from '#angular/core';
import { Observable} from 'rxjs/Rx';
const URL = 'myuploadURL';
#Component({
selector: 'upload',
templateUrl: 'upload.component.html',
styleUrls: ['upload.component.css']
})
export class UploadComponent {
filetoUpload: Array<File>;
response: {};
constructor() {
this.filetoUpload = [];
}
upload() {
this.makeFileRequest(URL, [], this.filetoUpload).then((result) => {
this.response = result;
}, (error) => {
console.error(error);
});
}
fileChangeEvent(fileInput: any){
this.filetoUpload = <Array<File>> fileInput.target.files;
}
makeFileRequest(url: string, params: Array<string>, files: Array<File>) {
return new Promise((resolve, reject) => {
let formData: any = new FormData();
let xhr = new XMLHttpRequest();
for(let i =0; i < files.length; i++) {
formData.append("file", files[i], files[i].name);
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.response));
} else {
reject(xhr.response);
}
}
};
xhr.open("POST", url, true);
xhr.send(formData);
});
}
}
I can then inject a response into my html like:
<div class="input-group">
<input type="file" id="file" name="file" placeholder="select file" (change)="fileChangeEvent($event)">
<input type="submit" value="upload" (click)="upload()" class="btn btn-primary">
</div>
<div *ngIf="response">
<div class="alert alert-success" role="alert">
<strong>{{response.myResponseObjectProperty | number}}</strong> returned successfully!
</div>
This has support for multiple file uploads. I created it as an injectable service in this plunkr:
https://plnkr.co/edit/wkydlC0dhDXxDuzyiDO3

Resources