Bienvenida e Introducción

1

Bienvenida y Presentación del proyecto: PlatziRooms

2

Creación y configuración inicial del proyecto utilizando VUE CLI 3

3

Boilerplate y Limpieza del proyecto

Render Functions y JSX

4

¿Que son las Render Functions y JSX?

5

Preparando nuestro entorno para un prototipo

6

Creación de un componente con createElement

7

Utilizando JSX para la creación de un componente

8

Utilizando Slots con Render Functions y JSX

9

Creando un Modal de Login para PlatziRooms

10

Creando la lógica general de nuestros modales con Vuex

11

Creando el contenido de nuestro modal

Componentes controlados y uso de librerías externas

12

Componentes Controlados y Variables Personalizadas

13

Construcción del componente de Recordar Contraseña

14

Creación de un componente Slider utilizando la librería tiny-slider

High Order Functions

15

¿Qué son las High Order Functions? - Crea tu primera HOF

16

Vuex

17

Obtener datos usando Vuex

18

Crea un getter dinámico en Vuex utilizando HOF

19

Creación de la vista para creación de publicaciones

20

Agregando datos con Vuex

Base de datos en tiempo real con Firebase

21

Instalación y Configuracion de Firebase Realtime Database

22

Obteniendo los datos desde Firebase Realtime Database

23

Agregando la consulta de usuarios

24

Almacenando nuevas publicaciones en Firebase Realtime Database

25

Perfeccionando el flujo de navegación de nuestra App

Autenticación con Firebase

26

Configuracion Inicial para trabajar con Firebase Authentication

27

Agregando nuevos usuarios en firebase autentication

28

Inicio de sesión de usuario

29

Cierre de sesión de usuario

30

Protegiendo páginas utilizando Navigation Guards

Scoped Slots

31

¿Qué son los Scoped Slots? Dónde utilizarlos y por qué

32

Integración de Scoped Slots en Platzi Rooms

Deploy

33

Creando y desplegando nuestra app en Heroku

34

Conclusiones y Despedida

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Almacenando nuevas publicaciones en Firebase Realtime Database

24/34
Recursos

Aportes 8

Preguntas 2

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Reto:

App.vue

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>
import { mapActions } from 'vuex';
export default {
  created() {
    this.fethServices();
  },

  methods: {
    ...mapActions({ fethServices: 'FETCH_SERVICES'}),
  },
}
</script>


<style lang="postcss" src="./assets/tailwind.postcss"></style>
<style lang="css" src="./assets/main.css"></style>

Store.js

...
actions: {
... 
FETCH_SERVICES: ({ state, commit }) => new Promise((resolve) => {
      const instance = firebase.database().ref('services');

      instance.once('value', (snapshot) => {
        const services = snapshot.val();
        Object.keys(services).forEach((serviceId) => {
          const service = services[serviceId];
          const data = {
            item: service,
            id: serviceId,
            resource: 'services',
          };
          commit('SET_ITEM', data);
        });

        resolve(Object.values(state.rooms));
      });
    }),
...
}
...

getters: {
...
services: state => state.services,
...
}

filters/Id2service.js

import store from '../store';

const id2Serive = {};
const { services } = store.state;

id2Serive.install = function id2s(Vue) {
  Vue.filter('id-to-service', (val) => {
    if (services[val]) {
      return services[val].name;
    }
    return val;
  });
};

export default id2Serive;

CreateHousePage.vue

<template>
  <page-layout>
    <section class="py-4 bg-teal-dark">
      <div class="container">
        <form class="form">
          <div class="form__field relative">
            <i class="input-icon material-icons absolute text-grey-darker">search</i>
            <input
              class="input__search"
              id="where"
              type="text"
              placeholder="Mexico City, Mexico">
          </div>
        </form>
      </div>
    </section>
    <section class="section__create py-6">
      <div class="container">
        <h1 class="text-3x1"> Publish a new rooms</h1>
        <form>
          <div class="mb-4">
            <label class="input__label">Title</label>
            <input
              v-model="publication.title"
              class="input__field" type="text" placeholder="Bruce Wayne">
          </div>
          <div class="mb-4">
            <label class="input__label">Description</label>
            <textarea
              v-model="publication.description"
              class="input__field" rows="10" placeholder="Bruce Wayne"></textarea>
          </div>
          <div class="mb-4">
            <label class="input__label">Feacture Image</label>
            <input
              v-model="publication.feacturedImage"
              class="input__field"
              type="text" placeholder="https://images.unsplash.com/photo-1560681610-68f081d2e7dd?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=934&q=80">
          </div>
          <div class="mb-4">
            <label class="input__label">Servicios</label>
            <button v-for="(service, id) in services" :key="id"
              @click.prevent="addService(id)"
              class="font-semibold py-3 px-6 mr-4 rounded"
              :class="isActive(id) ? 'bg-blue-dark':'bg-blue-light'">
              {{ service.name }}
            </button>
          </div>
          <div class="mb-4 text-right">
            <button
              @click.prevent="save"
              class="w-full bg-yellow-dark text-yellow-darker font-semibold py-3 px-6 rounded">
              Publish
            </button>
          </div>
        </form>
      </div>
    </section>
  </page-layout>
</template>

<script>
import { mapGetters } from 'vuex';
import PageLayout from '@/layouts/PageLayout.vue';

export default {
  name: 'CreateHousePage',

  data() {
    return {
      publication: {
        title: '',
        description: '',
        feacturedImage: '',
        services: {},
      },
    };
  },

  components: {
    PageLayout,
  },

  computed: {
    ...mapGetters(['services']),
  },

  methods: {
    save() {
      const {
        title,
        description,
        services,
        feacturedImage,
      } = this.publication;

      const room = {
        title,
        description,
        services,
        featured_image: feacturedImage,
        publishedAt: Date.now(),
      };

      this.$store.dispatch('CREATE_ROOM', room);
    },

    addService(serviceId) {
      if (this.publication.services[serviceId]) {
        this.$delete(this.publication.services, serviceId);
      } else {
        const id = JSON.parse(JSON.stringify(serviceId));
        this.$set(this.publication.services, id, id);
      }
    },

    isActive(serviceId) {
      if (this.publication.services[serviceId]) {
        return true;
      }
      return false;
    },
  },
};
</script>
  • Formulario:

  • HouseCard:

Reto Aceptado!!!

store.js

    FETCH_SERVICES: ({ state, commit }) => new Promise((resolve) => {
      const instance = firebase.database().ref('services');
      instance.once('value', (snapshot) => {
        const services = snapshot.val();
        Object.keys(services).forEach((serviceId) => {
          const service = services[serviceId];
          commit('SET_ITEM', { resource: 'services', id: serviceId, item: service });
        });
        resolve(Object.values(state.services));
      });
    }), 

CreateHousePage.vue

<template>
  <page-layout>
    <section class="py-4 bg-teal-dark">
      <div class="container">
        <form class="form">
          <div class="form__field relative">
            <i class="input-icon material-icons absolute text-grey-darker">search</i>
            <input
              class="input__search"
              id="where"
              type="text"
              placeholder="Mexico City, Mexico">
          </div>
        </form>
      </div>
    </section>
    <section class="section_create py-6">
     <div class="container">
       <h1 class="text-3xl">Publish a new room</h1>
       <form action="submit">
         <div class="mb-4">
           <label for="" class="input__label">Title</label>
           <input v-model="publication.title"
                  type="text" class="input__field" placeholder="Bruce Wayne">
         </div>
         <div class="mb-4">
           <label for="" class="input__label">Description</label>
           <textarea v-model="publication.description" type="text" class="input__field"
                     rows="10" placeholder="Bruce Wayne"> </textarea>
         </div>
         <div class="mb-4">
           <label for="" class="input__label">Feature Image</label>
           <input v-model="publication.featureImage" type="text" class="input__field"
                  placeholder="https://images.unsplash.com/photo-1573770397940-1bb341182ac0?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80">
         </div>
         <div class="mb-4" v-for="service in services" :key="service['.key']">
           <input type="checkbox"
                  @click="checked(service['.key'])"
                  :id="service['.key']">
           <label for="checkbox"> {{ service.name }}</label><br>
         </div>
         <div class="mb-4 text-right">
           <button v-on:click.prevent="save" class="w-full bg-yellow-dark text-yellow-dark
           font-semibold py-3 px-6 rounded">Crear</button>
         </div>
       </form>
     </div>
    </section>
  </page-layout>
</template>

<script>
import { mapGetters } from 'vuex';
import PageLayout from '../layouts/PageLayout.vue';

export default {
  name: 'CreateHousePage',
  components: {
    PageLayout,
  },
  computed: {
    ...mapGetters([
      'services',
    ]),
  },
  data() {
    return {
      publication: {
        title: '',
        description: '',
        featureImage: '',
        idservice: {},
      },
    };
  },
  methods: {
    save() {
      const {
        title, description, featureImage, idservice,
      } = this.publication;
      const room = {
        title,
        description,
        featured_image: featureImage,
        services: idservice,
        publishedAt: Date.now(),
      };
      this.$store.dispatch('CREATE_ROOM', room)
        .then(() => {
          this.$router.push({ name: 'SearchPage' });
        });
    },
    checked(idserv) {
      this.publication.idservice[idserv] = idserv;
    },
  },
  beforeCreate() {
    this.$store.dispatch('FETCH_SERVICES');
  },
};
</script>

<style scoped>

</style>

HomePage.vue

 <div class="mb-4 ml-4" v-for="service in services" :key="service['.key']">
              <li v-show="service['.key'] === room.services[service['.key']]">
                {{ service.name }}
              </li>
            </div>

