I am trying to submit a form using axios post request in laravel. In this form i have 3 fields, name,age and a file called image.
here is the form
<form action="{{route('forms.store')}}" method="post" enctype="multipart/form-data">
#{{name}}
#csrf
<span v-if="errors.name">#{{errors.name[0]}}</span>
<label for="name">Name:</label>
<input type="text" name="name" id="name" v-model="name">
<span v-if="errors.age">#{{errors.age[0]}}</span>
<label for="age">Age:</label>
<input type="text" name="age" id="age" v-model="age">
<label for="image">Image:</label>
<span v-if="errors.image">#{{errors.image[0]}}</span>
<input type="file" name="image" id="image" #change="imageChanged">
<button #click.prevent="submitForm">Submit</button>
</form>
Here is my vueJs code:
const app = new Vue({
el: '#app',
data:{
name:'',
age:'',
image:'',
errors:{}
},
methods:{
imageChanged(e){
app.image = e.target.files[0]
console.log(e.target.files[0]);
},
submitForm(){
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
const fd = new FormData(this.$data);
fd.append('image',this.image);
axios.post('{{route('forms.store')}}',this.fd,config).then((response)=>{
console.log(response.data);
}).catch((error)=>{
//console.log(error.response.data);
this.errors = error.response.data.errors;
})
}
}
});
And here is my controller
public function store(Request $request)
{
$this->validate($request, [
'name' => 'required',
'age' => 'required',
]);
if ($request->hasFile('image')) {
$image = $request->file('image');
return $ext = $image->extension();
} else {
return "NOT OK";
}
}
So here I am validating name and age. But my problem is when I fill the form and submit the form,
It sends back errors that name and age field is required.
where am I doing wrong and how to receive this data in the controller.
Thank you in advance.
I believe your code is in error in this part. changes:
axios.post('{{route('forms.store')}}',this.fd,config)
to:
axios.post('{{route('forms.store')}}',fd,config)
Related
I'm a newbie and i'm trying to create a rest project with Vue and Laravel and I have a form that allows to send an image, but when I try to store it in the db, I got an error:
"Request failed with status code 422"
and
"The image must be an image"
I can't figure how to solve it, any suggestion?
<script>
export default {
data() {
return {
title: undefined,
year: undefined,
director: undefined,
plot: undefined,
rating: undefined,
image: null,
};
},
methods: {
insertedFile(e) {
this.image = e.target.files[0];
},
addFilm() {
const formData = new FormData;
formData.set('image', this.image)
console.log(formData.get('image'));
//
axios
.post("/api/films", {
title: this.title,
year: this.year,
director: this.director,
plot: this.plot,
rating: this.rating,
image:formData
})
.then((response) => {
console.warn(response)
});
},
},
};
</script>
<template>
<form #submit.prevent="addFilm()" enctype="multipart/form-data" method="post">
<input type="text" name="title" placeholder="title" v-model="title" />
<input type="number" name="year" placeholder="year" v-model="year" />
<input
type="text"
name="director"
placeholder="director"
v-model="director"
/>
<input type="text" name="plot" placeholder="plot" v-model="plot" />
<input
type="number"
name="rating"
placeholder="rating"
v-model="rating"
/>
<input
type="file"
name="image"
id="image"
#change="insertedFile($event)"
/>
<button type="submit">Submit</button>
</form>
</template>
Controller:
public function store(Request $request)
{
$request->validate([
'title' => 'required',
'year' => 'required',
'plot' => 'required',
'director' => 'required',
'rating' => 'required',
'image' => 'image|mimes:jpg,png,jpeg,svg|max:2048'
]);
$film = new Film([
'title' => $request->title,
'year' => $request->year,
'plot' => $request->plot,
'director' => $request->director,
'rating' => $request->rating,
"image" => $request->file('image')->store('images', 'public')
]);
$film->save();
return redirect()->route('home')
->with('success', 'film created successfully!');
}
Try combining your payload (data) with your formData and setting the content-type header of your axios request to multipart/form-data:
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
let data = new FormData();
data.append('title', this.title);
data.append('year', this.year);
data.append('director', this.director);
data.append('plot', this.plot);
data.append('rating', this.rating);
data.append('image', this.image);
axios.post('api/films', data, config)
.then((response) => {
console.warn(response)
})
.catch((error) => {
console.log(error);
});
you're passing the FormData object as image.
in order to make it work, you should give axios the FormData object containing all the data you want to send.
addFilm method should look like this:
const formData = new FormData;
formData.append('image', this.image)
formData.append('title', this.title)
formData.append('year', this.year)
formData.append('director', this.director)
formData.append('plot', this.plot)
formData.append('rating', this.rating)
formData.append('image', this.image)
axios
.post("/api/films", formData)
.then((response) => {
console.warn(response)
});
I have a view which contains a form and looks like this,
<form class="flex-form" id="form" method="">
<div class="form-component">
<label>Type</label>
<input type="text" id="type" name="type">
</div>
<div class="form-component">
<div class="form-component"><label><b>Contents</b></label></div>
<label>Savoury</label><input type="text" name="savoury" id="savoury">
<label>Fillings</label><input type="text" name="fillings" id="fillings">
<label>Amount</label><input type="text" name="amount" id="amount">
<div class="flex-component">
<button class="set-button" type="button" id="set">Set Item</button>
</div>
</div>
<div class="form-component">
<label class="description-label">Description</label>
<textarea class="fixed-textarea" id="description" name="description" cols="15" rows="10"></textarea>
</div>
<div class="form-component">
<label >Unit Price</label>
<input type="text" id="price" name="unit_price">
</div>
<div class="flex-component">
<button class="form-button" type="submit">Add</button>
</div>
</form>
I have a JavaScript that allows me to capture some intermediary information (via the Set Item button) from the form before the form gets submitted (via the Add Button). I want to handle the form's submission from the script since I need to capture the intermediary data.
let collectedItems = [];
let setter = document.getElementById('set');
let form = document.getElementById('form');
setter.addEventListener('click',getSetContent);
function getSetContent() {
let type = document.getElementById('savoury');
let fillings = document.getElementById('fillings');
let amount = document.getElementById('amount');
const content = {
type: type.value,
fillings: fillings.value.split(','),
amount: Number(amount.value)
};
collectedItems.push(content);
clearInputFields([type,fillings,amount]);
}
function clearInputFields(inputFields) {
inputFields.forEach(field => {
field.value = ''
});
console.log(collectedItems);
}
form.addEventListener('submit',submitForm);
function submitForm() {
const type = document.getElementById('type').value;
const desc = document.getElementById('description').value;
const price = Number(document.getElementById('price').value);
const content = collectedItems;
const data = {
type: type,
contents: content,
description: desc,
unit_price: price
};
post('http://localhost:8001/add/box',
{ 'Content-Type': 'application/json' },
JSON.stringify(data)
);
}
function post(endpoint,header,body) {
const response = fetch(endpoint,{ method: 'POST',headers: header,body: body });
response.then(
resp => {
if (resp.ok) {
console.log('form submitted');
} else {
console.log('form not submitted');
}
}
)
}
I then make a POST request using fetch() to an endpoint I have setup in Express which looks like this,
app.post('/add/box',(req,res) => {
const box: any = req.body;
console.log(box);
// DO SOME DB STUFF
res.redirect('/');
});
The form submission works as intended (logs to terminal using nodemon), however I am unable to redirect to the homepage. Instead I stay on the form page after the submission has occurred and I can't figure out why. Any help with this issue is much appreciated.
I'm new in vue, and I am trying to make a get request from a field that has a v-model="embed.url" after pasting the link. Event after paste works well but, I don't know how to reference to input with v-model="embed.url" and get the data.
When I try code below error appear:
[Vue warn]: Error in v-on handler: "ReferenceError: embed is not defined"
and
ReferenceError: "embed is not defined"
My vue code:
<script type="text/javascript">
axios.defaults.xsrfHeaderName = "X-CSRFToken";
new Vue({
el: '#app',
delimiters: ['!!', '!!'],
data () {
return {
embed: {
url: '',
title: '',
description: '',
type: '',
thumbnail_url: '',
html: '',
},
isPaste: false,
embedsinfo: [],
}
},
methods: {
formSubmit(e) {
e.preventDefault();
let currentObj = this;
axios.post('http://wegemoc.local:8000/recipes/recipe/embed/add/', {
url: this.url,
})
.then(function (response) {
currentObj.output = response.data;
})
.catch(function (error) {
currentObj.output = error;
});
},
paste() {
this.isPaste = true;
},
input() {
if (this.isPaste) {
axios.get('http://iframe.ly/api/oembed?url=' + embed.url + '&api_key=493c9ebbdfcbdac2a10d6b')
.then(response => (this.embedsinfo = response))
isPaste = false;
}
}
},
});
My form:
<div id="app">
!! embedsinfo.title !!
<form method="post" class="margin-bottom-25" #submit="formSubmit">
{% csrf_token %}
<div class="form-group">
<label for="formGroupExampleInput">Adres przepisu*</label>
<input type="url" class="form-control" placeholder="Url" #paste="paste" #input="input" v-model="embed.url">
</div>
<div class="form-group">
<label for="formGroupExampleInput2">Tytuł</label>
<input class="form-control" placeholder="Title" v-model="embed.title">
</div>
<div class="form-group">
<label for="formGroupExampleInput2">Description</label>
<input type="textarea" class="form-control" id="formGroupExampleInput2" placeholder="Description" v-model="embed.description">
</div>
<div class="form-group">
<label for="formGroupExampleInput2">Thumbnail_url</label>
<input type="text" class="form-control" id="formGroupExampleInput2" placeholder="Tthumbnail_url" v-model="embed.thumbnail_url">
</div>
<button type="submit" class="btn btn-success-gradiant">Dodaj link</button>
</form>
</div>
When you are not using Vue as Single Component files, your data property must be an object and not a function. Change your data property to an object and then give it a try.
new Vue({
el: '#app',
delimiters: ['!!', '!!'],
data: {
return {
embed: {
url: '',
...
};
},
...
Also you must access your data embed.url property using "this" like how you have referenced other properties in your code.
if (this.isPaste) {
axios.get('http://iframe.ly/api/oembed?url=' + this.embed.url + '&api_key=493c9ebbdfcbdac2a10d6b')
.then(response => (this.embedsinfo = response))
isPaste = false;
}
We have an old website where I have implemented a form that is sent by AngularJS to a PHP script and after processing an email message get sent. If the form is not valid the PHP script returns a JSON with the validation errors. Since we already use Symfony for some other applications (REST APIs), I thought it would be nice to reimplement my plain PHP script in Symfony.
For the sake of simplicity I put only a small but relevant fragment of my code. This is what I have:
HTML (ng-app is bound on body tag, not shown here):
<form name="infoscreenForm" class="form-horizontal" enctype="multipart/form-data" ng-controller="FormController">
<div class="form-group">
<div class="col-lg-1 control-label">*</div>
<div class="col-lg-11 input-group">
<input type="text" class="form-control" id="contact_person"
name="contact_person" ng-model="formData.contactPerson"
placeholder="Kontaktperson">
</div>
<span class="text-warning" ng-show="errors.contactPerson">
{{ errors.contactPerson }}
</span>
</div>
<div class="form-group">
<div class="col-lg-1 control-label">*</div>
<div class="col-lg-11 input-group">
<span class="input-group-addon">#</span>
<input type="email" class="form-control" id="email" name="email"
ng-model="formData.email" placeholder="E-Mail">
</div>
<span class="text-warning" ng-show="errors.email">
{{ errors.email }}
</span>
</div>
<div class="form-group">
<div class="col-lg-1 control-label"> </div>
<div class="col-lg-11 input-group">
<input type="file" class="form-control" id="file" name="file"
file-model="formData.file"
accept="application/pdf,image/jpeg,image/png">
</div>
<span class="text-warning" ng-show="errors.file">
{{ errors.file }}
</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-default" id="submit"
name="submit" ng-click="submitForm()">
Formular absenden
</button>
</div>
</form>
JS:
var app = angular.module('InfoscreenApp', []);
app.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function () {
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}]);
app.factory('multipartForm', ['$http', function ($http) {
return {
post : function (uploadUrl, data) {
var fd = new FormData();
for (var key in data) {
fd.append(key, data[key]);
}
return $http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers : { 'Content-Type': undefined }
});
}
};
}]);
app.controller('FormController', ['$scope', 'multipartForm', function ($scope, multipartForm) {
$scope.formData = {};
$scope.submitForm = function () {
var uploadUrl = 'http://localhost:8000/infoscreen';
multipartForm.post(uploadUrl, $scope.formData)
.then(function (data) {
console.log(data);
if (data.success) {
$scope.message = data.data.message;
console.log(data.data.message);
} else {
$scope.errors = data.data.errors;
}
});
};
}]);
With the plain PHP script everything works fine. Here is what I tried to do in Symfony:
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
class DefaultController extends Controller
{
/**
* #Route("/infoscreen", name="infoscreen")
*/
public function infoscreenAction(Request $request)
{
$defaultData = array('message' => 'infoscreenForm');
$form = $this->createFormBuilder($defaultData)
->add('contactPerson', TextType::class, array(
'constraints' => array(
new NotBlank(),
)
))
->add('email', EmailType::class, array(
'constraints' => array(
new NotBlank(),
new Email(),
)
))
->add('file', FileType::class)
->add('submit', SubmitType::class)
->getForm();
;
$form->submit($request->request->get($form->getName()));
$data = $form->getData();
if ($form->isValid()) {
echo 'Alles ok';
// send an email
}
$errors = array();
$validation = $this->get('validator')->validate($form);
foreach ($validation as $error) {
$errors[$error->getPropertyPath()] = $error->getMessage();
}
$response = new Response();
$response->setContent(json_encode(array(
'form_data' => $data,
'errors' => $errors,
)));
$response->headers->set('Content-Type', 'application/json');
return $response;
}
}
CSRF is disabled in config.yml. The form is not bound to an entity class. After submitting the form I get the following object in the console:
{
data: Object,
status: 200,
config: Object,
statusText: "OK"
}
The important part is in data: Object:
{
form_data: {
contactPerson: null,
email: null,
message: "infoscreenForm",
file: null
},
errors : {
children[contactPerson].data = "This value should not be blank",
children[email].data = "This value should not be blank"
}
}
This happens when I submit the form with some values entered in the fields. It seems that the submitted data is not bound to the form in the controller. I'm probably missing something, but I stuck here and have no idea how to proceed. I tried with $form->bind($request), $form->handleRequest($request) and few other things, but it didn't work. Even if I bind the fields individually, I still don't get their values in the form.
Can somebody please help me.
Thanks in advance.
Try
$this->get('form.factory')->createNamedBuilder(null, 'form', $defaultData)
instead of
$this->createFormBuilder($defaultData)
I've made a registration form with a lot of fields. Since when I submit data and the validator redirects back with errors some inputs are empty and the user has to lose time refilling them, I want to implement some front-end validations.
I'm stuck on checking if an username is already used on submit button press becasuse I'm not expert about AJAX.
In the AuthController I've created a function that returns a Json containing a response in relation of existence or not of the username in the database.
class UserAuthController extends Controller
{
public function isUserNameInUse( $username )
{
if (Auth::where('username', $username) != null){
return [ 'is_used' => 1 ];
}
return [ 'is_used' => 0 ];
}
}
In the routes.php there are these lines:
Route::group([ 'as' => 'api', 'prefix' => 'api', 'namespace' => 'Api'], function () {
Route::group([ 'as' => 'auth', 'prefix' => 'auth'], function () {
Route::any('/is_username_in_use/{username}', [
'as' => 'isUserNameInUse',
'uses' => 'UserAuthController#isUserNameInUse']);
});
});
The view is like that (only a piece of the form):
<form action="{{ route('web.company.postSignup') }}" method="post" id="signup-form" class="form-horizontal">
{!! csrf_field() !!}
#include( 'errors.handler' )
<label for="username">
{{ _('Username*') }} </label>
<input type="text" class="form-control" name="username" id="username"
value="{{ Input::old('username') }}" required>
<label for="password">
{{ _('Password*') }}
</label>
<input type="password" class="form-control" name="password" id="password"
value="{{ Input::old('password') }}" onchange="form.confirmPassword.pattern = this.value;"
required>
<label for="confirmPassword">
{{ _('Confirm Password*') }}
</label>
<input type="password" class="form-control" name="confirmPassword" id="confirmPassword" required>
<button class="btn btn-warning" id="submit-btn" type="submit">{{ _('Sign Up') }}</button>
</form>
This is the script, for now I've only tried to log the response of the controller, but it prints anything.
$(document).ready(function () {
$('form#signup-form').submit(function () {
var input_username = $('input[name=username]').val();
console.log(input_username);
$.getJSON('/api/auth/is_username_in_use/' + input_username, function (json) {
console.log(json);
});
return false;
});
});
There is no need to make an explicit check if user name is in use. You may skip this part and instead, when you storing your user's data validate them accordingly.
An example of this might be
public function store(Request $request)
{
$this->validate($request, [
'username' => 'required|unique:users',
'password' => 'required|confirmed'
]);
// process your logic
}
This way, if validation failed, you'll get a json response object containing error messages.
Note, this will work if you're on Laravel 5. If you are on 4.* refer to documentation for validation part.
You should change return [ 'is_used' => 0 ]; into return Response::json([ 'is_used' => 0 ]); and add use Response; to the top of your controller.