Backend DevelopmentFrontend DevelopmentVueJs Tutorials

Building a Simple Real estate App With Laravel and Vuejs 3 Part9

Building a Simple Real estate App With Laravel and Vuejs 3

In this article of the series “building simple realestate app” we will implement a core feature in the realestate app which is to search the properties.

 

 

The search functionality is an important feature in any website. It provides the user with the appropriate data he looking for using specific search criteria.

Preparing The Search Page

At first i will prepare the search page UI. I have extracted the component html from the template html that i downloaded in previous parts.

resources/js/components/pages/Properties.vue

<template>
    <div class="page-head">
        <div class="container">
            <div class="row">
                <div class="page-head-content">
                    <h1 class="page-title">Search Properties</h1>
                </div>
            </div>
        </div>
    </div>

    <div class="properties-area recent-property" style="background-color: #FFF;">
        <div class="container">
            <div class="row">

                <SearchSidebar />

                <div class="col-md-9  pr0 padding-top-40 properties-page">
                    <div class="col-md-12 clear">
                        <div class="col-xs-10 page-subheader sorting pl0">
                            <ul class="sort-by-list">
                                <li class="active">
                                    <a href="javascript:void(0);" class="order_by_date" data-orderby="property_date" data-order="ASC">
                                        Property Date <i class="fa fa-sort-amount-asc"></i>
                                    </a>
                                </li>
                                <li class="">
                                    <a href="javascript:void(0);" class="order_by_price" data-orderby="property_price" data-order="DESC">
                                        Property Price <i class="fa fa-sort-numeric-desc"></i>
                                    </a>
                                </li>
                            </ul><!--/ .sort-by-list-->

                            <div class="items-per-page">
                                <label for="items_per_page"><b>Property per page :</b></label>
                                <div class="sel">
                                    <select id="items_per_page" name="per_page">
                                        <option value="3">3</option>
                                        <option value="6">6</option>
                                        <option value="9">9</option>
                                        <option selected="selected" value="12">12</option>
                                        <option value="15">15</option>
                                        <option value="30">30</option>
                                        <option value="45">45</option>
                                        <option value="60">60</option>
                                    </select>
                                </div><!--/ .sel-->
                            </div><!--/ .items-per-page-->
                        </div>

                        <div class="col-xs-2 layout-switcher">
                            <a class="layout-list" href="javascript:void(0);"> <i class="fa fa-th-list"></i>  </a>
                            <a class="layout-grid active" href="javascript:void(0);"> <i class="fa fa-th"></i> </a>
                        </div><!--/ .layout-switcher-->
                    </div>

                    <div class="col-md-12 clear">
                        <div id="list-type" class="proerty-th">
                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-3.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>


                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-2.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-1.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-3.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-1.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>

                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-2.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-3.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-2.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <div class="col-sm-6 col-md-4 p0">
                                <div class="box-two proerty-item">
                                    <div class="item-thumb">
                                        <a href="property-1.html" ><img src="assets/img/demo/property-1.jpg"></a>
                                    </div>

                                    <div class="item-entry overflow">
                                        <h5><a href="property-1.html"> Super nice villa </a></h5>
                                        <div class="dot-hr"></div>
                                        <span class="pull-left"><b> Area :</b> 120m </span>
                                        <span class="proerty-price pull-right"> $ 300,000</span>
                                        <p style="display: none;">Suspendisse ultricies Suspendisse ultricies Nulla quis dapibus nisl. Suspendisse ultricies commodo arcu nec pretium ...</p>
                                        <div class="property-icon">
                                            <img src="assets/img/icon/bed.png">(5)|
                                            <img src="assets/img/icon/shawer.png">(2)|
                                            <img src="assets/img/icon/cars.png">(1)
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    <div class="col-md-12">
                        <div class="pull-right">
                            <div class="pagination">
                                <ul>
                                    <li><a href="#">Prev</a></li>
                                    <li><a href="#">1</a></li>
                                    <li><a href="#">2</a></li>
                                    <li><a href="#">3</a></li>
                                    <li><a href="#">4</a></li>
                                    <li><a href="#">Next</a></li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
