1

I'm trying to access nested JSON in an array using Vue for a basic search. Each school is wrapped in a "hit" array, so instead of returning each school's data, it's thinking there's only 1 result of "hit." I'm certain I just need to add hit to each instance of schools, but I'm not sure how. Thank you for any help.

My main app file:

<template>
    <div class="app search">
        <!-- Search header -->
        <header id="searchHeader" class="search--header py-2 py-md-4">
            <div class="container">
                <div class="input-group">
                    <!-- Type filter -->
                    <TypeFilter v-model="type"/>

                    <!-- Location filter -->
                    <!--<LocationFilter />-->

                    <!-- Search box -->
                    <SearchBox v-model="searchTerm"/>

                    <!-- More filters -->
                    <!--<div class="dropdown checkbox-dropdown mx-2">
                        <button class="btn btn-lg btn-white py-3 px-4 dropdown-toggle" type="button" id="dropdownMenuButtonFilters" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">More Filters</button>
                        <div class="dropdown-menu" aria-labelledby="dropdownMenuButtonFilters">
                        </div>
                    </div>-->

                    <!-- Search button -->
                    <button v-on:click="searchSubmit(searchTerm)" class="btn btn-lg btn-white ml-2 px-4 search-submit">Search</button>
                </div>

                <!-- Active filters (hidden for v0) -->
                <!--<div class="search--header--filters mt-3">
                    <span class="badge">Filter</span>
                    <span class="badge">Filter</span>
                    <span class="badge">Filter</span>
                </div>-->
            </div>
        </header>

        <!-- Main results -->
        <div class="container">
            {{ message }}

            <!-- Result count and show/sort -->
            <ResultCount v-model="page" :items="schools.length" :perPage="10"/>

            <!-- Results -->
            <SchoolList :schools="pageOfSchools"/>

            <!-- Pagination -->
            <Pagination v-model="page" :items="schools.length" :perPage="10"/>
        </div>
    </div>
</template>

<script>
    import SchoolList from './SchoolList'
    import ResultCount from './ResultCount'
    import Pagination from './Pagination'
    import SearchBox from './SearchBox'
    import TypeFilter from "./TypeFilter";
    import LocationFilter from "./LocationFilter";
    import getArraySection from '../utilities/get-array-section'
    //import {schools as schoolData} from '../data'

    export default {
        name: 'app',
        components: {SchoolList, ResultCount, Pagination, SearchBox, TypeFilter, LocationFilter},
        data: () => ({
            searchTerm: '',
            type: '',
            //schools: [],
            schools: [
                {
                    "hit": [{
                        "title": "State Peter Pan Institute",
                    }, {
                        "title": "State Flatland University",
                    }, {
                        "title": "State Applewood Halls of Ivy",
                    }]
                }
            ],
            page: 1,
            message: '{}'
        }),
        computed: {
            pageOfSchools: function () {
                return getArraySection(this.schools, this.page, 10)
            }
        },
        watch: {
            /*searchTerm: function () {
                this.filterSchools()
            },
            type: function () {
                this.filterSchools()
            }*/
        },
        methods: {    
            filterSchools: function () {
                const searchTerm = this.searchTerm.toLowerCase()
                const type = this.type
                let result = schoolData

                if (searchTerm) {
                    result = result.filter(school => {
                        return (
                            school.title.toLowerCase().search(searchTerm) >= 0 ||
                            school.location.toLowerCase().search(searchTerm) >= 0
                        )
                    })
                }

                if (type) {
                    result = result.filter(school => school.type.indexOf(type) >= 0)
                }

                this.schools = result
                this.page = 1
            }
        },
        created: function () {
            this.filterSchools()
        }
    }
</script>

My SchoolList component:

