Error: Objects are not valid as a React child (found: \[object Promise\]). If you meant to render a collection of children, use an array instead." - next.js13

I am making a project using Next js 13 beta version. I know all the components are server components so when I use useState, I need to write "use client" statement at the top but in this case, I got "Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead."
"use client";
import { useRouter } from "next/navigation"
import { useState } from "react"
async function addNewStudent(sInfo, refresh){
await fetch('/api/students', {
method: "POST",
body: JSON.stringify(sInfo)
});
refresh()
}
export default async function Student() {
const router = useRouter()
const [sInfo, setSInfo] = useState({
sName:"",
sSurname:"",
sMobile:"",
sEmail:"",
sAddress:"",
sClass:""
})
const handleChange = (event) => {
setSInfo({ ...sInfo, [event.target.name]: event.target.value });
};
const handleSubmit = async (e) => {
e.preventDefault()
await addNewStudent(sInfo, router.refresh)
}
return (
<form onSubmit={handleSubmit} className="relative bg-white rounded-lg shadow dark:bg-gray-700">
<div className="flex items-start justify-between p-4 border-b rounded-t dark:border-gray-600">
<h3 className="text-xl font-semibold text-gray-900 dark:text-white">
Edit Student
</h3>
<button type="button" className="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-hide="editUserModal">
<svg aria-hidden="true" className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path></svg>
</button>
</div>
<div className="p-6 space-y-6">
<div className="grid grid-cols-12 gap-6">
<div className="col-span-12 sm:col-span-4">
<label htmlFor="first-name" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Name</label>
<input type="text" value={sInfo.sName} onChange={handleChange} name="sName" id="first-name" className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="student name" />
</div>
<div className="col-span-12 sm:col-span-4">
<label htmlFor="last-name" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Lastname</label>
<input type="text" value={sInfo.sSurname} onChange={handleChange} name="sSurname" id="last-name" className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="student lastname" />
</div>
<div className="col-span-12 sm:col-span-4">
<label htmlFor="email" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Email</label>
<input type="email" value={sInfo.sEmail} onChange={handleChange} name="sEmail" id="email" className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="email" />
</div>
<div className="col-span-12 sm:col-span-4">
<label htmlFor="phone-number" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Mobile</label>
<input type="number" value={sInfo.sMobile} onChange={handleChange} name="sMobile" id="phone-number" className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="mobile" />
</div>
<div className="col-span-12 sm:col-span-4">
<label htmlFor="class" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Class</label>
<select id="class" value={sInfo.sClass} onChange={handleChange} name="sClass" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option selected>Class</option>
<option value="5">5. Sınıf</option>
<option value="6">6. Sınıf</option>
<option value="7">7. Sınıf</option>
<option value="8">8. Sınıf</option>
<option value="9">9. Sınıf</option>
<option value="10">10. Sınıf</option>
<option value="11">11. Sınıf</option>
<option value="12">12. Sınıf</option>
</select>
</div>
<div className="col-span-12 sm:col-span-4">
<label for="department" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Address</label>
<input type="text" value={sInfo.sAddress} onChange={handleChange} name="sAddress" id="department" className="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="address" />
</div>
</div>
</div>
<div className="flex items-center p-6 space-x-2 border-t border-gray-200 rounded-b dark:border-gray-600">
<button type="submit" className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800">Save all</button>
</div>
</form>
)
}
When I delete the statement "use client", I got "You're importing a component that needs useState. It only works in a Client Component but none of its parents are marked with "use client", so they're Server Components by default."

Related

Using x-if doesn't bind the data in livewire wire:model

