0

This is the code I'm using to try creating a single page app. I'm reading about vue router and I've tried to apply it to my project. I'm not sure what's wrong with the code, but I'm not able to see the page where the app run. I get some errors on chrom console, but this happen after I've added vue router to my project. Any help to fix it?

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
    <link href="https://fonts.googleapis.com/css2?family=Courgette&display=swap" rel="stylesheet">


    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>

    <title>C safe!</title>
  </head>
  <body>

    <style>
    h1{
      font-family: 'Courgette', cursive;
    }
    .list-group {
      max-height: 300px;
      margin-bottom: 10px;
      overflow:scroll;
      -webkit-overflow-scrolling: touch;
    }
    #map {
      height: 400px;
    }
    </style>
    <div class="container-fluid bg-light p-0" id="app">

      <div class="row">
        <div class="col-sm-12 col-md-12 col-lg-12 p-5">
        <ul class="nav">
          <router-link to="/">
            <li class="nav-item">
              <a class="nav-link" href="#">Home</a>
            </li>
          </router-link>
        </ul>
        </div>
      </div>

    <script type="text/x-template" id="search-bar">
      <div class="row justify-content-center m-0" style="height:100vh;">
        <div class="col-sm-12 col-md-8 col-lg-8 p-5 mx-auto">
          <h1 class="mt-5 mb-3 text-center">C</h1>
          <div class="input-group">
              <input type="email" class="form-control rounded-0" v-on:change="instantSearch()" v-model="q" placeholder="Cerca nelle vicinanze es: Pizzeria">
            <div class="input-group-append">
              <button class="btn btn-outline-success rounded-0" v-on:click="instantSearch()">Cerca</button>
            </div>
          </div>
          <ul v-if="resultSet" class="list-group rounded-0">
            <small v-if="q" class="list-group-item rounded-0"><a class="text-muted" href="#" v-on:click="resetSearch()">Annulla</a></small>
            <router-link to="/results">
              <a v-for="result in resultSet" :key="result.id" v-on:click="expandMap(result.lat, result.lon)" href="#" class="list-group-item rounded-0" :place="result.display_name">{{ result.display_name }}</a>
            </router-link>
          </ul>
          <ul class="nav">
            <li class="nav-item">
              <a class="nav-link" href="#">FAQ</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Aggiungi la tua attività</a>
            </li>
          </ul>
        </div>
      </div>
    </script>

    <script type="text/x-template" id="result-detail">
      <div class="collapse" id="map-collapsable">
        <div class="row m-0 results-map">
          <div class="col-sm-12 col-md-8 col-lg-8 p-0" id="map"></div>
          <div v-for="place in placeData" class="col-sm-12 col-md-4 col-lg-4">
          {{ place.lat }} {{ place.lng }}
          </div>
        </div>
      </div>
    </script>

    <router-view></router-view>
    </div>
<!-- TODO: Vue components -->
   
    <script src="app.js"></script>
  </body>
</html>

This is the JS vue code

window.onload = function(){
  Vue.component('search-bar',{
    template: '#search-bar',
    data(){
      return {}
    }
  });

  Vue.component('view-result',{
    template: '#result-detail',
    data(){
      return {}
    }
  });

  const searchbar = {template: 'search-bar'}
  const result = {template: 'view-result'}

  const routes = [
    {path: '/', component: searchbar},
    {path: '/results', component: result}
  ];

  const router = new VueRouter({
    routes // short for `routes: routes`
  });

  const web = new Vue({
    router,
    el: '#app',
    data: {
      pageData: [],
      resultSet: [],
      placeData: [],
      q: '',
      name: '',
    },
    mounted: function(){
      //this.initPage();
    },
    ready: function(){

    },
    computed: {

    },
    watch: {
      q: function(val, oldVal){
        this.instantSearch();
      }
    },
    methods: {
      instantSearch: function(){
        axios.get('https://nominatim.openstreetmap.org/search?q='+this.q+'&format=json').then( (places) => {
          console.log(places);
          this.resultSet = [];
          places.data.forEach( (item, i) => {
            this.resultSet.push(item);
          });
        });
      },
      resetSearch: function(){
        this.resultSet = [];
        this.q = '';
      },
      expandMap: function(lat, lng){
        let collapsable = document.getElementById('map-collapsable');
        console.log(lat, lng);
        //$(collapsable).collapse();
        this.placeData.push({lat: lat, lng: lng});
      },
      initMap: function(data){

      }
    }
  }).$mount('#app');

}

The errors I get are the following:

vue.js:634 [Vue warn]: Property or method "result" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

(found in <Root>)
warn @ vue.js:634
warnNonPresent @ vue.js:2047
has @ vue.js:2092
eval @ VM258:3
Vue._render @ vue.js:3551
updateComponent @ vue.js:4067
get @ vue.js:4478
Watcher @ vue.js:4467
mountComponent @ vue.js:4074
Vue.$mount @ vue.js:9044
Vue.$mount @ vue.js:11944
Vue._init @ vue.js:5012
Vue @ vue.js:5078
window.onload @ app.js:28
load (async)
(anonymous) @ app.js:1
vue.js:634 [Vue warn]: Error in render: "TypeError: Cannot read property 'display_name' of undefined"

(found in <Root>)
warn @ vue.js:634
logError @ vue.js:1893
globalHandleError @ vue.js:1888
handleError @ vue.js:1848
Vue._render @ vue.js:3553
updateComponent @ vue.js:4067
get @ vue.js:4478
Watcher @ vue.js:4467
mountComponent @ vue.js:4074
Vue.$mount @ vue.js:9044
Vue.$mount @ vue.js:11944
Vue._init @ vue.js:5012
Vue @ vue.js:5078
window.onload @ app.js:28
load (async)
(anonymous) @ app.js:1
vue.js:1897 TypeError: Cannot read property 'display_name' of undefined
    at Proxy.eval (eval at createFunction (vue.js:11649), <anonymous>:3:1557)
    at Vue._render (vue.js:3551)
    at Vue.updateComponent (vue.js:4067)
    at Watcher.get (vue.js:4478)
    at new Watcher (vue.js:4467)
    at mountComponent (vue.js:4074)
    at Vue.$mount (vue.js:9044)
    at Vue.$mount (vue.js:11944)
    at Vue._init (vue.js:5012)
    at new Vue (vue.js:5078)
logError @ vue.js:1897
globalHandleError @ vue.js:1888
handleError @ vue.js:1848
Vue._render @ vue.js:3553
updateComponent @ vue.js:4067
get @ vue.js:4478
Watcher @ vue.js:4467
mountComponent @ vue.js:4074
Vue.$mount @ vue.js:9044
Vue.$mount @ vue.js:11944
Vue._init @ vue.js:5012
Vue @ vue.js:5078
window.onload @ app.js:28
load (async)
(anonymous) @ app.js:1
vue.js:634 [Vue warn]: Cannot find element: #app
warn @ vue.js:634
query @ vue.js:5666
Vue.$mount @ vue.js:11882
window.onload @ app.js:83
load (async)
(anonymous) @ app.js:1
vue.js:634 [Vue warn]: Property or method "result" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property. See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.

(found in <Root>)
warn @ vue.js:634
warnNonPresent @ vue.js:2047
has @ vue.js:2092
eval @ VM258:3
Vue._render @ vue.js:3551
updateComponent @ vue.js:4067
get @ vue.js:4478
Watcher @ vue.js:4467
mountComponent @ vue.js:4074
Vue.$mount @ vue.js:9044
Vue.$mount @ vue.js:11944
window.onload @ app.js:83
load (async)
(anonymous) @ app.js:1
vue.js:634 [Vue warn]: Error in render: "TypeError: Cannot read property 'display_name' of undefined"

(found in <Root>)
warn @ vue.js:634
logError @ vue.js:1893
globalHandleError @ vue.js:1888
handleError @ vue.js:1848
Vue._render @ vue.js:3553
updateComponent @ vue.js:4067
get @ vue.js:4478
Watcher @ vue.js:4467
mountComponent @ vue.js:4074
Vue.$mount @ vue.js:9044
Vue.$mount @ vue.js:11944
window.onload @ app.js:83
load (async)
(anonymous) @ app.js:1
vue.js:1897 TypeError: Cannot read property 'display_name' of undefined
    at Proxy.eval (eval at createFunction (vue.js:11649), <anonymous>:3:1557)
    at Vue._render (vue.js:3551)
    at Vue.updateComponent (vue.js:4067)
    at Watcher.get (vue.js:4478)
    at new Watcher (vue.js:4467)
    at mountComponent (vue.js:4074)
    at Vue.$mount (vue.js:9044)
    at Vue.$mount (vue.js:11944)
    at window.onload (app.js:83)
logError @ vue.js:1897
globalHandleError @ vue.js:1888
handleError @ vue.js:1848
Vue._render @ vue.js:3553
updateComponent @ vue.js:4067
get @ vue.js:4478
Watcher @ vue.js:4467
mountComponent @ vue.js:4074
Vue.$mount @ vue.js:9044
Vue.$mount @ vue.js:11944
window.onload @ app.js:83
load (async)
(anonymous) @ app.js:1

I don't know why but before I create the components and try to use the vue-router, all was working fine.

1 Answer 1

2

There are a few problems:

  1. The script type=text/x-templates need to be moved outside of the app div. Otherwise, the template would be processed as part of the app's template, resulting in the undefined errors you observed in the console.

  2. Since your Vue declaration already specifies el: #app, you need to remove .$mount('#app').

  3. The route paths incorrectly specify a literal string template in: { template: 'search-bar' } and { template: 'result-detail' }. That should be { template: '<search-bar />' } and { template: '<result-detail />' } respectively.

  4. The q and resultSet data props should be moved into the search-bar component. Similarly, the placeData data prop should be moved into the result-detail component.

Demo:

window.onload = function(){
  Vue.component('search-bar',{
    template: '#search-bar',
    data(){
      return {
        resultSet: [],
        q: '',
      }
    }
  });

  Vue.component('view-result',{
    template: '#result-detail',
    data(){
      return {
        placeData: [],
      }
    }
  });

  const searchbar = {template: '<search-bar />'}
  const result = {template: '<view-result />'}

  const routes = [
    {path: '/', component: searchbar},
    {path: '/results', component: result}
  ];

  const router = new VueRouter({
    routes // short for `routes: routes`
  });

  const web = new Vue({
    router,
    el: '#app',
    data: {
      pageData: [],
      name: '',
    },
    mounted: function(){
      //this.initPage();
    },
    ready: function(){

    },
    computed: {

    },
    watch: {
      q: function(val, oldVal){
        this.instantSearch();
      }
    },
    methods: {
      instantSearch: function(){
        axios.get('https://nominatim.openstreetmap.org/search?q='+this.q+'&format=json').then( (places) => {
          console.log(places);
          this.resultSet = [];
          places.data.forEach( (item, i) => {
            this.resultSet.push(item);
          });
        });
      },
      resetSearch: function(){
        this.resultSet = [];
        this.q = '';
      },
      expandMap: function(lat, lng){
        let collapsable = document.getElementById('map-collapsable');
        console.log(lat, lng);
        //$(collapsable).collapse();
        this.placeData.push({lat: lat, lng: lng});
      },
      initMap: function(data){

      }
    }
  })

}
<script src="https://unpkg.com/[email protected]/dist/vue.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/vue-router.js"></script>

<style>
  h1{
    font-family: 'Courgette', cursive;
  }
  .list-group {
    max-height: 300px;
    margin-bottom: 10px;
    overflow:scroll;
    -webkit-overflow-scrolling: touch;
  }
  #map {
    height: 400px;
  }
</style>

<div class="container-fluid bg-light p-0" id="app">

  <div class="row">
    <div class="col-sm-12 col-md-12 col-lg-12 p-5">
      <ul class="nav">
        <router-link to="/">
          <li class="nav-item">
            <a class="nav-link" href="#">Home</a>
          </li>
        </router-link>
      </ul>
    </div>
  </div>

  <router-view></router-view>
</div>

<script type="text/x-template" id="search-bar">
  <div class="row justify-content-center m-0" style="height:100vh;">
    <div class="col-sm-12 col-md-8 col-lg-8 p-5 mx-auto">
      <h1 class="mt-5 mb-3 text-center">C</h1>
      <div class="input-group">
        <input type="email" class="form-control rounded-0" v-on:change="instantSearch()" v-model="q" placeholder="Cerca nelle vicinanze es: Pizzeria">
        <div class="input-group-append">
          <button class="btn btn-outline-success rounded-0" v-on:click="instantSearch()">Cerca</button>
        </div>
      </div>
      <ul v-if="resultSet" class="list-group rounded-0">
        <small v-if="q" class="list-group-item rounded-0"><a class="text-muted" href="#" v-on:click="resetSearch()">Annulla</a></small>
        <router-link to="/results">
          <a v-for="result in resultSet" :key="result.id" v-on:click="expandMap(result.lat, result.lon)" href="#" class="list-group-item rounded-0" :place="result.display_name">{{ result.display_name }}</a>
        </router-link>
      </ul>
      <ul class="nav">
        <li class="nav-item">
          <a class="nav-link" href="#">FAQ</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Aggiungi la tua attività</a>
        </li>
      </ul>
    </div>
  </div>
</script>

<script type="text/x-template" id="result-detail">
  <div class="collapse" id="map-collapsable">
    <div class="row m-0 results-map">
      <div class="col-sm-12 col-md-8 col-lg-8 p-0" id="map"></div>
      <div v-for="place in placeData" class="col-sm-12 col-md-4 col-lg-4">
        {{ place.lat }} {{ place.lng }}
      </div>
    </div>
  </div>
</script>

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

3 Comments

Thanks for the help. I'm new to vue so I make a lot of mistakes at the moment. From the main instance of vue, is there something I can pass to the components? I need to use the <router-link> function to change the view at click on instant search result
In particular, the details of a search result needs to be passed to the result component, some help will be appreciated
@guggio There are a few ways to handle this. You could implement v-model in search-bar and a public prop in view-result. In app, bind the two together with a local data prop. I recommend asking that as a new question to get more helpful answers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.