import SearchSidebar from "../partials/SearchSidebar";
export default {
    name: "Properties",
    components: {SearchSidebar}
}
</script>

<style scoped>

</style>

As you see i have added the necessary code and also extracted the search filters to a separate partial component <SearchSidebar />.

resources/js/components/partials/SearchSidebar.vue

<template>
    <div class="col-md-3 p0 padding-top-40">
        <div class="blog-asside-right pr0">
            <div class="panel panel-default sidebar-menu wow fadeInRight animated" >
                <div class="panel-heading">
                    <h3 class="panel-title">Smart search</h3>
                </div>
                <div class="panel-body search-widget">
                    <form action="#" class=" form-inline">
                        <fieldset>
                            <div class="row">
                                <div class="col-xs-12">
                                    <input type="text" class="form-control" name="keyword" placeholder="Key word" />
                                </div>
                            </div>
                        </fieldset>

                        <fieldset>
                            <div class="row">
                                <div class="col-xs-12">

                                  <SelectPicker title="Select your country" :options="countries" name="country" @onChange="onCountryChange" />

                                </div>
                                <div class="col-xs-12">

                                  <SelectPicker title="Select your State" :options="states" name="state" @onChange="onStateChange" />

                                </div>
                                <div class="col-xs-12">

                                  <SelectPicker title="Select your City" :options="cities" name="city" @onChange="onCityChange" />

                                </div>

                                <div class="col-xs-12" style="margin-top: 7px">

                                    <select name="status" class=" show-tick form-control">
                                        <option value=""> -Type- </option>
                                        <option v-for="type in propertyTypes" v-bind:key="type" :value="type">{{type}} </option>
                                    </select>
                                </div>
                            </div>
                        </fieldset>

                        <fieldset class="padding-5">
                            <div class="row">
                                <div class="col-xs-12">
                                    <NumberRange label="Price range ($)" id="price-range" :min="0" :max="100000000" :step="1" />
                                </div>
                                <div class="col-xs-12">
                                    <NumberRange label="Rent range ($)" id="rent-range" :min="0" :max="100000000" :step="1" />

                                </div>
                                <div class="col-xs-12" v-show="status === 'Rent'">
                                    <select name="rent_amount_per" class="form-control">
                                        <option value="">- Rent Per -</option>
                                        <option v-for="rPer in rentAmountPerList" :key="rPer.id" :value="rPer.id">{{rPer.name}}</option>
                                    </select>
                                </div>
                            </div>
                        </fieldset>

                        <fieldset class="padding-5">
                            <div class="row">
                                <div class="col-xs-12">
                                    <NumberRange label="Min baths" id="min-baths" :min="0" :max="50" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min bed" id="min-bed" :min="0" :max="50" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min area" id="min-area" :min="0" :max="500" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min rooms" id="min-rooms" :min="0" :max="50" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min garages" id="min-garages" :min="0" :max="10" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min units" id="min-units" :min="0" :max="1000" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min floors" id="min-floor_number" :min="0" :max="100" :step="1" />
                                </div>

                                <div class="col-xs-12">
                                    <NumberRange label="Min year built" id="min-year_built" :min="0" :max="(new Date()).getFullYear()" :step="1" />
                                </div>

                                <div class="col-xs-12" style="margin-top: 7px">

                                    <select name="property_finalizing" class="form-control">
                                        <option value="">- Property Finalizing -</option>
                                        <option v-for="finalizing in propertyFinalizingList" :key="finalizing" :value="finalizing">{{finalizing}}</option>
                                    </select>
                                </div>
                            </div>
                        </fieldset>

                        <fieldset class="padding-5">
                            <div class="row">
                                <div class="col-xs-12" v-for="feature in features" v-bind:key="feature.id">
                                    <ICheckInput type="checkbox" :name="'feature'+feature.id" :value="feature.id" :text="feature.title" />
                                </div>

                            </div>
                        </fieldset>

                        <fieldset>
                            <div class="row">
                                <div class="col-xs-12">
                                    <input class="button btn largesearch-btn" value="Search" type="submit">
                                </div>
                            </div>
                        </fieldset>

                    </form>
                </div>
            </div>

        </div>
    </div>
