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

A煤n no tienes acceso a esta clase

Crea una cuenta y contin煤a viendo este curso

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? Crea una cuenta o inicia sesi贸n.

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:

Tal parece que el realtime database maneja us URL鈥檚 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, 鈥渄ocument.field鈥: 鈥渧alue鈥

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 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>

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]
      }
    },
  },
}