简体   繁体   中英

Accessing nested JSON with Vue

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>

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM