A煤n no tienes acceso a esta clase

Crea una cuenta y contin煤a viendo este curso

Comunicacion entre Componentes Genericos - Event Bus y Plugins

30/53
Recursos

Aportes 42

Preguntas 7

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesi贸n.

Estoy de acuerdo que el uso de Vuex soluciona esto mejor centralizando la data, pero ese es un tema para ver en futuros videos, por el momento si me gustar铆a resaltar que no veo la necesidad en este caso de hacer uso de un plugin (no estoy claro si en el momento de la grabaci贸n del mismo si lo era, o si quer铆an dar un ejemplo de plugins), ya que existe el elemento $root:

Player.vue

this.$root.$on("set-track", (track) => {
	this.track = track;
});

Track.vue

this.$root.$emit("set-track", this.track);

Vale, este cap铆tulo ya es dif铆cil de seguir en Vue 3, se puede hacer la comunicaci贸n entre hermanos pero es de otra manera, en Vue 3, en el m茅todo install del plugin ya NO te pasan el constructor de Vue, yo lo que hice fue hacer un downgrade de mi proyecto a la versi贸n 2 de Vue

En Vue 3 ya no existe el m茅todo $on, en su lugar sugieren usar una libreria llamada 鈥渕itt鈥 para emitir los eventos y cacharlos

Event Bus y Plugins permite la comunicacion entre componentes que no tienes relacion de padre/hijo sino q son componentes llamos desde distintos puntos de la aplicacion.

Hola.
Estoy necesitando graficar con highchart y Vue. Los datos para graficar vienen por api.
Estoy usando Axios para graficar pero me responde que no reconoce la variable.
驴En que estoy fallando?
Es importante!! gracias!

<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<script src="https://code.highcharts.com/stock/modules/export-data.js"></script>
  <!-- NPM de el highcharts para VUE -->
<script src="https://cdn.jsdelivr.net/npm/highcharts/highcharts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-highcharts/dist/vue-highcharts.min.js"></script>

  <!--
<script src="https://code.highcharts.com/highcharts.js"></script>
-->
<script type="text/javascript" src="https://cdn.rawgit.com/highcharts/highcharts-vue/1ce7e656/dist/script-tag/highcharts-vue.min.js"></script>



<div id="app">
  <div class="title-row">
    <p>Haciendo pruebas de graficos</p>
  </div>

  <highcharts :options="chartOptions"></highcharts>

</div>

<hr>
<p>Probando llegada de datos</p>
{{barra.total}}

<hr>
{{barra.fecha}}
<hr>


<style media="screen">
.title-row {
display: flex;
flex-direction: column;
align-items: center;
background: #eee;
padding: 20px;
}
</style>

<!-- ESTA CARGADA LA VERSION DE VUE PARA PRODUCCION
-->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<script type="text/javascript">


Vue.use(HighchartsVue.default)

var app = new Vue({
  el: '#app',
  data: {
   barra: null,
   mensaje: null
 },
//filters: {
//  currencydecimal (value) {
//    return value.toFixed(2)
//    }
//  },

created() {
  this.pedidoJson();
},
  data() {
    return {
      chartOptions: {
        chart: {
          type: 'spline'
        },
        title: {
          text: 'Vue y Highcharts'
        },
        series: [{
          data: [10, 0, 8, 2, 6, 4, 5, 5]
        }]
      },
    }
  },
  methods: {
    pedidoJson: function(){
      axios.get('https://desa-tablero-asistire.educar.gob.ar/servicios/asist_institucion.php')
        .then(response => (
          //alert("hizo el pedido json")
          //alert(response.data.fecha)
         this.barra = response.data
        ))
        .catch(error => {
          alert("Error del pedido por axios a la api")
          this.errored = true
        })
    }
  }
})



</script>

Hola! tengo un peque帽o problema, me funciona todo super bn, pero! en Chrome no me funciona el reproductor si le doy audio(controls) no veo nada pero en el inspector de elementos me aparece, ahora si le digo audio(controls autoplay) me suena la musica al momento de seleccionar la cancion, pero en firefox me funciona sin problemas le paso a alguien?

Saludos!

mi ejemplo
App.vue html

<pm-notification v-show="showNotification" :isSucceed="isSucceed">
      <p slot="body" v-if="isSucceed">
        {{ searchMessages }}
      </p>
       <p slot="body" v-else>
        No se encontraron resultados
      </p>

App.vue js

 data () {
    return {
      showNotification: false,
      isSucceed: false, 
     }
  },