Tal parece que el realtime database maneja us URL’s similares a Firestore, ya que se pueden acceder mediante su URL por así decirlo, aunque con firestore para actualizar se hace mediante el JSON directamente, es decir, “document.field”: “value”

En este vídeo también vemos cómo crear un proyecto de React para utilizar Firebase en el nuevo sitio de Podcasts de la gente de Aprendiendo Frontend:

¿Cual es la finalidad de resolver las promesas en los actions del store
CREATE_ROOM, FETCH_ROOM y FETCH_USER?
ya que el no colocarlas sigue funcionando…

FETCH_USER({ state, commit }, id) {
      return new Promise((resolve) => {
        firebase.database().ref('users').child(id).once('value', (snapshot) => {
          commit('SET_ITEM', {
            resource: 'users',
            id: snapshot.key,
            item: snapshot.val(),
          });
        });
        resolve(state.users[id]);
      });
    }

Si se supone que el método once( ) retorna una promesa.

Me tomó bastante tiempo pero lo logré xD
Dejo mi commit del ejercicio :3
https://github.com/andres87mx/vuejs-avanzado-firebase/commit/d506d29da34bf7e196ee80eea544060e321d0538

Reto:

  1. Store
FETCH_SERVICES: ({ state, commit }) =>
  new Promise((resolve, reject) => {
    firebase
      .database()
      .ref('services')
      .once('value', snapshot => {
        const services = snapshot.val()
        Object.keys(services).forEach(serviceId => {
          const service = services[serviceId]
          commit('SET_ITEM', { resource: 'services', id: serviceId, item: service })
        })
        resolve(Object.values(state.services))
      }, err => {
        reject(err)
      })
  }),
  1. Custom Checkbox:
<template>
  <label :for="label.identity" class="container-checkbox">
    {{ label }}
    <input :id="label.identity" @change="changeHandler" type="checkbox" v-model="checked" />
    <span class="checkmark"></span>
  </label>
</template>

<script>
export default {
  data() {
    return {
      checked: false,
    }
  },
  name: 'CheckBox',
  props: ['label', 'identity'],
  methods: {
    changeHandler() {
      this.$emit('changed', {
        checked: this.checked,
        id: this.identity,
      })
    },
  },
}
</script>
<style>
/* Customize the label (the container) */
.container-checkbox {
  display: block;
  position: relative;
  padding-left: 35px;
  margin-bottom: 12px;
  cursor: pointer;
  font-size: 22px;
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}

/* Hide the browser's default checkbox */
.container-checkbox input {
  position: absolute;
  opacity: 0;
  cursor: pointer;
  height: 0;
  width: 0;
}

/* Create a custom checkbox */
.checkmark {
  position: absolute;
  top: 0;
  left: 0;
  height: 25px;
  width: 25px;
  background-color: #eee;
}

/* On mouse-over, add a grey background color */
.container-checkbox:hover input ~ .checkmark {
  background-color: #ccc;
}

/* When the checkbox is checked, add a blue background */
.container-checkbox input:checked ~ .checkmark {
  background-color: #2196f3;
}

/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
  content: '';
  position: absolute;
  display: none;
}

/* Show the checkmark when checked */
.container-checkbox input:checked ~ .checkmark:after {
  display: block;
}

/* Style the checkmark/indicator */
.container-checkbox .checkmark:after {
  left: 9px;
  top: 5px;
  width: 5px;
  height: 10px;
  border: solid white;
  border-width: 0 3px 3px 0;
  -webkit-transform: rotate(45deg);
  -ms-transform: rotate(45deg);
  transform: rotate(45deg);
}
</style>
  1. CreateHousePage.vue
<div class="mb-4">
  <label for="" class="input__label">Services</label>
  <check-box
    v-for="service in services"
    :key="service['.key']"
    :label="service.name"
    :identity="service['.key']"
    @changed="checked"
  ></check-box>
</div>
.
.
.
import { mapGetters } from 'vuex'
import PageLayout from '@/layouts/PageLayout.vue'
import CheckBox from '@/components/CheckBox.vue'

export default {
  name: 'CreateHousePage',
  beforeCreate() {
    this.$store.dispatch('FETCH_SERVICES')
  },
  data() {
    return {
      publication: {
        title: '',
        description: '',
        featured_image: '',
        services: {},
      },
    }
  },
  computed: {
    ...mapGetters(['services']),
  },
  components: {
    PageLayout,
    CheckBox,
  },
  methods: {
    savePublication() {
      const { title, description, featured_image, services } = this.publication
      const room = {
        title,
        description,
        featured_image,
        services,
        publishedAt: Date.now(),
      }
      this.$store.dispatch('CREATE_ROOM', room)
    },
    checked($event) {
      if ($event.checked) {
        this.publication.services[$event.id] = $event.id
      } else {
        delete this.publication.services[$event.id]
      }
    },
  },
}