</template>

<script>

import ICheckInput from "./ICheckInput";
import NumberRange from "./NumberRange";
import SelectPicker from "./SelectPicker";

import {useLoadStaticData} from "../../composables/useLoadStaticData";

export default {
    name: "SearchSidebar",
    components: {SelectPicker, NumberRange, ICheckInput},
    setup() {

      const {rentAmountPerList, propertyFinalizingList, propertyTypes,
        features, countries, states, cities, handleSelectCountry, handleSelectState,
        handleSelectCity, selectedCountry, selectedState, selectedCity} = useLoadStaticData();

      const onCountryChange = e => {
        handleSelectCountry(e);

  
      }

      const onStateChange = e => {
        handleSelectState(e);
        
      }

      const onCityChange = e => {
        handleSelectCity(e);
        
      }

        const {submitSearch} = useSubmitSearch();

        return {
            propertyFinalizingList,
            rentAmountPerList,
            propertyTypes,
            features,
            countries,
            states,
            cities,
            onCountryChange,
            onStateChange,
            onCityChange
        }
    }
}
</script>

In this component to prepare and initialize the search fields i used the same composable file useLoadStaticData.js we used to before in the create property form to initialize the countries, states, cities, features, etc. Also i included the custom components we created before like <SelectPicker />, <NumberRange />, <ICheckInput />. We will add more code this component below.

We will move to handling the form storage and submission using Vuex.

 

Preparing Vuex Store:

Go to resources/js/store and create search.js file inside:

resources/js/store/search.js

import {
    SET_SEARCH_CITY,
    SET_SEARCH_COUNTRY,
    SET_SEARCH_KEYWORD,
    SET_SEARCH_FEATURE,
    SET_SEARCH_PROPERTY_FINALIZING,
    SET_SEARCH_RANGE,
    SET_SEARCH_RENT_PER,
    SET_SEARCH_STATE,
    SET_SEARCH_STATUS,
    REMOVE_SEARCH_FEATURE,
    SET_SEARCH_FEATURES,
    SET_SEARCH_PROPERTIES,
    SET_LOADING,
    SET_SORTING_PARAMS,
    SET_PAGINATE_PARAMS,
    SET_LAYOUT
} from "./types";