search () {
      if (!this.searchQuery ) { return }
      this.isLoading = true
      trackService.search(this.searchQuery)
        .then( res => {
            if (res.tracks.total === 0) {
           	 this.isSucceed = false
          }else {
            this.isSucceed = true 
    
          this.isLoading = false
          this.tracks = res.tracks.items
	  this.showNotification = true

        })
    },

Notification.vue html

.notification(:class="{'is-success': isSucceed, 'is-danger': !isSucceed }")
          slot(name="body") mensaje

Notification.vue js

props:{
      isSucceed: {
        Type: Boolean,
        
      }

Mi showNotification:

    showNotification(newVal, prevVal){
      if(this.showNotification){
        setTimeout(() => {
          this.showNotification = !this.showNotification
          if(this.isFound) this.isFound = !this.isFound
        }, 3000)
      }
    }

Mi fetchTrackByName:

fetchTrackByName(this.searchQuery)
      .then(res => {
        if(res.tracks.total > 0){
          this.isFound = true;
        }
        this.showNotification = res.tracks.total === 0 || this.isFound;
        this.tracks = res.tracks.items
      })

Mi contenedor de la notificacion:

.notification(v-bind:class="{ isFound: isFound, isNotFound: !isFound }")

Mis estilos para el bind de la clase:

    .isFound{
  background-color: #23d160;
  color: whitesmoke;
    }

.isNotFound{
  background-color: #DA4644;
  color: whitesmoke;
}

Mi rendereo condicional para el mensaje del slot:

p(slot='body') {{ isFound ? searchMessage : 'No se encontraron resultados'}}

As铆 vamos de momento:

creo que la desventaja de usar alias para el @ es que se pierde el autocomplete de vscode鈥

solucion al desafio, en el query agregas el tipo de notificacion:

      trackService.search(this.searchQuery).then(res => {
        this.showNotification = true;
        this.notificationType = res.tracks.total === 0 ? "danger" : "success";
        this.tracks = res.tracks.items;
        this.isLoading = false;
      });

lo usas en el html

    pm-notification(v-show="showNotification", :notificationType="notificationType")
      p( v-show="notificationType === 'danger'",slot="body") No se encontraron resultados
      p( v-show="notificationType === 'success'",slot="body") Se encontraron {{this.tracks.length}} resultados

y en el componente hijo agregas la clase dinamicamente

          .notification(:class="{'is-danger' : notificationType === 'danger','is-success' : notificationType === 'success'}")
            slot(name="body")

no olviden registrar el notificationType en data y recibirlo como prop

Thoughts? 馃ぃ

  <h2>Canciones que estan Vue-nisimas</h2>

Un event-bus va a actuar como un emisor de eventos que podremos usar en todos los componentes y as铆 obtener la informaci贸n que necesitamos

Comparto mi solucion:

Cambie la propiedad 鈥渟earchMessage鈥 para que devolviera un objeto el cual leer谩 el componente de las notificaciones y modifique el watch para que cuando sea el objeto con la propiedad 鈥渃lass: alert鈥 este se borre en 2 segundos (me gusto mas tener 2 segundos)

Componente:

pm-notification(v-show="showNotification" :class="searchMessage.class")
  p(slot="texto") {{ searchMessage.text }}

Variable y watcher:

computed: {
  searchMessage () {
    if (this.tracks.length) {
      return {class:'success', text: `Econtrados: ${this.tracks.length}`}
    } else {
      return {class:'alert', text:'No se encontraron resultados'}
    }
  },
},

watch: {
  showNotification () {
    if (this.showNotification && this.searchMessage.class === 'alert') {
      setTimeout(() => this.showNotification = false,2000)
    }
  }
}

App.vue

<template lang="pug">
  #app
    pm-header

    pm-notification(v-show="showNotification", :notificationColor="notificationClass")
      p(slot="body") {{ notificationMessage }}

    pm-loader(v-show="isLoading")

    section.section(v-show="!isLoading")
      nav.nav
        .container
          input.input.is-large(
            type="text",
            placeholder="Buscar canciones"
            v-model="searchQuery"
          )
          a.button.is-info.is-large(@click="search") Buscar
          a.button.is-danger.is-large &times;
      //.container
        p
          small {{ searchMessage }}
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(:class="{ 'is-active': t.id === selectedTrack }", :track="t", @select="setSelectedTrack")

    pm-footer
</template>

<script>
import trackService from '@/services/track'

import PmFooter from '@/components/layout/Footer.vue'
import PmHeader from '@/components/layout/Header.vue'

import PmTrack from '@/components/Track.vue'

import PmLoader from '@/components/shared/Loader.vue'
import PmNotification from '@/components/shared/Notification.vue'

export default {
  name: 'app',

  components: { PmFooter, PmHeader, PmTrack, PmLoader, PmNotification },

  data () {
    return {
      searchQuery: '',
      tracks: [],

      isLoading: false,

      showNotification: false,
      notificationMessage: '',
      notificationClass: '',

      selectedTrack: ''
    }
  },

  computed: {
    searchMessage () {
      return `Se encontraron: ${this.tracks.length} canciones`
    }
  },

  watch: {
    showNotification () {
      if (this.showNotification) {
        if (this.tracks.length === 0) {
          this.notificationMessage = 'No se encontraron resultados'
          this.notificationClass = 'is-danger'
          setTimeout(() => {
            this.showNotification = false
          }, 2500)
        } else {
          this.notificationMessage = this.searchMessage
          this.notificationClass = 'is-success'
        }
      }
    }  
  },

  methods: {
    search () {
      if (!this.searchQuery) { return }

      this.isLoading = true

      trackService.search(this.searchQuery)
        .then(res => {
          this.showNotification = true
          this.tracks = res.tracks.items
          this.isLoading = false
        })
    },

    setSelectedTrack (id) {
      this.selectedTrack = id
    }
  }
}
</script>

<style lang="scss">
  @import './scss/main.scss';
  .results {
    margin-top: 30px;
  }

  a.button {
    margin-right: 10px;
    margin-top: 10px;
  }

  .is-active {
    border: 3px #23D160 solid;
  }
</style>

Notification.vue

<template lang="pug">
  .container
    .columns
      .columns.is-5.is-offset-4
        div(:class="classType")
          slot(name="body") Algo anduvo mal
</template>

<script>
export default {
  props: {
    notificationColor: ''
  },
  computed: {
    classType () {
      return `notification ${this.notificationColor}`
    }
  }
}
</script>

<style lang="scss" scoped>
  .columns {
    margin: auto;
    margin-top: 5px;
  }
</style>

Sumo al texto de recien que la idea es usar esto para pasarle otro json y cambiarle que sea de torta en vez de linea y funciona, y asi

Si el componente player(reproductor no aparece) den le un ancho a la etiqueta audio

Hola, una inquietud tengo este error:
[Vue warn]: Error in render: "TypeError: _vm.track.album is undefined" found in ---> <PmPlayer> at src/components/Player.vue <PmHeader> at src/components/layout/Header.vue <App> at src/App.vue <Root> 1:592:7 TypeError: "_vm.track.album is undefined"

Pero su funcionalidad esta bien, alguien sabe a que se debe?

<template>
  <div class="container">
    <div class="columns">
      <div class="column is-5 is-offset-4">
        <div class="notification" :class=" totalResults == 0 ? 'is-danger' : 'is-success' ">
          <slot name="body"></slot>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    totalResults: {
        type: Number,
        required: true
    }
  }
};
</script>
<style lang="scss" scoped>
.notification {
  margin: 10px;
}
</style>```

Buenas tardes, hay algo que no entiendo y estoy ansioso por resolverlo. Digamos que refrescas la p谩gina o presionas la tecla F5, 驴Porque no aparece al menos una imagen sin src, el elemento audio sin src? Est谩 claro que el texto t铆tulo de la canci贸n no aparece porque es la primera carga y no tiene nada que mostrar, pero 驴y lo dem谩s ?

Obviamente si busco y selecciono un track, si aparecera todo, pero mi consulta es que cuando actualizo o cargo el proyecto por primera vez no aparece el html del pm-player. En ning煤n momento hemos puesto un v-if o v-show al compomente pm-player. Incluso si agrego un texto fijo en el componente pm-player:

p
   strong Reproductor (30 segundos)
p
   strong {{ track.name }}
p
   small [{{ track.duration_ms }}]
p
   audio(controls, :src="track.preview_url")

Este no aparece en la primera carga del proyecto o cuando actualizo la p谩gina. Si inspecciono la p谩gina aparece as铆

<div class=鈥渃ontainer has-text-centered鈥>
      <h1 class=鈥渢itle鈥>Platzi music</h1>
      <h2 class=鈥渟ubtitle鈥>Canciones buen铆simas</h2>
      <!---->
</div>

La parte donde deber铆a aparecer el html de pm-player aparece comentada.

Sin embargo鈥 si reemplazo todo el contenido de pm-player y lo dejo as铆:

<template lang=鈥減ug鈥>
.content
   p
      strong Texto fijo
</template>

Se comporta como deber铆a, aparece el texto 鈥楾exto fijo鈥 al refrescar la p谩gina y se muestra el html del componente pm-player al inspeccionar la p谩gina

Por favor, necesito que me ayuden a resolver esta duda. 驴A qu茅 se debe este comportamiento?

Solo se crear铆a un solo archivo de plugin para cualquier funcionalidad o ser铆a recomendable crear uno por cada funcionalidad que existiera dentro del proyecto (?). Es decir, si en el ejemplo de Platzi-music hubiese otra situaci贸n en la que se necesitara usar un plugin, se podr铆a usar el mismo event-bus.js o se crear铆a otro (?)

Un event bus seria el equivalente a una base de datos en memoria cierto? Osea, hace algo muy similar, pero si tenemos una base de datos por lo menos podermos persisitir los cambios

<code>
<div :class="call ? 'notification is-success' : 'notification is-danger'">
	<slot name="body">algo andubo mal</slot>
</div>


export default{
	name: 'PmNotification',
	props: {call:{type: Boolean, required: true}}
}
``

Soluci贸n:

Notification.vue

<template lang="pug">
  .container
    .columns
      .column.is-4.is-offset-5
        .notification(:class="type")
          slot(name="body") Algo salio mal!
</template>

<script>
export default {
  name: 'notificacion',
  props: {
    type: {
      type: String,
      default: 'is-danger'
    }
  }
}
</script>

<style lang="scss" scoped>
  .container {
    margin: 10px 0;
  }
</style>

App.vue

<template lang="pug">
  #app
    pm-header
    pm-notificacion(
      :type="typeNotification",
      v-show="showNotification",
    )
      p(slot="body") {{ mesaggeNotification }}
    pm-loading(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.navbar
        .field.has-addons
          .control.is-expanded
            input.input(
              type="text"
              placeholder="Buscar canciones"
              v-model="searchQuery",
              v-on:keyup.enter="buscar"
            )
          .control
            button.button.is-info(@click="buscar") Buscar
          .control
            button.button.is-danger &times;
      p
        small Encontrados: {{ cantidad }}
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(
              :class="{ 'is-active': t.id === trackIdSeleted }"
              :track="t",
              @select="setSelectedTrack"
            )
    pm-footer
</template>

<script>
import trackService from '@/services/traks'

import PmHeader from '@/components/layout/Header.vue'
import PmFooter from '@/components/layout/Footer.vue'

import PmTrack from '@/components/Track.vue'

import PmLoading from '@/components/shared/Loading.vue'
import PmNotificacion from '@/components/shared/Notification.vue'

export default {
  name: 'app',
  components: {
    PmHeader,
    PmFooter,
    PmTrack,
    PmLoading,
    PmNotificacion
  },
  data () {
    return {
      tracks: [],
      searchQuery: '',
      isLoading: false,
      trackIdSeleted: '',
      showNotification: false,
      typeNotification: '',
      mesaggeNotification: ''
    }
  },
  methods: {
    buscar () {
      if (!this.searchQuery) { return }
      this.isLoading = true
      trackService.search(this.searchQuery)
        .then(res => {
          this.tracks = res.tracks.items
          this.isLoading = false

          const total = res.tracks.total
          let message, type
          if (total) {
            message = `Cantidad de Resultados: ${total}`
            type = 'is-info'
          } else {
            message = 'No se encontraron resultados'
            type = 'is-danger'
          }
          this.updateNotification(message, type)
        })
        .catch(() => {
          const type = 'is-danger'
          const message = `Ocurrio un error`
          this.updateNotification(message, type)
          this.isLoading = false
        })
    },
    setSelectedTrack (trackid) {
      this.trackIdSeleted = trackid
    },
    updateNotification (message, type) {
      this.mesaggeNotification = message
      this.typeNotification = type
      this.showNotification = true
    }
  },

  watch: {
    showNotification () {
      if (this.showNotification) {
        setTimeout(() => {
          this.showNotification = false
        }, 3000)
      }
    }
  },
  computed: {
    cantidad () {
      return this.tracks.length
    }
  }
}
</script>

<style lang="scss">
@import 'scss/main.scss';

#app {
  .results {
    margin-top: 30px;
  }

  .is-active {
    border: solid $primary;
  }
}
</style>

Dejo mi soluci贸n, sencilla pero espero les ayude

APP.VUE

 pm-notification(v-show="showNotification", :typeNotification="totalTracks > 0 ? 'is-success' : 'is-danger'")
      p(slot="body")
        span(v-if="totalTracks > 0") Se encontraron {{ totalTracks }} resultados
        span(v-else) No se encontraron resultados
data() {
      return {
        searchQuery: '',
        tracks: [],
        isLoading: false,
        showNotification: false,
        totalTracks: 0,
        selectedTrack: ''
      }
    },
    watch: {
      showNotification () {
        if (this.showNotification) {
          setTimeout(() => {
            this.showNotification = false
          }, 3000)
        }
      }
    },
search() {
        if (this.searchQuery.trim().length > 0) {
          this.isLoading = true
          trackService.search(this.searchQuery).then(res => {
            this.showNotification = true
            this.totalTracks = res.tracks.total
            this.tracks = res.tracks.items
            this.isLoading = false
          })
        }
      }

Notification.vue

<template lang="pug">
  .container
    .columns
      .column.is-5.is-offset-4
        .notification(:class="typeNotification")
          slot(name="body") Algo anduvo mal
</template>

<script>
  export default {
    props: {
      typeNotification: {
        type: String,
        required: true
      }
    }
  }
</script>
<template  lang="pug">
    .container
        .columns
            .column.is-5.is-offset-4
                .notification(v-bind:class="type_message")
                    slot(name="body") Has Error.


</template>

<script>
    export default {
        props:{
            type_message:{
                type:String,
                require: false
            }

        }
    }
</script>
<style lang="scss" scoped>
    .notification{
        margin: 10px;
    }
</style>```




<template lang=鈥減ug鈥>
#app
pm-header

pm-notification(
    v-show="showNotification",
    v-bind:type_message="notificationData.class_css")

    p(slot="body") {{notificationData.message}}

pm-loader(v-show="isLoading")
section.section(v-show="!isLoading")
  nav.nav
    .container
      input.input.is-large(
        v-model="searchQuery",
        type="text",
        placeholder="Search songs")

      a.button.is-info.is-large(v-on:click="search") search
      a.button.is-danger.is-large &times;

  .container
      p
        small {{searchMessage}}

  .container.result
    .columns.is-multiline
      .column.is-one-quarter(v-for="t in tracks")
        pm-track(
        v-bind:class="{'is-active': t.id == selectedTrack}",
        v-bind:track="t",
        v-on:select="setSelectedTrack")



pm-footer

</template>

<script>
import trackService from 鈥楡/services/track鈥;

import PmFooter from 鈥楡/components/layout/Footer.vue鈥;
import PmHeader from 鈥楡/components/layout/Header.vue鈥;

import PmTrack from 鈥楡/components/Track.vue鈥;

import PmLoader from 鈥楡/components/shared/Loader.vue鈥;
import PmNotification from 鈥淍/components/shared/Notification.vue鈥

export default {
name: 鈥榓pp鈥,
data () {
return {
searchQuery:鈥欌,
tracks:[],
isLoading:false,
selectedTrack:鈥欌,
showNotification:false,
notificationData:{
class_css : 鈥樷,
message : 鈥樷
}

}

},

components:{
PmFooter,
PmHeader,
PmTrack,
PmLoader,
PmNotification
},

methods:{
search(){
if(this.searchQuery.length < 2){
return false;
}

    this.isLoading = true;
    trackService.search(this.searchQuery)
        .then((res)=>{

            if(res.tracks.total !== 0){
                this.notificationData.class_css = 'is-success';
                this.notificationData.message = `found ${res.tracks.total} total`;
            }else{
                this.notificationData.class_css = 'is-danger';
                this.notificationData.message = `Not Found Result.`;
            }

            this.showNotification = true;
            this.tracks = res.tracks.items;
            this.isLoading = false;
        })
},
setSelectedTrack(id){
    this.selectedTrack = id;
}

},
computed:{
searchMessage(){
return Found ${this.tracks.length};
}
},

watch:{
showNotification(){
if(this.showNotification){
if(this.notificationData.class_css === 鈥榠s-success鈥) return;
setTimeout(()=>{
this.showNotification = false;
},3000)
}
}
}

}
</script>

<style lang=鈥渟css鈥>
@import 鈥./scss/main鈥;

.result{
margin-top:50px;
}

.is-active{
border: 3px solid #23d160;
}

</style>


Notifications.vue

<template lang="pug">
    .container
        .columns
            .column.is-4.is-offset-4
                .notification(:class="type_message")
                    slot(name="body") Algo anduvo mal


</template>
<script>
    export default {
        props:{
            type_message: {
                type: String,
                require: false,
            }
        }
    }
</script>
<style  lang="scss" scoped>
    .notification {
        margin: 10px;
    }
</style>

App.vue

<template lang="pug">
    .container
        pm-header
        pm-notifications(v-show='showNotification' :type_message='notificationType')
            p(slot='body') {{notificationMessage}}

        pm-loader(v-show="isLoading")
        section(v-show="!isLoading").section
            nav.nav
                .container
                    input.input.is-large(
                    v-model='searchQuery',
                    type="text",
                    placeholder="Buscar cancion"
                    )
                    a(@click="search").button.is-info.is-large Buscar
                    a.button.is-danger.is-large &times;
            .container.results
                p
                    small {{searchMessage}}
                .columns.is-multiline
                    .column.is-one-quarter(v-for ="t in tracks")
                        pm-track(
                            :class="{'is-active' : t.id === selectedTrack }",
                            :track="t",
                            @select="setSelectedTrack",

                        )


        pm-footer

</template>

<script>
    /* eslint-disable no-console */
    import PmLoader from '@/components/shared/Loader.vue'
    import trackService from '@/services/track'
    import PmHeader from '@/components/layout/Header.vue'
    import PmFooter from '@/components/layout/Footer.vue'
    import PmTrack from '@/components/Track.vue'
    import PmNotifications from '@/components/shared/Notifications.vue'
    export default {
        name: 'platzi-music',
        data(){
            return {
                searchQuery : '',
                tracks: [],
                isLoading: false,

                selectedTrack: '',
                showNotification: false,
                notificationMessage: ''

            }
        },

        components:{
            PmFooter,
            PmHeader,
            PmTrack,
            PmLoader,
            PmNotifications
        },
        computed: {
            searchMessage(){
                return 'Encontrados: '+ this.tracks.length;
            }
        },
        methods:{
            search (){
                if(!this.searchQuery ) {return}
                this.isLoading = true;
                trackService.search(this.searchQuery)
                    .then(res => {
                        console.log(res);
                        this.showNotification = true;
                        if(res.tracks.total === 0){
                            this.notificationType = 'is-danger';
                            this.notificationMessage = 'No se han encontrado resultados';
                        }else {
                            this.notificationType = 'is-success';
                            this.notificationMessage = 'Sea han encontrado ' + res.tracks.total + ' Resultados' ;
                        }
                        this.tracks = res.tracks.items;
                        this.isLoading = false;
                    })
            },
            setSelectedTrack(id){
                this.selectedTrack = id ;
            }

        },
        watch:{
            showNotification () {
                if(this.showNotification) {
                    setTimeout(() => {
                        this.showNotification = false
                    },3000)
                }
            }
        },
        mounted(){

        }
    }
</script >

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">

    .results {
        margin-top: 50px;
        columns{
            flex-wrap: wrap;
        }

        .column{
            min-width: 150px;
        }
    }
    .is-active{
        border: 3px #23d160 solid;
    }
</style>

aqui el reto quizas exita una mejor manera. Yo solo utilice el html

NOTIFICACIONES

<template lang="pug">
  .container
    .columns
      .column.is-5.is-offset-4
        .notification.is-danger(v-show="notification")
          slot(name="body") Notificacion default
        .notification.is-success(v-show="!notification")
          slot(name="body") Notificacion default
</template>

<script>
  export default {
    props:{
      notification:{
        type:Boolean,
      }
    }
  }
</script>

<style lang="scss" scoped>
  .notification{
    margin: 10px
  }
</style>



App.vue

<template lang="pug">
  #app
    PmHeader
    pm-notification(:notification="showNotification")
      p(slot="body" v-if="showNotification") no se encontraron resultados
      p(slot="body" v-else="showNotification") resultados {{serachMessage}}
  
    pm-loading(v-show="isLoading")

    section.section(v-show="!isLoading")
      nav.nav
        .container
          input.input(type="text" placeholder="Search Sons" v-model="searchQuery")
          a.is-info.button.is-large(@click="search") Search
          a.is-danger.button.is-large &times; 
      .container
      
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="track in tracks")
              pm-track(
                :class="{'is-active': track.id ===selectedTrack }"
                :track="track",
                @select="setSelected"
              )
              
    PmFooter            

</template>

No creoq ue nadie responda a estas alturas, pero como funciona la parte de event bus, es decir haces $bus.emit, o lo que es lo mismo new Vue.emit, pero luego haces $bus.on o lo que es lo mismo new Vue.on, son todo el rato instancias nuevas, como es posibles que instancias nuevas y diferentes tengan guardados eventos?

Gracias.

Notification.vue

<template lang="pug">
  .container
    .columns
      .column.is-5.is-offset-4      v-bind:class="{'is-active': t.id === selectedTrack}"
        .notification.is-success(v-show="notificationType==='Success'")
          slot(name="body") Something went wrong
        .notification.is-danger(v-show="notificationType==='Failed'")
          slot(name="body") Something went wrong
</template>

<script>
export default {
  props: {
    notificationType: { // "Success", "Failed"
      type: String,
      required() {
        return true;
      },
    },
  },
};
</script>

<style lang="scss" scoped>
  .notification {
    margin: 10px;
  }
</style>

App.vue

<template lang="pug">
  #app
    pm-header

    pm-notification(v-show="showNotification" v-bind:notification-type="notificationType")
      p(slot="body") {{notificationMessage}}

    pm-loader(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.nav
        .container
          input.input.is-large(
            type="text",
            placeholder="Buscar canciones",
            v-model="searchQuery"
          )
          a.button.is-info.is-large(@click="search") Buscar
          a.button.is-danger.is-large &times;
      .container
        p
          small {{ searchMessage }}

      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(
              v-bind:class="{'is-active': t.id === selectedTrack}",
              v-bind:track="t",
              v-on:select="setSelectedTrack")
    pm-footer
</template>

<script>
import trackService from './services/track';

import PmFooter from './components/layout/Footer.vue';
import PmHeader from './components/layout/Header.vue';

import PmTrack from './components/Track.vue';

import PmLoader from './components/shared/Loader.vue';
import PmNotification from './components/shared/Notification.vue';

export default {
  name: 'app',

  components: {
    PmFooter,
    PmHeader,
    PmTrack,
    PmLoader,
    PmNotification,
  },

  data() {
    return {
      searchQuery: '',
      tracks: [],
      isLoading: false,
      selectedTrack: '',
      showNotificationSuccess: false,
      showNotificationFailed: false,
    };
  },

  watch: {
    showNotification() {
      if (this.showNotification) {
        setTimeout(() => {
          this.hideNotifications();
        }, 3000);
      }
    },
  },

  computed: {
    searchMessage() {
      return `Found: ${this.tracks.length}`;
    },
    showNotification() {
      return this.showNotificationSuccess || this.showNotificationFailed;
    },
    notificationMessage() {
      let message = 'N/A';
      if (this.showNotificationSuccess) {
        message = `${this.tracks.length} tracks found.`;
      } else if (this.showNotificationFailed) {
        message = 'No tracks found.';
      }
      return message;
    },
    notificationType() {
      if (this.showNotificationSuccess) {
        return 'Success';
      }
      if (this.showNotificationFailed) {
        return 'Failed';
      }

      return 'N/A';
    },
  },

  methods: {
    search() {
      if (!this.searchQuery) { return; }
      this.isLoading = true;
      trackService.search(this.searchQuery)
        .then((res) => {
          if (res.tracks.total > 0) {
            this.showNotificationSuccess = true;
            this.showNotificationFailed = false;
          } else {
            this.showNotificationSuccess = false;
            this.showNotificationFailed = true;
          }

          this.tracks = res.tracks.items;
          this.isLoading = false;
        });
    },
    setSelectedTrack(id) {
      this.selectedTrack = id;
    },
    hideNotifications() {
      this.showNotificationSuccess = false;
      this.showNotificationFailed = false;
    },
  },
};
</script>

<style lang="scss">
  @import './scss/main.scss';

  .results {
    margin-top: 50px;
  }

  .is-active {
    border: 3px #48c774 solid;
  }
</style>

Espero haber entendido el ejercicio xD
**App.vue
**

<template lang="pug">
  #app
    pm-header
    pm-notification(v-show="showNotification", :notification="notify")
      //- p(slot="body") No se encontraron resultados!
    pm-loader(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.nav
        .container
          input.input.is-large(type="text", placeholder="Buscar canciones", v-model="searchQuery")
          a.button.is-info.is-large(@click="search") Buscar
          a.button.is-dange.is-large &times;
      .container
        p
          small {{ searchMessage }}
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(:track="t", v-on:select="setSelectedTrack", v-bind:class="{ 'is-active': t.id == selectedTrack }")
    pm-footer
</template>

<script>
import trackService from '@/services/track'

import PmFooter from '@/components/layout/Footer.vue'
import PmHeader from '@/components/layout/Header.vue'

import PmTrack from '@/components/Track.vue'

import PmLoader from '@/components/shared/Loader.vue'
import PmNotification from '@/components/shared/Notification.vue'

export default {
  name: 'app',
  components: { PmFooter, PmHeader, PmTrack, PmLoader, PmNotification },
  data () {
    return {
      searchQuery: '',
      tracks: [],
      isLoading: false,
      selectedTrack: '',
      showNotification: false,
      notify: {}
    }
  },
  computed: {
    searchMessage () {
      return `Encontrados: ${this.tracks.length}`
    }
  },
  watch: {
    showNotification () {
      if (this.showNotification) {
        setTimeout(() => {
          this.showNotification = false
        }, 3000)
      }
    }
  },
  methods: {
    search () {
      if (!this.searchQuery) { return }
      this.isLoading = true
      trackService.search(this.searchQuery)
        .then(
          res => {
            if (res.tracks.total === 0) {
              this.notify = {
                message: 'Algo anduvo mal',
                type: 'is-danger'
              }
            } else {
              this.notify = {
                message: 'Se encontraron resultados',
                type: 'is-success'
              }
            }
            console.log(this.notify)
            this.showNotification = true
            this.tracks = res.tracks.items
            this.isLoading = false
          })
    },
    setSelectedTrack (id) {
      this.selectedTrack = id
    }
  }
}
</script>

<style lang="scss">
  @import './scss/main.scss';
  .results {
    margin-top: 50px;
  }
  .is-active {
    border: 3px #23d160 solid;
  }
</style>

**Notification.vue
**

<template lang="pug">
    .container
      .columns
        .column.is-5.is-offset-4
          .notification(:class="notification.type")
            slot(name="body") {{ notification.message }}
</template>
<script>
export default {
  props: {
    notification: { type: Object, required: true }
  }
}
</script>
<style lang="scss">
  .notification {
    margin: 10px;
  }
</style>

A donde le doy me gusta, por que tambien es muy buena!

Crei que este video ya lo habiamos visto, ya tenia todo creado pero no me acuerdo a donde estaba el video

No puedo agregar mi codigo me sale un mensaje de forbbiden

App.vue

<template lang="pug">
  #app
    px-header
    px-notification(v-show="showNotification", :notificationStatus='notificationStatus')
      p(slot="body") No se encontraron resultados
    px-notification(v-show="!showNotification", :notificationStatus='notificationStatus')
      p(slot="body") Se encontraron {{ searchMessage }} resultados
    px-loader(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.nav.has-shadow
        .container.flexbox
          input.input.is-large(
            type="text"
            placeholder= "Buscar cancion"
            v-model="searchQuery" )
          a.button.is-info.is-large(@click='search') Buscar
          a.button.is-danger.is-large &times;
      .container
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            px-track(
              :class="{'is-active':t.id===selectedTrack}"
              :track="t",
              @select="setSelectedTrack") //<- componente hijo - importante poner v-bind
            //- | {{ t.name }} - {{ t.artists[0].name }}
    px-footer
</template>

<script>
import pxHeader from '@/components/layout/pxHeader'
import pxFooter from '@/components/layout/pxFooter'
import trackService from '@/services/track'
import pxTrack from '@/components/layout/Track'
import pxLoader from '@/components/shared/loader'
import pxNotification from '@/components/shared/notification'
export default {
  name: 'App',
  components: { pxHeader, pxFooter, pxTrack, pxLoader, pxNotification },
  data() {
    return {
      tracks: [],
      searchQuery: '',
      isLoading: false,
      selectedTrack: '',
      showNotification: false
    }
  },
  methods: {
    search() {
      if (!this.searchQuery) {
        //Evitar errores en consola valores truly or falsy
        return
      }
      this.isLoading = true
      trackService.search(this.searchQuery).then(res => {
        this.showNotification = res.tracks.total === 0
        this.tracks = res.tracks.items
        this.isLoading = false
      })
    },
    setSelectedTrack(id) {
      this.selectedTrack = id
    }
  },
  computed: {
    searchMessage() {
      return `${this.tracks.length}`
    },
    notificationStatus() {
      if (this.showNotification) {
        return true
      } else {
        return false
      }
    }
  },
  watch: {
    showNotification() {
      if (this.showNotification) {
        setTimeout(() => {
          this.showNotification = false
        }, 3000)
      }
    }
  }
}
</script>

<style lang="scss">
@import './scss/main.scss';
.results {
  margin-top: 50px;
}
.flexbox {
  display: flex;
}
.is-active {
  border: 3px solid #23d160;
}
</style>

notification.vue

<template lang="pug">
  .container
    .columns
      .column.is-5.is-offset-4
        .notification.is-danger(v-show='notificationStatus')
          slot(name="body")
        .notification.is-success(v-show='!notificationStatus')
          slot(name="body")
</template>

<script>
export default {
  props: {
    notificationStatus: {
      required: true
    }
  }
}
</script>

<style lang="scss" scoped>
.notification {
  margin: 10px;
}
</style>

Aqu铆 mi soluci贸n:
App.vue

<template lang="pug">
  #app
    pm-header
    pm-notification(v-show="showNotification", :foundNotification="foundNotification")
      p(slot="body", v-show="!foundNotification") No se encontraron resultados
      p(slot="body", v-show="foundNotification") Se encontraron {{totalSearch}} concidencias
    pm-loader(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.nav.has-shadow
        .container
          input.input.is-large(
            type="text",
            placeholder="Buscar canciones",
            v-model="searchQuery"
          )
          a.button.is-info.is-large(@click="search") Buscar
          a.button.is-danger.is-large &times;
      .container
        p
          small {{searchMessage}}
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(
              :track="t",
              @select="setSelectedTrack",
              :class="{'is-active' : t.id === selectedTrack}")
    pm-footer
</template>

<script>
import trackService from '@/services/track'
import PmHeader from '@/components/layout/Header.vue'
import PmFooter from '@/components/layout/Footer.vue'
import PmTrack from '@/components/Track.vue'
import PmLoader from '@/components/share/Loader.vue'
import PmNotification from '@/components/share/Notification.vue'

export default {
  name: 'app',
  data () {
    return {
      searchQuery: '',
      tracks: [],
      isLoading: false,
      selectedTrack: '',
      showNotification: '',
      totalSearch: 0
    }
  },

  components: {
    PmFooter,
    PmHeader,
    PmTrack,
    PmLoader,
    PmNotification
  },

  methods: {
    search () {
      if (!this.searchQuery) { return }

      this.isLoading = true

      trackService.search(this.searchQuery)
        .then(res => {
          this.showNotification = true
          this.tracks = res.tracks.items
          this.isLoading = false
          this.totalSearch = res.tracks.total
        })
    },
    setSelectedTrack (id) {
      this.selectedTrack = id
    }
  },

  computed: {
    searchMessage () {
      return `Encontrados: ${this.tracks.length}`
    },
    foundNotification () {
      return this.totalSearch > 0
    }
  },

  watch: {
    showNotification () {
      if (this.showNotification) {
        setTimeout(() => {
          this.showNotification = false
        }, 5000)
      }
    }
  }

}
</script>

<style lang="scss">
  @import './scss/main.scss';

  .results {
    margin-top: 50px;
  }

  .is-active{
    border: 3px solid #3298dc;
  }
</style>

Notification.vue

<template lang="pug">
    .container
        .columns
            .column.is-5.is-offset-4
                .notification(:class="{ 'is-success' : foundNotification, 'is-danger' : !foundNotification}")
                    slot(name="body") Algo anduvo mal
</template>

<script>
export default {
  props: {
    foundNotification: { type: Boolean, required: true }
  }
}
</script>

<style lang="scss" scoped>
    .notification{
        margin: 10px;
    }
</style>

Esta es mi soluci贸n en app.vue

<template lang="pug">
  #app
    pm-header

    pm-notifications(v-show="showNotifications")
      p(slot="body") No se encontro ning煤n resultado
    pm-numerotemas(v-show="!showNotifications")
      p(slot="nTemas") Total temas encontrados : {{ temasTotales }}

    pm-loader(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.navbar
        .container
          .control.is-expanded
            input.input.is-large(
            type="text",
            placeholder="Buscar canciones",
            v-model = "searchQuery"
            )
          a.button.is-info.is-large(@click="search") Buscar
          a.button.is-danger.is-large &times;
          p
            small {{ searchMessage }}
      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(
            :class="{ 'is-active': t.id === selectedTrack }",
            :track="t",
            @select="setSelectedTrack"
            )
    pm-footer
</template>

  <script>

  import trackService from '@/services/track'
  import Tarjetas from '@/components/Tarjetas.vue'
  import Encabezado from '@/components/Encabezado.vue'
  import Menu from '@/components/Menu.vue'
  import DomTarea from '@/components/DomTarea.vue'
  import NavMusica from '@/components/NavMusica.vue'
  import PmFooter from '@/components/layout/Footer.vue'
  import PmHeader from '@/components/layout/Header.vue'
  import PmTrack from '@/components/Track.vue'
  import PmLoader from '@/components/shared/Loader.vue'
  import PmNotifications from '@/components/shared/Notifications.vue'
  import PmNumerotemas from '@/components/shared/Numerotemas.vue'


  export default {
    name: 'App',
    components: {
      Tarjetas,
      Encabezado,
      Menu,
      DomTarea,
      NavMusica,
      PmFooter,
      PmHeader,
      PmTrack,
      PmLoader,
      PmNotifications,
      PmNumerotemas
    },

    data () {
    return {
      searchQuery: '',
      tracks: [],
      isLoading: false,
      selectedTrack: '',
      temasTotales: 0,
      showNotifications: false
    }
    },
    computed: {
      searchMessage () {
        return `Encontrados: ${this.tracks.length}`
      }

    },
    watch: {
      showNotifications() {
        if (this.showNotifications) {
          setTimeout(() => {
            this.showNotifications = false
          }, 3000)
        }
      }
    },
    methods: {
      search() {
      if (!this.searchQuery){ return }
      this.isLoading = true
      trackService.search(this.searchQuery)
      .then(res => {
      console.log(res)
      this.temasTotales = res.tracks.total
      this.showNotifications = res.tracks.total === 0
      this.tracks = res.tracks.items
      this.isLoading = false
      })
      },
      setSelectedTrack(id) {
        this.selectedTrack = id
      }
    }

  }

  </script>

<style lang="scss">
  @import './scss/main.scss';

  .results {
    margin-top: 50px;
  }

  .is-active {
    border: 3px #23d160 solid;
  }
</style>

Recien ando leyendo un libro sobre vue, tocan el tema del even bus, pero al final concluye que no es recomendable su uso y si se usa es con precauci贸n.

https://vuejs.org/v2/style-guide/#Non-flux-state-management-use-with-caution

Vuex permite solucionar esto de una manera mas eficiente usando los modules de vuex, esto igualmente se ve en el curso, recomiendo que vean el codigo y lo analizen pero yo no lo haria paso a paso porque justo para esto se desarrollo Vuex el cual es creado por el equipo de desarrollo de Vue

No se si es la mejor forma, pero pues, ahi les va. XD

App.vue

<template lang="pug">
  #app
    pm-header

    pm-notification(v-show="showNotification" :totalTracks="totalTracks")
      p(slot="body" ) {{ tracks.length === 0 ? `No hay resultados &#x1F50D;` : `Se encontraron ${totalTracks} resultados &#x2714;`}}

    pm-loader(v-show="isLoading")
    section.section(v-show="!isLoading")
      nav.nav
        .container
          input.input.is-large(
            type="text",
            placeholder="Buscar canciones",
            v-model="searchQuery"
          )
          a.button.is-info.is-large(v-on:click="search()") Buscar
          a.button.is-danger.is-large &times;
      .container
        p
          small {{ searchMessage }}

      .container.results
        .columns.is-multiline
          .column.is-one-quarter(v-for="t in tracks")
            pm-track(
              :class="{ 'is-active': t.id === selectedTrack }"
              :track="t", 
              v-on:select="setSelectedTrack")

    pm-footer

    //- h1 Ciclo de Vida
</template>

<script>
import trackService from './services/track.js'

import PmFooter from './components/layout/Footer.vue'
import PmHeader from './components/layout/Header.vue'

import PmTrack from './components/Track.vue'

import PmNotification from './components/shared/Notification.vue'

import PmLoader from './components/shared/Loader.vue'

export default {
  name: 'App',
  components: { PmFooter, PmHeader, PmTrack, PmLoader, PmNotification },
  data(){
    return {
      searchQuery: '',
      tracks: [],

      isLoading: false,
      showNotification: false,
      totalTracks: '',

      selectedTrack: ''
    }
  },
  watch: {
    showNotification () {
      if(this.showNotification){
        setTimeout(() => {
          this.showNotification = false
        }, 30000)
      }
    }
  },
  methods: {
    search () {
      if (!this.searchQuery){
        return
      }
      
      this.isLoading = true

      trackService.search(this.searchQuery)
      .then(res =>{
        this.totalTracks = res.tracks.total
        this.showNotification = true
        this.tracks = res.tracks.items
        this.isLoading = false
      })
    },
    setSelectedTrack(id){
      this.selectedTrack = id
    }
  },
  computed:{
    searchMessage () {
      return `Encontrados: ${this.tracks.length}`
    }
  }
}

</script>

<style lang="scss">
@import './scss/main.scss';

.results{
  margin-top: 50px
}

.is-active{
  border: 3px #23d160 solid;
}
</style>

Notification.vue

<template lang="pug">
    .container
      .columns
        .column.is-5.is-offset-4
          .notification(:class="{ 'is-danger' : totalTracks === 0, 'is-success': totalTracks !== 0}")
            slot(name="body") Algo anduvo mal &#x1FA79;
</template>

<script>
export default {
  props:{
    totalTracks: {
      type: Number
    }
  }
}
</script>

<style lang="scss" scoped>
  .notification {
      margin: 10px;
  }
</style>

馃憣

Notification.vue

<template lang="pug">
    .container
        .columns
            .column.is-5.is-offset-4
                .notification(:class='color')
                    slot(name="body") Algo anduvo mal
</template>

<script >
  export default {
    props: {
      color: { type: String, required: true }
    }
  }
</script >

App.vue

<template>

pm-notification(v-show=鈥渟howNotification鈥 :color=鈥榗olorNotification鈥)
p(slot=鈥渂ody鈥) {{ messageNotification }}

</template

en data adicione las siguientes propiedades
colorNotification: 鈥榠s-success鈥,
messageNotification: 鈥樷,

y en search me quedo asi
search () {
if (!this.searchQuery) { return } // esto se lee como si no existe searchQuery entonces return
this.isLoading = true
trackService.search(this.searchQuery)
.then(res => {
this.showNotification = true
this.colorNotification = 'is-danger鈥
this.messageNotification = 鈥楴o se encontraron resutlados鈥

      if (res.tracks.total > 0) {
        this.messageNotification = `Se encontraron: ${this.tracks.length}`
        this.colorNotification = 'is-success'
      }
      this.tracks = res.tracks.items
      this.isLoading = false
    })
}