0

In my Vue app I have a view ('projects.vue') that gets some .json and which has a child component ('subheader.vue') which imports a search/filter mixin. I had this working but I wanted to split out the search elements from the subheader component to its own component, so subheader would hold only the headings and then import the search component. Despite adapting the props and bindings from the working version, my new three-component setup is throwing an error. Here's the setup:

the searchMixin.js:

export default {
    computed: {
        filteredProjects: function() {
            const searchTerm = this.search.toLowerCase();
            if (!searchTerm) {
                return false;
            }
            return this.projects.filter((project) => {
                return  (project.client.toLowerCase().match(searchTerm)) ||
                            (project.contacts.filter((el) => {
                                return el.name.toLowerCase().match(searchTerm);
                            }).length > 0) ||
                            (project.projectReference.toLowerCase().match(searchTerm));
            });
        }
    }
}

Projects.vue:

<template>

    <div class="container" id="projects">
    <!-- app-subheader is global component, imported & registered in main.js -->
        <app-subheader v-bind:title="title" v-bind:subtitle="subtitle" />
        [ snip irrelevant stuff ] 
    </div>

</template>


<script>

    export default {
        data () {
            return {
                title: "Projects",
                subtitle: "",
                projects: []
            }
        },
        created: function() {
            this.$http.get('https://xyz.firebaseio.com/projects.json')
                .then(function(data){
                    return data.json();
                })
                .then(function(data){
                    var projectsArray = [];
                    for (let key in data) {
                        data[key].id = key;
                        this.projectID = key;
                        projectsArray.push(data[key]);
                    }
                    this.projects = projectsArray;
                })
        },
    } // export default
</script>

subheader.vue:

<template>  
    <div class="subheader"> 

        <div class="headings">
            <h1 v-html="title"></h1>
            <h2 v-html="subtitle"></h2>

            <!-- I want to conditionally include <app-search /> according to an ID on an element in the grandparent (e.g., projects.vue)  -->
            <app-search v-bind:projects="projects" />

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


<script>

    import Search  from './search.vue';

    export default {
        components: {
            'app-search': Search
        },
        props: [ "title", "subtitle", "projects" ],
        data() {
            return {
                search: "",
                projects: []
            }
        },
        created: function() {
            console.log("created; log projects", this.projects);
        },
        methods: {},
        computed: {}
    }
</script>

search.vue:

<template>  
    <div class="search-wrapper">

        <div class="search">
            <input type="text" v-model="search" placeholder="search by client, contact name, description, project, source" />
        </div>

        <div class="search-results-wrapper">
            <h3>search-results:</h3>
            <span class="results-count" v-if="filteredProjects.length == 0">
                no results matching search "{{ search }}":
            </span>
            <span class="results-count" v-if="filteredProjects.length > 0">
                {{ filteredProjects.length }} result<span v-if="filteredProjects.length > 1">s</span> matching search "{{ search }}":
            </span>

            <ul class="search-results" v-bind:class="{ open: filteredProjects.length > 0 }">
                <li v-for="(project, ix) in filteredProjects" :key="ix">
                    {{ ix + 1 }}:
                    <router-link v-bind:to="'/project-detail/' + project.id">
                        {{ project.client }} ({{ project.projectReference }})
                    </router-link>
                </li>
            </ul>
        </div>

    </div><!-- END .search-wrapper -->
</template>


<script>

    import searchMixin  from '../mixins/searchMixin.js';

    export default {
        props: [ "projects" ],
        data() {
            return {
                search: "",
            }
        },
        created: function() {
        },
        mixins: [ searchMixin ],
    }

When the search function is invoked, this error is thrown:

[Vue warn]: Error in render: "TypeError: Cannot read property 'filter' of undefined"
found in --->
<AppSearch> at src/components/search.vue
    <AppSubheader> at src/components/subheader.vue
        <Projects> at src/components/projects.vue
            <App> at src/App.vue

... which seems to suggest the search mixin is not getting 'projects'.

Also, in subheader.vue, I get various errors whether I have 'projects' as a prop or 'projects: []' as a data key, and in once case or another I either get no results from the search function or an error, "Property or method "projects" is not defined on the instance but referenced during render".

Obviously I'm lacking clarity on the docs re; grandparent-parent-child data flow. Any help is greatly appreciated.

Whiskey T.

Whiskey T.
  • 203
  • 5
  • 15
  • You're not passing a `projects` prop to the `sub-header` component – thanksd Feb 19 '18 at 23:14
  • You're also not setting the `projects` array correctly in the `$http.get` callback. Wrong reference to `this`. See https://stackoverflow.com/questions/20279484 – thanksd Feb 19 '18 at 23:15
  • @thanksd Thanks for your help. In subheader.vue, 'props: [ "title", "subtitle", "projects" ] ... isn't that passing in 'projects' as prop? Would you please expand on both these points? (all of this is working in the orig 2-component setup, data and search). Thank you – Whiskey T. Feb 19 '18 at 23:21
  • No that’s defining what the component takes in as property values. You still need to actually pass the value in from the parent component context via ‘:projects=“projects”’ – thanksd Feb 19 '18 at 23:24
  • @thanksd Thx, but I'm not clear on where the binding belongs, if not in subheader.vue? – Whiskey T. Feb 19 '18 at 23:49
  • 1
    On the ‘’ tag in your Projects.vue file – thanksd Feb 20 '18 at 00:27
  • @thanksd oof, precisely the code in my wkng prev vsn. Thanks again – Whiskey T. Feb 20 '18 at 01:40

0 Answers0