export default {
    namespaced: true,
    state: () => ({
        search_params: {
            keyword: "",
            country: "",
            state: "",
            city: "",
            status: "",
            rent_amount_per: "",
            range_fields: {
                price: {min: 0, max: 0},
                rent: {min: 0, max: 0},
                bathrooms: {min: 0, max: 0},
                bedrooms: {min: 0, max: 0},
                area: {min: 0, max: 0},
                rooms: {min: 0, max: 0},
                garages: {min: 0, max: 0},
                units: {min: 0, max: 0},
                floor_number: {min: 0, max: 0},
                year_built: {min: 0, max: 0}
            },
            property_finalizing: "",
            features: []
        },
        sorting_params: {
            sortBy: 'updated_at',
            sortOrder: 'DESC'
        },
        paginate_params: {
            page: 1,
            per_page: 30
        },
        defaultLayout: 'grid',
        properties: null,
        loading: true
    }),
    mutations: {
        [SET_SEARCH_KEYWORD](state, data) {
            state.search_params.keyword = data;
        },
        [SET_SEARCH_COUNTRY](state, data) {
            state.search_params.country = data;
        },
        [SET_SEARCH_STATE](state, data) {
            state.search_params.state = data;
        },
        [SET_SEARCH_CITY](state, data) {
            state.search_params.city = data;
        },
        [SET_SEARCH_STATUS](state, data) {
            state.search_params.status = data;
        },
        [SET_SEARCH_RENT_PER](state, data) {
            state.search_params.rent_amount_per = data;
        },
        [SET_SEARCH_PROPERTY_FINALIZING](state, data) {
            state.search_params.property_finalizing = data;
        },
        [SET_SEARCH_RANGE](state, data) {
            state.search_params.range_fields[data.field] = data.value;
        },
        [SET_SEARCH_FEATURES](state, data) {
            state.search_params.features = data;
        },
        [SET_SEARCH_FEATURE](state, data) {
            if(state.search_params.features.find(i => i === parseInt(data)) === undefined) {
                state.search_params.features = [...state.search_params.features, parseInt(data)];
            }
        },
        [REMOVE_SEARCH_FEATURE](state, data) {
            if(state.search_params.features.find(i => i === parseInt(data)) !== undefined) {
                state.search_params.features = state.search_params.features.filter(i => i !== parseInt(data));
            }
        },
        [SET_SEARCH_PROPERTIES](state, data) {
            state.properties = data;
        },
        [SET_LOADING](state, data) {
            state.loading = data;
        },
        [SET_SORTING_PARAMS](state, data) {
            state.sorting_params = data;
        },
        [SET_PAGINATE_PARAMS](state, data) {
            state.paginate_params = data;
        },
        [SET_LAYOUT](state, data) {
            state.defaultLayout = data;
        }
    },
    getters: {
      range(state) {
          return (field) => {
              return [{...state.search_params.range_fields[field]}.min, {...state.search_params.range_fields[field]}.max];
          }
      }
    },
    actions: {
        async search({commit}, params) {

           // call the search api
        
        },
        bulkStoreUpdate({commit, state}, queryParams) {
            
        },
        resetPaginateSortParams({commit}) {
            
        },
        setSearchDefaults({commit, dispatch}) {
           
        }
    }
}

In the above file i already prepared the store state parameters from the search form. The state function composed of some main objects. The search_params object contains all the search form related parameters.

The sorting_params contains the sorting parameters when manipulating sort later. The paginate_params contains the pagination parameters like page. The defaultLayout represent the default layout of the search results whether “grid” or “list”.

The properties parameter holds the searched properties after performing search request and the loading key contains a boolean indicating start fetch and fetch complete.

In the mutations object i added all the mutations we need to update the state, so you have to add these types in types.js

resources/js/store/types.js

export const SET_SIGNED_IN = 'SET_SIGNED_IN';
export const SET_USER = 'SET_USER';

export const SET_SEARCH_KEYWORD = 'SET_SEARCH_KEYWORD';
export const SET_SEARCH_COUNTRY = 'SET_SEARCH_COUNTRY';
export const SET_SEARCH_STATE = 'SET_SEARCH_STATE';
export const SET_SEARCH_CITY = 'SET_SEARCH_CITY';
export const SET_SEARCH_STATUS = 'SET_SEARCH_STATUS';
export const SET_SEARCH_RENT_PER = 'SET_SEARCH_RENT_PER';
export const SET_SEARCH_RANGE = 'SET_SEARCH_RANGE';
export const SET_SEARCH_PROPERTY_FINALIZING = 'SET_SEARCH_PROPERTY_FINALIZING';
export const SET_SEARCH_FEATURE = 'SET_SEARCH_FEATURE';
export const SET_SEARCH_FEATURES = 'SET_SEARCH_FEATURES';
export const REMOVE_SEARCH_FEATURE = 'REMOVE_SEARCH_FEATURE';
export const SET_SEARCH_PROPERTIES = 'SET_SEARCH_PROPERTIES';
export const SET_SORTING_PARAMS = 'SET_SORTING_PARAMS';
export const SET_PAGINATE_PARAMS = 'SET_PAGINATE_PARAMS';

export const SET_LOADING = 'SET_LOADING';
export const SET_LAYOUT = 'SET_LAYOUT';

Also in getters object i added one getter “range” which returns the range value as an array for specific range field type like price, rent, beds, etc.

And finally in the actions object i added some actions like search, bulkStoreUpdate we will update them later.

Continue to part 10: Processing Search>>>

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments