In this article of this series we will implement an important feature of the real-estate app which is to prepare the property form.
Â
Let’s begin by preparing the property form. If you remember from the template we download in previous parts that the property form is a multi-step form constructed using jquery to move forward or backwards through the steps.
Our target here is to make this form UI the same but to handle moving through the steps using Vuejs instead of jquery.
I already done this and modified the form code like below:
resources/js/components/pages/SubmitProperty.vue
<template> <div class="page-head"> <div class="container"> <div class="row"> <div class="page-head-content"> <h1 class="page-title">Submit new property</h1> </div> </div> </div> </div> <!-- property area --> <div class="content-area submit-property" style="background-color: #FCFCFC;"> <div class="container"> <div class="clearfix" > <PropertyForm /> </div> </div> </div> </template> <script> import PropertyForm from "../partials/PropertyForm"; export default { name: "SubmitProperty", components: {PropertyForm} } </script>
Create this new component file:
resources/js/components/partials/PropertyForm.vue
<template> <div class="wizard-container"> <div class="wizard-card ct-wizard-orange" id="wizardProperty"> <form action="#" method="post"> <div class="wizard-header"> <h3> <b>Submit</b> YOUR PROPERTY <br /> <small>Please fill all the fields marked as required</small> </h3> </div> <ul class="nav nav-pills"> <li style="width: 25%;" :class="{'active': shownStep===1}"><a>Step 1 </a></li> <li style="width: 25%;" :class="{'active': shownStep===2}"><a>Step 2 </a></li> <li style="width: 25%;" :class="{'active': shownStep===3}"><a>Step 3 </a></li> <li style="width: 25%;" :class="{'active': shownStep===4}"><a>Finished </a></li> </ul> <div class="tab-content"> <div class="tab-pane" :class="{'active': shownStep===1}" id="step1" v-show="shownStep === 1"> <div class="row p-b-15"> <h4 class="info-text">Let's start with the basic information</h4> <div class="col-sm-4 col-sm-offset-1"> <div class="picture-container"> <div class="picture"> <img src="/template/assets/img/default-property.jpg" class="picture-src" title="select property image" /> <input type="file" id="wizard-picture" name="mainPicture" accept="image/*" /> </div> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Property title <small>(required)</small></label> <input name="title" type="text" class="form-control" placeholder="Super villa ..." /> </div> <div class="form-group"> <label>Property Type: (required)</label> <select name="status" class="form-control"> </select> </div> <div class="row"> <div class="col-md-7"> <div class="form-group"> <label>Property Price / Rent <small>(required)</small></label> <input name="price" type="number" class="form-control" placeholder="3330000" /> </div> </div> <div class="col-md-5"> <label>Rent Per <small>(for rent only)</small></label> <select name="rent_amount_per" class="form-control"> <option>- Rent Per -</option> </select> </div> </div> <div class="form-group"> <label>Phone <small>(empty if you wanna use default phone number)</small></label> <input name="phone" type="text" class="form-control" placeholder="+1 473 843 7436" /> </div> <div class="form-group"> <label>Area <small>(required)</small></label> <input name="area" type="number" class="form-control" placeholder="200 Square Meter" /> </div> </div> </div> </div> <!-- End step 1 --> <div class="tab-pane" :class="{'active': shownStep===2}" id="step2" v-show="shownStep === 2"> <h4 class="info-text">How much your Property is Beautiful ?</h4> <div class="row"> <div class="col-sm-12"> <div class="col-sm-12"> <div class="form-group"><label>Property Description :</label> <textarea name="description" class="form-control"></textarea> </div> </div> </div> <div class="col-sm-12"> <div class="col-sm-3"> <div class="form-group"> <label>Property Country :</label> <SelectPicker title="Select your country" :options="[]" name="country" /> </div> </div> <div class="col-sm-3"> <div class="form-group"> <label>Property State :</label> <SelectPicker title="Select your state" :options="[]" name="state" /> </div> </div> <div class="col-sm-3"> <div class="form-group"> <label>Property City :</label> <SelectPicker title="Select your city" :options="cities" name="city" /> </div> </div> </div> <div class="col-sm-12 padding-top-15"> <div class="col-sm-2"> <div class="form-group"> <label>Beds :</label> <input type="number" name="bedrooms" class="form-control" > </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Baths :</label> <input type="number" name="bathrooms" class="form-control" > </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Rooms :</label> <input type="number" name="rooms" class="form-control" > </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Units :</label> <input type="number" name="units" class="form-control" > </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Floor number :</label> <input type="number" name="floor_number" class="form-control" > </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Garages :</label> <input type="number" name="garages" class="form-control" > </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Year Built:</label> <select name="year_built" class="form-control"> <option>- Year Built -</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Property Finalizing:</label> <select name="property_finalizing" class="form-control"> <option>- Property Finalizing -</option> </select> </div> </div> </div> <br /><br /> <h4 style="margin-left:26px">Features</h4> <div class="col-sm-12 padding-top-15"> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Swimming Pool </label> </div> </div> </div> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> 2 Stories </label> </div> </div> </div> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Emergency Exit </label> </div> </div> </div> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Fire Place </label> </div> </div> </div> </div> <div class="col-sm-12 padding-bottom-15"> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Laundry Room </label> </div> </div> </div> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Jog Path </label> </div> </div> </div> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Ceilings </label> </div> </div> </div> <div class="col-sm-3"> <div class="form-group"> <div class="checkbox"> <label> <input type="checkbox"> Dual Sinks </label> </div> </div> </div> </div> <br /> </div> </div> <!-- End step 2 --> <div class="tab-pane" :class="{'active': shownStep===3}" id="step3" v-show="shownStep === 3"> <h4 class="info-text">Give us some images and video</h4> <div class="row"> <div class="col-sm-6"> <div class="form-group"> <label>Choose Images :</label> <input class="form-control" type="file" name="pictures" multiple accept="image/*" /> <p class="help-block">Select multiple images for your property .</p> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Property video :</label> <input class="form-control" placeholder="http://www.youtube.com" name="youtube_video" type="text" /> </div> </div> </div> </div> <!-- End step 3 --> <div class="tab-pane" :class="{'active': shownStep===4}" id="step4" v-show="shownStep === 4"> <h4 class="info-text">Finished and submit</h4> <div class="row"> <div class="col-sm-12"> <div class=""> <p> <label><strong>Terms and Conditions</strong></label> By accessing or using GARO ESTATE services, such as posting your property advertisement with your personal information on our website you agree to the collection, use and disclosure of your personal information in the legal proper manner </p> <div class="checkbox"> <label> <input type="checkbox" name="accept_terms" value="1" /> <strong>Accept termes and conditions.</strong> </label> </div> </div> </div> </div> </div> <!-- End step 4 --> </div> <div class="wizard-footer"> <div class="pull-right"> <input type='button' class='btn btn-next btn-primary' name='next' value='Next' @click="moveForward" v-show="shownStep >= 1 && shownStep < 4" /> <input type='button' class='btn btn-primary ' name='finish' value="!this.$route.params.id ? 'Finish':'Update'" v-show="shownStep === 4" /> </div> <div class="pull-left"> <input type='button' class='btn btn-previous btn-default' name='previous' value='Previous' @click="moveBackward" v-show="shownStep > 1" /> </div> <div class="clearfix"></div> </div> </form> </div> <!-- End submit form --> </div> </template> <script> import {useSetupFormWizard} from "../../composables/useSetupFormWizard"; import ICheckInput from "./ICheckInput"; import SelectPicker from "./SelectPicker"; export default { name: "PropertyForm", components: {SelectPicker, ICheckInput}, setup() { const {shownStep, moveForward, moveBackward} = useSetupFormWizard(); return { shownStep, moveForward, moveBackward } } } </script>
In extracted the form html into the above component. And using the composition Api setup() function i initialized the form data located in a composable file useSetupFormWizard.js
Create this file resources/js/composables/useSetupFormWizard.js
import {ref} from "vue"; export const useSetupFormWizard = () => { let shownStep = ref(1); const moveForward = function () { if(shownStep.value <= 3) { shownStep.value++; } } const moveBackward = function () { if(shownStep.value >= 2) { shownStep.value--; } } return { shownStep, moveForward, moveBackward } }
The function in this composable control the form movement to move forward or backward using showStep reactive property which hold the current step, moveForward() and moveBackward() functions to move next or previous respectively.
Now run:
npm run watch
Then launch the application and go to the submit property page:
php artisan serve
What’s missing in this form so that the setup is complete is to load the location (Country, State, City), and Features from the backend. For this we will create an api to fetch those details.
Static Data Seeders
For the areas and features we need some predefined data stored in database and later more data can be entered from admin panel. So let’s create a seeder for this.
We will create two seeders for the features and the areas:
php artisan make:seeder FeaturesSeeder php artisan make:seeder AreasSeeder
Now open both seeder files and populate with this code:
database/seeders/FeaturesSeeder.php
<?php namespace Database\Seeders; use App\Models\Feature; use Illuminate\Database\Seeder; class FeaturesSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // list of features $AvailableFeatures = ['Electricity', 'Water', 'Gas', 'Phone', 'Internet', 'Basement', 'Swimming Pool', 'AC']; foreach ($AvailableFeatures as $vailableFeature) { $feature = new Feature(); $feature->title = $vailableFeature; $feature->save(); } } }
database/seeders/AreasSeeder.php
<?php namespace Database\Seeders; use App\Models\City; use App\Models\Country; use App\Models\State; use Illuminate\Database\Seeder; class AreasSeeder extends Seeder { /** * Run the database seeds. * * @return void */ public function run() { // list of areas (country, state, city) $areas = ['USA' => [ 'Hawaii' => [ 'Honolulu' ], 'California' => [ 'Los Angeles', 'Sacramento' ], 'New York' => [ 'New York City', 'Albany' ], 'New Jersey' => [ 'Trenton' ] ], 'France' => [ 'Île-de-France' => [ 'Paris' ], 'Normandy' => [ 'Reims', 'Rouen' ], 'Occitanie' => [ 'Montpellier' ] ], 'UK' => [ 'London' => [ 'City of London' ], 'Scotland' => [ 'Aberdeen', 'Glasgow' ], 'Wales' => [ 'Bangor' ], 'Northern Ireland' => [ 'Armagh' ] ], 'Morocco' => [ 'Casablanca-Settat' => [ 'Casablanca', 'Dar Bouazza' ], 'Fès-Meknès' => [ 'Fez' ], 'Marrakesh-Safi' => [ 'Marrakesh', 'Safi' ] ], 'Egypt' => [ 'Cairo' => [ 'Old Cairo', 'Maadi', 'Zamalek' ], 'Alexandria', 'Sharm El Sheikh', 'Hurghada' ], 'Spain' => [ 'Madrid', 'Barcelona', 'Granada' ], 'Turkey' => [ 'Istanbul' => [ 'Istanbul' ], 'Ankara' => [ 'Ankara' ], 'İzmir' => [ 'İzmir' ], 'Bursa' => [ 'Bursa' ] ] ]; foreach ($areas as $countryName => $states) { $country = Country::create(['name' => $countryName]); foreach ($states as $stateName => $cities) { $state = State::create(['name' => $stateName, 'country_id' => $country->id]); if($cities && is_array($cities)) { foreach ($cities as $cityName) { City::create(['name' => $cityName, 'country_id' => $country->id, 'state_id' => $state->id]); } } } } } }
Call these seeders in DatabaseSeeder.php
database/seeders/DatabaseSeeder.php
<?php namespace Database\Seeders; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { // \App\Models\User::factory(10)->create(); $this->call([ FeaturesSeeder::class, AreasSeeder::class ]); } }
Once i run this command the data will be inserted into db:
php artisan db:seed
Now inspect the database and check the data is inserted into features, country, state, and city tables.
These are just simple data but you can add more data according to your needs.
The next step is to create a simple api that loads this static data. So create a simple controller to fetch those data and we will add them to cache for performance considerations.
Static Data Api
Create new controller app/Http/Controllers/StaticDataController.php
<?php namespace App\Http\Controllers; use App\Models\Country; use App\Models\Feature; use Carbon\Carbon; use Illuminate\Support\Facades\Cache; class StaticDataController extends Controller { public function index() { $data = Cache::remember("app_static_data", Carbon::now()->addWeek(), function () { return [ 'features' => $this->getFeatures(), 'areas' => $this->getAreas() ]; }); return response()->json($data); } protected function getFeatures() { return Feature::all(); } protected function getAreas() { $areas = []; $countries = Country::all(); foreach ($countries as $country) { $arr = [ 'id' => $country->id, 'name' => $country->name, 'states' => [] ]; $states = $country->states; if($states) { foreach ($states as $state) { $arr2 = [ 'id' => $state->id, 'name' => $state->name, 'cities' => $state->cities ]; $arr['states'][] = $arr2; } } $areas[] = $arr; } return $areas; } }
The index() method returns a json response of the static data (features, areas). In this case i cache the results for better performance.
Perhaps this may not be the best approach and some people may load the country, state, and city in separate controllers but for the purpose of this tutorial i am loading them in same api.
Next add this route in routes/api.php:
Route::get('static_data', [App\Http\Controllers\StaticDataController::class, 'index']);
The next challenge is to populate the country, state, city dropdowns and features checkboxes located on the create property form page.
Static Data Composable
To simplify working with static data and because we will be using the same static data in multiple places like the create property form and later the search page, so i will create a shared composable file to just load these static data.
Create resources/js/composables/useLoadStaticData.js
import {onMounted, ref} from "vue"; import {propertyFinalizingList, propertyTypes, rentAmountPerList} from "../api/static_data"; export const useLoadStaticData = ({selectedCountryId = 0, selectedStateId = 0, selectedCityId = 0} = {}) => { const allAreas = ref([]); const countries = ref([]); const states = ref([]); const cities = ref([]); const selectedCountry = ref(selectedCountryId ? selectedCountryId : null); const selectedState = ref(selectedStateId ? selectedStateId : null); const selectedCity = ref(selectedCityId ? selectedCityId : null); const features = ref([]); const loadStaticData = (callback = null) => { window.axios.get("/api/static_data").then(res => { allAreas.value = res.data.areas; countries.value = res.data.areas; features.value = res.data.features; // store in locale storage window.localStorage.setItem("static_data", JSON.stringify({areas: allAreas.value, features: features.value})); if(callback) { callback(allAreas, countries, features); } }); } const loadStates = (country) => { states.value = []; cities.value = []; const area = allAreas.value.find(a => a.id === parseInt(country)); if(area) { states.value = area.states; } } const loadCities = (state, country) => { cities.value = []; const area = allAreas.value.find(a => a.id === country); if(area && area.states.length > 0) { const targetState = area.states.find(s => s.id === parseInt(state)); if(targetState && targetState.cities) { cities.value = targetState.cities; } } } const handleSelectCountry = val => { selectedCountry.value = val; loadStates(selectedCountry.value); } const handleSelectState = val => { selectedState.value = val; loadCities(selectedState.value, selectedCountry.value); } const handleSelectCity = val => { selectedCity.value = val; } const getYears = function () { const currentYear = new Date().getFullYear(); const years = [currentYear]; for(let i=1; i < 20; i++) { years.unshift(currentYear - i); } return years; }; onMounted(() => { loadStaticData(() => { if (selectedCountry.value) { loadStates(selectedCountry.value); if (selectedState.value) { loadCities(selectedState.value, selectedCountry.value); } } }); }); return { propertyFinalizingList, rentAmountPerList, propertyTypes, features, countries, states, cities, handleSelectCountry, handleSelectState, handleSelectCity, selectedCountry, selectedState, selectedCity, getYears, loadStates, loadCities } }
The useLoadStaticData() function returns the necessary data and functions we need to populate the property form like the countries, cities, states, features, property types, etc. I have declared some reactive properties like allAreas, countries, states, cities, features.
The loadStaticData() function makes an http GET request to the api we just created previously to fetch the static data and update the reactive properties allAreas, countries and features. Then we store those data into localStorage.
The composable also contains the loadStates() function to load the states by particular country, loadCities() to load the cities by particular country and state.
Also added other functions as events which fire when selecting country, state, or city using the handleSelectCountry(), handleSelectState(), and handleSelectCity() respecitvely.
The onMounted() hook load the static data on initial page load and update the countries, states, and cities.
And finally i return all this payload as an object to be consumed in PropertyForm.vue.
Create this file resources/js/api/static_data.js
export const propertyTypes = ['Sale', 'Rent']; export const propertyFinalizingList = ['Not Finalized', 'Normal Finalizing', 'Super Finalizing', 'High Class Finalizing']; export const rentAmountPerList = [{ id: 1, name: 'Per Night', }, { id: 2, name: 'Per Week', }, { id: 3, name: 'Per Month', }, { id: 4, name: 'Per Year', }];
Now update PropertyForm.vue:
resources/js/components/partials/PropertyForm.vue
<template> <div class="wizard-container"> <div class="wizard-card ct-wizard-orange" id="wizardProperty"> <form action="#" method="post" @submit.prevent="handleSubmit" novalidate="novalidate"> <div class="wizard-header"> <h3> <b>Submit</b> YOUR PROPERTY <br /> <small>Please fill all the fields marked as required</small> </h3> <div v-if="validationErrors.length" class="alert alert-danger"> <ul> <li v-for="error of validationErrors" :key="error">{{error}}</li> </ul> </div> </div> <ul class="nav nav-pills"> <li style="width: 25%;" :class="{'active': shownStep===1}"><a>Step 1 </a></li> <li style="width: 25%;" :class="{'active': shownStep===2}"><a>Step 2 </a></li> <li style="width: 25%;" :class="{'active': shownStep===3}"><a>Step 3 </a></li> <li style="width: 25%;" :class="{'active': shownStep===4}"><a>Finished </a></li> </ul> <div class="tab-content"> <div class="tab-pane" :class="{'active': shownStep===1}" id="step1" v-show="shownStep === 1"> <div class="row p-b-15"> <h4 class="info-text">Let's start with the basic information</h4> <div class="col-sm-4 col-sm-offset-1"> <div class="picture-container"> <div class="picture"> <img src="/template/assets/img/default-property.jpg" class="picture-src" title="select property image" /> <input type="file" id="wizard-picture" name="mainPicture" accept="image/*" /> </div> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Property title <small>(required)</small></label> <input name="title" type="text" class="form-control" placeholder="Super villa ..." /> </div> <div class="form-group"> <label>Property Type: (required)</label> <select name="status" class="form-control"> <option v-for="type in propertyTypes" v-bind:key="type" :value="type">{{type}} </option> </select> </div> <div class="row"> <div class="col-md-7"> <div class="form-group"> <label>Property Price / Rent <small>(required)</small></label> <input name="price" type="number" class="form-control" placeholder="3330000" /> </div> </div> <div class="col-md-5"> <label>Rent Per <small>(for rent only)</small></label> <select name="rent_amount_per" class="form-control"> <option>- Rent Per -</option> <option v-for="rentPer in rentAmountPerList" :key="rentPer.id" :value="rentPer.id">{{rentPer.name}}</option> </select> </div> </div> <div class="form-group"> <label>Phone <small>(empty if you wanna use default phone number)</small></label> <input name="phone" type="text" class="form-control" placeholder="+1 473 843 7436" /> </div> <div class="form-group"> <label>Area <small>(required)</small></label> <input name="area" type="number" class="form-control" placeholder="200 Square Meter" /> </div> </div> </div> </div> <!-- End step 1 --> <div class="tab-pane" :class="{'active': shownStep===2}" id="step2" v-show="shownStep === 2"> <h4 class="info-text">How much your Property is Beautiful ?</h4> <div class="row"> <div class="col-sm-12"> <div class="col-sm-12"> <div class="form-group"><label>Property Description :</label> <textarea name="description" class="form-control"></textarea> </div> </div> </div> <div class="col-sm-12"> <div class="col-sm-3"> <div class="form-group"> <label>Property Country :</label> <SelectPicker title="Select your country" :options="countries" name="country" /> </div> </div> <div class="col-sm-3"> <div class="form-group"> <label>Property State :</label> <SelectPicker title="Select your state" :options="states" name="state" /> </div> </div> <div class="col-sm-3"> <div class="form-group"> <label>Property City :</label> <SelectPicker title="Select your city" :options="cities" name="city" /> </div> </div> </div> <div class="col-sm-12 padding-top-15"> <div class="col-sm-2"> <div class="form-group"> <label>Beds :</label> <input type="number" name="bedrooms" class="form-control" > </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Baths :</label> <input type="number" name="bathrooms" class="form-control" /> </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Rooms :</label> <input type="number" name="rooms" class="form-control" /> </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Units :</label> <input type="number" name="units" class="form-control" /> </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Floor number :</label> <input type="number" name="floor_number" class="form-control" /> </div> </div> <div class="col-sm-2"> <div class="form-group"> <label>Garages :</label> <input type="number" name="garages" class="form-control" /> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Year Built:</label> <select name="year_built" class="form-control"> <option value="0">- Year Built -</option> <option v-for="year in getYears()" :key="year" :value="year">{{year}}</option> </select> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Property Finalizing:</label> <select name="property_finalizing" class="form-control"> <option>- Property Finalizing -</option> <option v-for="finalizing in propertyFinalizingList" :key="finalizing" :value="finalizing">{{finalizing}}</option> </select> </div> </div> </div> <br /><br /> <h4 style="margin-left:26px">Features</h4> <div class="col-sm-12 padding-top-15 padding-bottom-15"> <div class="col-sm-3" v-for="feature in features" v-bind:key="feature.id"> <div class="form-group"> <ICheckInput type="checkbox" :name="'feature'+feature.id" :value="feature.id" :text="feature.title" /> </div> </div> </div> <br /> </div> </div> <!-- End step 2 --> <div class="tab-pane" :class="{'active': shownStep===3}" id="step3" v-show="shownStep === 3"> <h4 class="info-text">Give us some images and video</h4> <div class="row"> <div class="col-sm-6"> <div class="form-group"> <label>Choose Images :</label> <input class="form-control" type="file" name="pictures" multiple accept="image/*" /> <p class="help-block">Select multiple images for your property .</p> </div> </div> <div class="col-sm-6"> <div class="form-group"> <label>Property video :</label> <input class="form-control" placeholder="http://www.youtube.com" name="youtube_video" type="text" /> </div> </div> </div> </div> <!-- End step 3 --> <div class="tab-pane" :class="{'active': shownStep===4}" id="step4" v-show="shownStep === 4"> <h4 class="info-text">Finished and submit</h4> <div class="row"> <div class="col-sm-12"> <div class=""> <p> <label><strong>Terms and Conditions</strong></label> By accessing or using GARO ESTATE services, such as posting your property advertisement with your personal information on our website you agree to the collection, use and disclosure of your personal information in the legal proper manner </p> <ICheckInput type="checkbox" name="accept_terms" value="1" text="Accept terms and conditions." /> </div> </div> </div> </div> <!-- End step 4 --> </div> <div class="wizard-footer"> <div class="pull-right"> <input type='button' class='btn btn-next btn-primary' name='next' value='Next' @click="moveForward" v-show="shownStep >= 1 && shownStep < 4" /> <input type='submit' class='btn btn-primary ' name='finish' :value="!this.$route.params.id ? 'Finish' : 'Update'" v-show="shownStep === 4" /> </div> <div class="pull-left"> <input type='button' class='btn btn-previous btn-default' name='previous' value='Previous' @click="moveBackward" v-show="shownStep > 1" /> </div> <div class="clearfix"></div> </div> </form> </div> <!-- End submit form --> </div> </template> <script> import ICheckInput from "./ICheckInput"; import SelectPicker from "./SelectPicker"; import {useSetupFormWizard} from "../../composables/useSetupFormWizard"; import {useLoadStaticData} from "../../composables/useLoadStaticData"; export default { name: "PropertyForm", components: {SelectPicker, ICheckInput}, setup(props) { const {getYears, rentAmountPerList, propertyFinalizingList, propertyTypes, features, countries, states, cities, handleSelectCountry, handleSelectState, handleSelectCity, selectedCountry, selectedState, selectedCity, loadStates, loadCities} = useLoadStaticData(); const {shownStep, moveForward, moveBackward} = useSetupFormWizard(); return { getYears, shownStep, moveForward, moveBackward, propertyTypes, rentAmountPerList, propertyFinalizingList, features, countries, states, cities } } } </script> <style scoped> .btn[disabled] { background-color: #ccc; color: #000; } .pictures li { display: inline; } </style>
Fine now all form setup is now complete. Check and test this in the browser and see the full property is loaded correctly.
Continue to part 7: Submitting Properties>>>