I have a form with several input fields and some of them render conditionally using AlpineJS x-if, those elements have wire:model data, but when the elements rendered conditionally the model binding seems not working. I tried to print the variable on the element but it doesn't work. Without x-if it works fine.
<div class="form-check" >
<input class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-[#60D619] checked:border-[#60D619] focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" type="radio" id="company" wire:model="isCompany" #click="open = true" value="true">
<label class="form-check-label inline-block px-1 text-sm text-gray-600" for="company">
Yes, I'm a company.
</label>
</div>
<div class="form-check">
<input class="form-check-input appearance-none rounded-full h-4 w-4 border border-gray-300 bg-white checked:bg-[#60D619] checked:border-[#60D619] focus:outline-none transition duration-200 mt-1 align-top bg-no-repeat bg-center bg-contain float-left mr-2 cursor-pointer" type="radio" id="person" wire:model="isCompany" #click="open = false" value="false">
<label class="form-check-label inline-block px-1 text-sm text-gray-600" for="person">
No. I'm not a company.
</label>
</div>
<div class="py-1" x-show="open" x-transition>
<span class="px-1 text-sm text-gray-600">Company Name</span>
<input wire:model.lazy="companyName" placeholder="" type="text"
class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md focus:placeholder-gray-500 focus:bg-white focus:border-gray-600 focus:outline-none">
</div>
<div class="py-1" x-show="!open" x-transition>
<span class="px-1 text-sm text-gray-600">User Name</span>
<input wire:model="name" placeholder="" type="text"
class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md focus:placeholder-gray-500 focus:bg-white focus:border-gray-600 focus:outline-none">
</div>
<div class="py-1" x-show="!open" x-transition>
<span class="px-1 text-sm text-gray-600">First Name</span>
<input wire:model="fullName" placeholder="" type="text"
class="text-md block px-3 py-2 rounded-lg w-full
bg-white border-2 border-gray-300 placeholder-gray-600 shadow-md focus:placeholder-gray-500 focus:bg-white focus:border-gray-600 focus:outline-none">
</div>
When x-show uses the issue goes away but the unnecessary input fields are not cleared. I don't want those data to be saved on the database. Is this because x-show only toggles the visibility of the elements and x-if completely removes the element from the DOM? Or is there a way to reset the values of the input fields when visibility is toggled using x-show?
x-if indeed removes the element, while x-show only hides it.
wire:model only works if it's loaded in with Livewire. Else Livewire doesn't "know" those fields exist, and thus can't add an event listener to detect change/input/etc. Therefore, you want to do what #Bennett already mentioned, and only hide the inputs, and clear the fields.
You can use $wire to directly clear the input. Otherwise, you will have to dispatch a change or input event on the input field (change for lazy, input for non-lazy).

How can I create an array of id's from the selected option in vue-multiselect plugin?

I've created an Laravel Vue SPA app. Right now, I can successfully retrieved the data in object format and can display the names in vue-multiselect (https://vue-multiselect.js.org/) when selected. Also, I can already save the selected options to the database in an object format. My question is how can I save only the id's in an array?
This is the results from api:
[
{
"id": 1,
"name": "Shoe Machine Operators",
"description": "Iusto cupiditate quo veniam.",
"created_at": "2022-03-23T10:23:35.000000Z",
"updated_at": "2022-03-23T10:23:35.000000Z"
},
{
"id": 2,
"name": "Librarian",
"description": "Vero eius quidem quo fugiat.",
"created_at": "2022-03-23T10:23:35.000000Z",
"updated_at": "2022-03-23T10:23:35.000000Z"
}
]
Here's my markup code where my vue-multiselect element is:
<form #submit.prevent="addEmployee">
<div class="flex space-x-6 md:w-3/4">
<div class="md:w-3/6 mb-4 flex-1">
<label class="block text-gray-700 text-sm font-bold mb-2" for="name">First Name</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline required" name="name" id="name" type="text" placeholder="First Name" tabindex="1" v-model="employee.first_name" />
</div>
<div class="md:w-3/6 mb-4 flex-1">
<label class="block text-gray-700 text-sm font-bold mb-2" for="name">Last Name</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline required" name="name" id="name" type="text" placeholder="Last Name" tabindex="2" v-model="employee.last_name" />
</div>
</div>
<div class="flex space-x-6 md:w-3/4">
<div class="md:w-3/6 mb-4 flex-1">
<label class="block text-gray-700 text-sm font-bold mb-2" for="designation_id">Designation</label>
<multiselect v-model="employee.designation_id"
:options="designation_options"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:preserve-search="true"
placeholder="Pick one or many"
label="name"
track-by="name"
:preselect-first="true"
>
<template slot="selection" slot-scope="{ values, search, isOpen }"><span class="multiselect__single" v-if="values.length && !isOpen">{{ values.length }} options selected</span></template>
</multiselect>
<pre class="language-json"><code>{{ employee.designation_id }}</code></pre>
</div>
<div class="md:w-3/6 mb-4 flex-1">
<label class="block text-gray-700 text-sm font-bold mb-2" for="position_id">Position</label>
<multiselect v-model="employee.position_id"
:options="position_options"
:multiple="true"
:close-on-select="false"
:clear-on-select="false"
:preserve-search="true"
placeholder="Pick one or many"
label="name"
track-by="name"
:preselect-first="true"
>
<template slot="selection" slot-scope="{ values, search, isOpen }"><span class="multiselect__single" v-if="values.length && !isOpen">{{ values.length }} options selected</span></template>
</multiselect>
<pre class="language-json"><code>{{ employee.position_id }}</code></pre>
</div>
</div>
<div class="flex space-x-6 md:w-3/4">
<div class="md:w-3/6 mb-4 flex-1">
<label class="block text-gray-700 text-sm font-bold mb-2" for="basic_pay">Basic Pay</label>
<div class="relative rounded">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="text-gray-700">₱</span>
</div>
<input class="shadow appearance-none border rounded w-full py-2 pl-8 pr-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline required" name="basic_pay" id="basic_pay" type="number" step="any" placeholder="00.00" tabindex="5" v-model="employee.basic_pay" />
<div class="absolute inset-y-0 right-0 flex items-center"><label for="basic_pay" class="sr-only">Basic Pay</label>
</div>
</div>
</div>
<div class="md:w-3/6 mb-4 flex-1"> </div>
</div>
<button type="submit" class="sm:hidden md:flex bg-blue-500 hover:bg-blue-400 text-white font-bold py-2 px-4 border-b-4 border-blue-700 hover:border-blue-500 rounded outline-none focus:outline-none">Create</button>
</form>
Here's my script:
<script>
export default {
data() {
return {
employee: {},
designation_id: [],
designation_options: [],
}
},
methods: {
addEmployee() {
this.axios.post('/api/employees', this.employee).then(response => (this.$router.push({
name: 'employees'
}))).catch(err => console.log(err)).finally(() => this.loading = false)
},
getDesignationNames() {
this.axios.get('/api/designation').then((res) => {
this.designation_options = res.data;
}).catch(err => console.log(err))
},
},
created: function() {
this.getDesignationNames();
},
}
</script>
Any help is much appreciated.
I tried getting only the id using .map() function but it caused the vue-multiselect to not show the names and return only the first clicked result in an id format.
getDesignationNames() {
this.axios.get('/api/designation').then((res) => {
this.designation_options = res.data.map((a) => {
return a.id;
});
}).catch(err => console.log(err))
},
Use v-model.
Define v-model binding as an array.
Check this example
<script setup>
import { ref } from 'vue'
const data = ref([{id:1,name:"first"},{id:2,name:"second"},{id:3,name:"third"},{id:4,name:"forth"},{id:5,name:"fifth"}])
const selected = ref(["2","4"])
</script>
<template>
<select v-model="selected" multiple>
<option v-for="item in data" :value="item.id">{{item.name}}</option>
</select>
<div>Selected: {{selected}}</div>
</template>

validation depends on radio button selection laravel

I have different inputs depends on radio buttons. How to validate them according to selection?
<div class="sm:col-span-2">
<div class="mt-4">
<nav class=" " aria-label="Tabs">
<div class="border-gray-300 border-2 rounded-lg pl-3 py-2 w-40" :class="tab == 'tab1' ? ' bg-green-200 border-green-500 ' : ''">
<input type="radio" #click="tab = 'tab1'" checked="tab == 'tab1' ? true: false" name="person_company"> <span class="text-gray-600 font-medium text-sm pl-2">person</span> </input>
</div>
<br>
<div class="border-gray-300 border-2 rounded-lg pl-3 py-2 w-40" :class="tab == 'tab2' ? ' bg-green-200 border-green-500 ' : ' '">
<input type="radio" #click="tab = 'tab2'" name="person_company"> <span class="text-gray-600 font-medium text-sm pl-2">company</span> </input>
</div>
</nav>
</div>
</div>
<div class="sm:col-span-2 " x-show="tab == 'tab1'">
<label for="identity_no" class="block text-sm font-medium text-gray-700">Identity</label>
<div class="mt-1">
<input type="text" name="identity_no" id="identity_no" class="block w-full py-4 border-gray-300 rounded-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm">
</div>
</div>
<div class="sm:col-span-2" x-show="tab == 'tab2'">
<label for="company" class="block text-sm font-medium text-gray-700">Company name</label>
<div class="mt-1">
<input type="text" name="company" id="company" class="block w-full py-4 border-gray-300 rounded-md shadow-sm focus:ring-green-500 focus:border-green-500 sm:text-sm">
</div>
</div>
this code needs both of them that's not what I want
$this->validate($request,[
'identity' => 'required',
'company' => 'required',
]);
You should use jquery to manage it. First of all, you will have to take values to radio inputs. And then apply the required property through jquery condition.
<input type="radio" #click="tab = 'tab1'" checked="tab == 'tab1' ? true: false" name="person_company" value = "tab1"> <span class="text-gray-600 font-medium text-sm pl-2">person</span> </input>
<input type="radio" #click="tab = 'tab2'" name="person_company"> <span class="text-gray-600 font-medium text-sm pl-2" value = "tab2">company</span> </input>
$('input[type=radio][name= person_company]').change(function() {
if (this.value == 'tab1') {
$("#identity_no").prop('required',true);
$("#company").prop('required',false);
}
else if (this.value == 'tab2') {
$("#company").prop('required',true);
$("#identity_no").prop('required',false);
}
});

make validation on the uploaded file with laravel

how can i add validation roles to the file, in this example here:
public function store( Product $product, Request $request)
{
$this->validate($request, [
'info' => ['required'],
'min' =>['required'],
'max' => ['required'],
'file' => ['nullable', 'mimes:jpg,jpeg,png,PNG,doc,docx,pdf,xsl,xlsx'],
]);
//store
if($request->hasFile('file')) {
$dest_path = 'public/orders/files';
$order_file = $request->file('file');
$file_name = time().'.'.$order_file->getClientOriginalName();
$order_file->storeAs($dest_path, $file_name);
try {
Order::create([
'info' => $request->info,
'min' => $request->min,
'max' => $request->max,
'file' => $file_name,
'product_id' => $product->id,
'user_id' => auth()->id(),
'owner_id' => $product->user->id
])->save();
in the method above it doesn't working...
and this is the .blade file:
<form action="{{route('order.store', [$product->id])}}" method="post"
enctype="multipart/form-data" class="">
#csrf
<div class="px-4 py-5 sm:p-6 items-center justify-center mb-5">
<label for="info" class="block font-medium text-sm text-gray-700">Details</label>
<textarea rows="5" name="info" id="info"
class="rounded-md shadow-sm mt-1 block w-full p-5"
placeholder="write any information about your order"></textarea>
#error('info')
<p class="text-sm text-red-600">{{ $message }}</p>
#enderror
</div>
<div class="flex flex-wrap -mx-3 mb-6">
<div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label class="block tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-name">
Minimum Period (in Days)
</label>
<input name="min" value="{{old('min')}}" class="#error('min') border-red-500 #enderror appearance-none block w-full bg-white text-gray-700 border border-gray-200 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white" id="grid-first-name" type="number" placeholder="30">
#error('min')
<p class="text-red-500 text-xs italic">{{$message}}</p>
#enderror
</div>
<div class="w-full md:w-1/2 px-3">
<label class="block tracking-wide text-gray-700 text-xs font-bold mb-2" for="grid-email">
Maximum Period (in Days)
</label>
<input name="max" value="{{old('max')}}" class="#error('max') border-red-500 #enderror appearance-none block w-full bg-white text-gray-700 border border-gray-200 rounded py-3 px-4 leading-tight focus:outline-none focus:bg-white focus:border-gray-500" id="grid-email" type="number" placeholder="40">
#error('max')
<p class="text-red-500 text-xs italic">{{$message}}</p>
#enderror
</div>
</div>
<div class="bg-gray-100 text-center justify-center text-black
font-bold border border-red-600 p-5 mb-10">
send a word, pdf, text or image file contains all information about your order, like information
about yourself, if your order is CV, or information, digrams and images ..etc.. about the
application if your order is application.
</div>
<div class="flex w-full h-20 items-center justify-center bg-grey-lighter">
<label
class="w-64 flex flex-col items-center px-4 py-6 bg-white text-blue rounded-lg shadow-lg tracking-wide uppercase border border-blue cursor-pointer hover:bg-blue hover:text-white">
<svg class="w-8 h-8" fill="currentColor" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20">
<path
d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1zM11 11h3l-4-4-4 4h3v3h2v-3z"/>
</svg>
<span class="mt-2 text-base leading-normal">Select a file</span>
<input type='file' name="file" class="hidden"/>
</label>
</div>
Thanks
There are two things you have to check:
Does your form have enctype="multipart/form-data"?
Check your file field name. Is itfile?

Laravel resource controller with AJAX form validation

I want to validate the form with AJAX, because when the normal validation fails, the uploaded files are gone.
I get a JSON response but at the working URL: /properties, instead of the form properties/create.
This is an example of the response:
{"error":{"title":["The title must be at least 10 max:50 characters."],"asking_price":["The asking price must be at least 10000."],"description":["The description must be at least 100 characters."],"feat_image_path":["The feat image path has invalid image dimensions."]}}
I tried a lot of things, but none is working.
Am I doing something wrong logically, or is it the URL of the AJAX script??
Thanks in advance, Sander
This is the blade file with the AJAX script.
#section('tinymce')
<script src="https://cdn.tiny.cloud/1/izqu9favecmjvbbfp1dhe96fphutb1stn4aqs7363k6j8trm/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>
<script>
tinymce.init({
selector: '#description'
});
</script>
#stop
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Properties') }}
</h2>
</x-slot>
<div class="py-12">
<div class=" mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 bg-white border-b border-gray-200">
<h1 class="text-2xl px-12 font-bold w-full my-4">Add Property<span class="text-sm ml-2 text-red-500 italic">* is required</span></h1>
<div class="container my-4 xl:text-lg text-center">
<div class="w-full mx-auto">
#if ($errors->any())
<div class="alert alert-danger">
<ul>
#foreach ($errors->all() as $error)
<li>{{ $error }}</li>
#endforeach
</ul>
</div>
#endif
<div class="alert alert-danger print-error-msg" style="display:none">
<ul></ul>
</div>
{{-- Add Porperty Form --}}
<form action="../properties" method="post" enctype="multipart/form-data" id="create_property_form" class="bg-white shadow-md rounded mb-4 xl:mb-8 w-full">
#csrf
<h2 class="text-center text-xl font-bold w-full my-4">General Information</h2>
{{-- Title --}}
<div class="form-wrapper w-full flex flex-wrap px-8">
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="title">
Property Title*
</label>
<input class="block shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="title" type="text" placeholder="Title of the property" name="title" value="{{ old('title') }}" required>
<span class="error title-error"></span>
</div>
{{-- Asking Price --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="asking_price">
Asking Price*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="asking_price" name="asking_price" type="number" placeholder="Price in $ USD" value="{{ old('asking_price') }}" required>
<span class="error asking_price-error"></span>
</div>
<h2 class="text-center text-xl font-bold w-full my-4">Adress</h2>
{{-- Street --}}
<div class="mb-4 xl:mb-8 w-full xl:w-2/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="street">
Street*
</label>
<input class="block shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="street" name="street" type="text" placeholder="Name of the street" value="{{ old('street') }}" required>
<span class="error street-error"></span>
</div>
{{-- Number --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="number">
Number*
</label>
<input class="block shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="number" name="number" type="text" placeholder="Appt/house number" value="{{ old('number') }}" required>
<span class="error number-error"></span>
</div>
{{-- Zip Code --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="zip_code">
Zip Code
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="zip_code" name="zip_code" type="text" placeholder="Zip / Postal code" value="{{ old('zip_code') }}">
<span class="error zip_code-error"></span>
</div>
{{-- City --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="city">
City*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="city" name="city" type="text" placeholder="eg. 'New Tork'" value="{{ old('city') }}" required>
<span class="error city-error"></span>
</div>
{{-- Location --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/3 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="location">
Location*
</label>
<select name="location_id" id="location_id" class="w-full">
#foreach ($locations as $location)
<option value="{{ $location->id }}">{{ $location-> name}}</option>
#endforeach
</select>
</div>
{{-- Coördinates --}}
<h2 class="text-center text-xl font-bold w-full my-4">Position<span class="text-sm ml-2 text-gray-500">Get coördinates with Google Maps or Latlong </span></h2>
{{-- Latitude --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="latitude">
Latitude*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="latitude" name="latitude" min="11.979144" max="18.145982" step="0.000001" type="number" placeholder="eg. 12.123456" value="{{ old('latitude') }}" required>
<span class="error latitude-error"></span>
</div>
{{-- Longitude --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="longitude">
Longitude*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="longitude" name="longitude" min="-68.440281" max="-62.966999" step="0.000001" type="number" placeholder="eg. -64.123456" value="{{ old('longitude') }}" required>
<span class="error longitude-error"></span>
</div>
{{-- Details --}}
<h2 class="text-center text-xl font-bold w-full my-4">Property Details</h2>
{{-- Built Year --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="built_in">
Built in Year*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="built_in" name="built_in" min="1900" step="1" type="number" placeholder="YYYY" value="{{ old('built_in') }}" required>
<span class="error built_in-error"></span>
</div>
{{-- Indoor Area --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="area_size_indoor">
Indoor Area Size*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="area_size_indoor" name="area_size_indoor" type="number" placeholder="Size in sqm (m2)" step="1" value="{{ old('area_size_indoor') }}" required>
<small>1 sqft = 0.09290304 sqm</small>
<span class="error area_size_indoor-error"></span>
</div>
{{-- Outdoor Area --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="area_size_outdoor">
Outdoor Area Size*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="area_size_outdoor" name="area_size_outdoor" type="number" placeholder="Size in sqm (m2)" step="1" value="{{ old('area_size_outdoor') }}" required>
<small>1 sqft = 0.09290304 sqm</small>
<span class="error area_size_outdoor-error"></span>
</div>
{{-- Bedrooms --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="bedrooms">
Bedrooms*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="bedrooms" name="bedrooms" type="number" step="1" value="{{ old('bedrooms') }}" required>
<span class="error bedrooms-error"></span>
</div>
{{-- Bathrooms --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="bathrooms">
Bathrooms*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="bathrooms" name="bathrooms" type="number" step="1" value="{{ old('bathrooms') }}" required>
<span class="error bathrooms-error"></span>
</div>
{{-- Property Type --}}
<div class="mb-4 xl:mb-8 w-full xl:w-4/12 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="property_type">
Property Type*
</label>
<select name="property_type" id="property_type" class="w-full">
<option value="Villa">Villa</option>
<option value="Chalet">Chalet</option>
<option value="Appartment">Appartment</option>
<option value="Condo">Condo</option>
<option value="Lot">Lot</option>
<option value="Penthouse">Penthouse</option>
</select>
</div>
{{-- Desciption --}}
<div class="mb-4 xl:mb-8 w-full">
<label class="block text-gray-500 text-base font-bold mb-2" for="description">
Description*
</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="description" name="description">
{{ old('description') }}
</textarea>
<span class="error description-error"></span>
</div>
{{-- Featured Image --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="feat_image_path">
Featured Image*
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" id="feat_image_path" name="feat_image_path" type="file" value="{{ old('feat_image_path') }}" required>
<span class="error feat_image_path-error"></span>
</div>
{{-- Gallery Images --}}
<div class="mb-4 xl:mb-8 w-full xl:w-1/2 px-4">
<label class="block text-gray-500 text-base font-bold mb-2" for="bathrooms">
Gallery Images
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-500 leading-tight focus:outline-none focus:shadow-outline" type="file" name="file[]" id="file" required="" multiple>
<span class="error file-error"></span>
</div>
{{-- Submit --}}
<div class=" mb-4 xl:mb-8 mx-auto w-48">
<input class="py-2 bg-green-300 hover:bg-green-200 text-white w-full font-bold rounded shadow" data-resource="" value="Submit Property" type="submit">
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
The AJAX script:
$(document).ready(function() {
$("#create_property_form").click(function(e) {
e.preventDefault();
let _token = $("input[name='_token']").val();
let title = $("input[name='asking_price']").val();
let latitude = $("input[name='latitude']").val();
let longitude = $("input[name='longitude']").val();
let description = $("textarea[name='description']").val();
let feat_image_path = $("textarea[name='feat_image_path']").val();
$.ajax({
url: "{{ url('/properties/create') }}"
, type: 'POST'
, data: {
_token: _token
, title: title
, latitude: latitude
, longitude: longitude
, description: description
, feat_image_path: feat_image_path
}
success: function(data) {
console.log(data.error)
if ($.isEmptyObject(data.error)) {
alert(data.success);
} else {
printErrorMsg(data.error);
}
}
});
});
function printErrorMsg(msg) {
$.each(msg, function(key, value) {
console.log(key);
$('.' + key + '_err').text(value);
});
}
});
I have one route for the resource controller:
Route::resource('properties', 'App\Http\Controllers\User\PropertyController');
My goal is to only show errors, when no error I add everything in the DB.
Here is the store method:
public function store(Request $request)
{
// Validate input
$validator = Validator::make($request->all(),[
'title' => 'required|unique:properties|min:10 max:50',
'asking_price' => 'required|numeric|min:10000|max:1000000000',
'street' => 'required|min:3',
'number' => 'required',
'city' => 'required|min:3',
'latitude' => 'required|between:9,12',
'longitude' => 'required|between:9,12',
'built_in' => 'required|numeric',
'area_size_indoor' => 'required|numeric',
'area_size_outdoor' => 'required|numeric',
'bedrooms' => 'required|numeric',
'bathrooms' => 'required|numeric',
'description' => 'required|min:100',
'feat_image_path' => 'required|image|dimensions:min_width=1500,min_height=800|max:2500',
]);
if ($validator->passes()) {
return response()->json(['success'=>'Added new records.']);
}
return response()->json(['error'=>$validator->errors()]);
// Generate random number for filename
$random = rand(1111,9999) * rand(1, 9);
// Validate Featured Image
if($request->hasFile('feat_image_path'))
{
// Make a full size imaye
ImageIvn::make($request->feat_image_path)->resize(1500,800)->save(public_path('img/' . $random . '_feat.jpg'))
->resize(400,300)->save(public_path('img/' . $random . '_feat_thumb.jpg'));
}
// Store Property
$property = new Property([
'title' => $request->title,
'slug' => Str::slug($request->title),
'user_id' => Auth::id(),
'street' => $request->street,
'number' => $request->number,
'zip_code' => $request->zip_code,
'city' => $request->city,
'asking_price' => $request->asking_price,
'status' => 'For Sale',
'location_id' => $request->location_id,
'property_type' => $request->property_type,
'latitude' => $request->latitude,
'longitude' => $request->longitude,
'built_in' => $request->built_in,
'area_size_indoor' => $request->area_size_indoor,
'area_size_outdoor' => $request->area_size_outdoor,
'bedrooms' => $request->bedrooms,
'bathrooms' => $request->bathrooms,
'description' => $request->description,
'feat_image_path' => 'img/' . $random . '_feat.jpg',
]);
$property->save();
/**
* Uploaded gallery images are stored with the use of the property ID as FK
*/
// Validate Gallery Images
if($request->hasFile('file'))
{
$request->validate([
'file.*' => 'image'
]);
$files = $request->file('file');
// Generate images
foreach($files as $file) {
// Generate random number for filename
$rand = $property->id . '-' . rand(99,999) * rand(99,999);
// Create Full size image
ImageIvn::make($file)->resize(1200,900)->save(public_path('img/' . $rand . '_full.jpg'));
// Save in DB
$image = new Image([
'property_id' => $property->id,
'image_path' => 'img/' . $rand . '_full.jpg',
'size' => 'full',
]);
$image->save();
// Create thumbnail size image
ImageIvn::make($file)->resize(1200,900)->save(public_path('img/' . $rand . '_thumb.jpg'));
// Store in DB
$image = new Image([
'property_id' => $property->id,
'image_path' => 'img/' . $rand . '_thumb.jpg',
'size' => 'thumb',
]);
$image->save();
}
}
return $this->index()->with(["message" => "Property " . $property->name . " is added"]);
}

Resources