<template>
    <Transition name="swap" mode="out-in">
        <div class="school-list" :key="schools">
            <div class="row">
                <div class="col-md-8 search--results">
                    <School class="school row justify-content-between search--results--card my-2 pt-2 pb-3 border-bottom" v-for="school in schools" :item="school" :key="school"/>
                </div>

                <div class="col-md-4 mt-4 pl-md-6 search--results--featured">
                    <FeaturedList />
                </div>
            </div>
        </div>
    </Transition>
</template>

<script>
    import School from './School'
    import FeaturedList from './FeaturedList'

    export default {
        name: 'school-list',
        components: {School, FeaturedList},
        props: ['schools']
    }
</script>

<style scoped>
    .swap-enter-active, .swap-leave-active {
        transition: opacity 0.2s ease-in-out;
    }

    .swap-enter, .swap-leave-active {
        opacity: 0;
    }

    .swap-enter {
        opacity: 1;
    }
    .swap-leave-to {
        opacity: 0;
    }
</style>

My School component:

<template>
    <div>
        <div class="col-9 col-md-auto d-flex align-items-center card-body">
            <img v-bind:src="item.logo" class="logo mr-2 mr-md-4 p-2 bg-white rounded-circle">
            <div>
                <h5 class="mb-0"><a :href="item.url" class="text-dark">{{ item.title }}</a></h5>
                <p class="mb-0">{{ item.location }}</p>
                <TypeLabel class="badge type-label mt-2" v-for="type in item.type" :type="type" :key="type"/>
                <span class="badge badge-yellow mt-2">Featured</span>
            </div>
        </div>
        <div class="col-3 col-md-auto text-right">
            <button type="button" class="btn btn-link d-inline-flex px-2 save"><i></i></button>
            <button type="button" class="btn btn-link d-inline-flex px-2 share"><i></i></button>
            <a data-placement="top" data-toggle="popoverMoreInfo" data-title="Get More Info" data-container="body" data-html="true" href="#" id="login" class="btn btn-outline-light d-none d-md-inline-block ml-3">Get More Info</a>
            <!-- Get More Info Popover -->
            <div id="popover-content" class="d-none more-info">
                <form>
                    <div class="form-group mb-2 has-float-label">
                        <input class="form-control" id="your-name" type="text" placeholder="Your name"
                               aria-label="Your name" aria-describedby="your-name"/>
                        <label for="your-name">Your name</label>
                    </div>

                    <div class="form-group mb-2 has-float-label">
                        <input class="form-control" id="your-email" type="email" placeholder="Your email"
                               aria-label="Your email" aria-describedby="your-email"/>
                        <label for="your-email">Your email</label>
                    </div>

                    <div class="form-group mb-2 has-float-label">
                        <input class="form-control" id="your-phone" type="tel" placeholder="Your phone"
                               aria-label="Your phone" aria-describedby="your-phone"/>
                        <label for="your-phone">Your phone</label>
                    </div>

                    <button type="submit" class="btn btn-primary w-100">Request More Info</button>
                </form>
            </div>

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

<script>
    import TypeLabel from './TypeLabel'

    let parser = document.createElement('a')

    export default {
        name: 'school',
        props: ['item'],
        components: {TypeLabel},
        methods: {
            domainOf: url => ((parser.href = url), parser.hostname.replace(/^www\./, ''))
        }
    }
</script>

1 Answer 1

1

To access of each school's name, you should access it like this:

const hit = this.schools[0].hit;

Then you iterate on the property hit

hit.forEach(function(el) {
  console.log(el.title);
});

To do that, may be you should flatten the property schools in a computed property to make it easier to access later.

To declare the flattened variable, you can declare it in the computed property:

computed: {
 // other properties ...
 flattenedSchools: function () {
      const schools = [];
      if (this.schools && this.schools.length) {
          // up to you to check if hit exist and has values
          this.schools[0].hit.forEach(function(el) {
            schools.push((el.title);
          });
      }
      return schools;
   },
},

To access to flattenedSchools, just use this.flattenedSchools and iterate on it.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your help. Where do I define the constant? Sorry, I'm new to Vue.js.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.