No tienes acceso a esta clase

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

Rutas dinámicas

32/38
Recursos

Aportes 54

Preguntas 13

Ordenar por:

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

Hay un error en el codigo, relacionado con la forma de obtener el valor promedio. Si bien el codigo funciona, no se obtiene el numerio esperado debido a un mal uso de la funcion Math.abs.

Una solucion correcta seria cambiar la computed avg por la siguiente funcion:

avg() {
  return this.history.reduce((a, b) => a + parseFloat(b.priceUsd), 0) / this.history.length
}

Endpoint para getAssetHistory

${url}/assets/${coin}/history?interval=h1&start=${start}&end=${end}

Para seguir el video en linea, debería haber un archivo para copiar el template igual a como lo copia en la pantalla, no uno que ya este terminado

Dejo el archivo CoinDetail.vue que se olvidaron de poner, no hay que usar el que esta en archivos y enlaces porque ese esta finalizado y va a dar error

<template>
  <div class="flex-col">
    <template>
      <div class="flex flex-col sm:flex-row justify-around items-center">
        <div class="flex flex-col items-center">
          <img class="w-20 h-20 mr-5" />
          <h1 class="text-5xl">
            <small class="sm:mr-2 text-gray-500"></small>
          </h1>
        </div>

        <div class="my-10 flex flex-col">
          <ul>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Ranking</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio actual</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más bajo</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más alto</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio Promedio</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Variación 24hs</b>
              <span></span>
            </li>
          </ul>
        </div>

        <div class="my-10 sm:mt-0 flex flex-col justify-center text-center">
          <button
            class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
          >
            Cambiar
          </button>

          <div class="flex flex-row my-5">
            <label class="w-full" for="convertValue">
              <input
                id="convertValue"
                type="number"
                class="text-center bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
              />
            </label>
          </div>

          <span class="text-xl"></span>
        </div>
      </div>
    </template>
  </div>
</template>

no es que hasta ahora me esté dando cuenta del tema, pero ciertamente con la aplicación de estas funciones de javascript veo la necesidad de primero dominar los fundamentos de JS, para luego adentrarme en los frameworks (no sé qué me hace pensar que puedo avanzar con solamente saber que el lenguaje existe, sin antes dominarlo, y es que es tan extenso!) 😭

Asi quedaria para los que estan trabajando con vue js3, dame un corazon por que me costo muchisimo encontrar la solucion jajaja

Router.js

<code> 
import { createWebHistory, createRouter } from "vue-router";

import Home from "@/views/Home.vue";
import Error from "@/views/Error.vue";
import About from "@/views/About.vue";
import CoinDetail from "@/views/CoinDetail.vue";

const routes = [
  {
    path: "/",
    name: "Home",
    component: Home,
  },
  {
    path: "/about",
    name: "about",
    component: About,
  },
  {
    path: "/CoinDetail/:id(.*)",
    name: "Coin-Detail",
    component: CoinDetail,
  },
  {
    path: "/:catchAll(.*)",
    name: "error",
    component: Error,
  },
];

const router = createRouter({
  history: createWebHistory(),
  routes,
});

export default router;

CoinDetail.vue

<code> 
<template>
  <div class="flex-col">
    <div class="flex flex-col sm:flex-row justify-around items-center">
      <div class="flex flex-col items-center">
        <img
          :src="`https://static.coincap.io/assets/icons/${asset.symbol.toLowerCase()}@2x.png`"
          :alt="asset.name"
          class="w-20 h-20 mr-5"
        />
        <h1 class="text-5xl">
          {{ asset.name }}
          <small class="sm:mr-2 text-gray-500">{{ asset.symbol }}</small>
        </h1>
      </div>

      <div class="my-10 flex flex-col">
        <ul>
          <li class="flex justify-between">
            <b class="text-gray-600 mr-10 uppercase">Ranking</b>
            <span>#{{ asset.rank }}</span>
          </li>
          <li class="flex justify-between">
            <b class="text-gray-600 mr-10 uppercase">Precio actual</b>
            <span>{{ dollarFilter(asset.priceUsd) }}</span>
          </li>
          <li class="flex justify-between">
            <b class="text-gray-600 mr-10 uppercase">Precio más bajo</b>
            <span>{{ dollarFilter(min) }}</span>
          </li>
          <li class="flex justify-between">
            <b class="text-gray-600 mr-10 uppercase">Precio más alto</b>
            <span>{{ dollarFilter(max) }}</span>
          </li>
          <li class="flex justify-between">
            <b class="text-gray-600 mr-10 uppercase">Precio Promedio</b>
            <span>{{ dollarFilter(avg) }}</span>
          </li>
          <li class="flex justify-between">
            <b class="text-gray-600 mr-10 uppercase">Variación 24hs</b>
            <span>{{ percentFilter(asset.changePercent24Hr) }}</span>
          </li>
        </ul>
      </div>

      <div class="my-10 sm:mt-0 flex flex-col justify-center text-center">
        <button
          class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
        >
          Cambiar
        </button>

        <div class="flex flex-row my-5">
          <label class="w-full" for="convertValue">
            <input
              id="convertValue"
              type="number"
              class="text-center bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
            />
          </label>
        </div>

        <span class="text-xl"></span>
      </div>
    </div>
  </div>
</template>


<script>
import api from "@/api";
import { dollarFilter, percentFilter } from "@/filters";

export default {
  name: "CoinDetail",

  data() {
    return {
      asset: {},
      history: [],
    };
  },

  setup() {
    return {
      dollarFilter,
      percentFilter,
    };
  },

  computed: {
    min() {
      return Math.min(
        ...this.history.map((h) => parseFloat(h.priceUsd).toFixed(2))
      );
    },

    max() {
      return Math.max(
        ...this.history.map((h) => parseFloat(h.priceUsd).toFixed(2))
      );
    },

    avg() {
      return (
        this.history.reduce((a, b) => a + parseFloat(b.priceUsd), 0) /
        this.history.length
      );
    },
  },

  created() {
    this.getCoinDetail();
  },

  methods: {
    getCoinDetail() {
      const id = this.$route.params.id;

      Promise.all([api.getAsset(id), api.getAssetHistory(id)]).then(
        ([asset, history]) => {
          this.asset = asset;
          this.history = history;
        }
      );
    },
  },
};
</script>

<style scoped>
td {
  padding: 10px;
  text-align: center;
}
</style>

Api.js

<code> 
const url = "https://api.coincap.io/v2";

function getAssets() {
  return fetch(`${url}/assets?limit=20`)
    .then((res) => res.json())
    .then((res) => res.data);
}

function getAsset(CoinDetail) {
  return fetch(`${url}/assets/${CoinDetail}`)
    .then((res) => res.json())
    .then((res) => res.data);
}

function getAssetHistory(CoinDetail) {
  const now = new Date();
  const end = now.getTime();
  now.setDate(now.getDate() - 1);
  const start = now.getTime();

  return fetch(
    `${url}/assets/${CoinDetail}/history?interval=h1&start=${start}&end=${end}`
  )
    .then((res) => res.json())
    .then((res) => res.data);
}
export default {
  getAssets,
  getAsset,
  getAssetHistory,
};

Espero les sirva.

return fetch(${url}/assets/${coin}/history?interval=h1&start=${start}&end=${end})

El uso de Fetch es similar al de axios?
Te lo pregunto porque estoy haciendo un ejemplo pero requiero autenticarme con Oauth 2

Hola compañeros, para los que estén usando Vue3, pueden utilizar los filters de la siguiente manera.

<template>
  <div class="flex-col">
    <template v-if="asset.id">
      <div class="flex flex-col sm:flex-row justify-around items-center">
        <div class="flex flex-col items-center">
          <img
            :src="
              `https://static.coincap.io/assets/icons/${asset.symbol.toLowerCase()}@2x.png`
            "
            :alt="asset.name"
            class="w-20 h-20 mr-5"
          />
          <h1 class="text-5xl">
            {{ asset.name }}
            <small class="sm:mr-2 text-gray-500">{{ asset.symbol }}</small>
          </h1>
        </div>

        <div class="my-10 flex flex-col">
          <ul>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Ranking</b>
              <span>#{{ asset.rank }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio actual</b>
              <span>{{ dollarFilter(asset.priceUsd) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más bajo</b>
              <span>{{ dollarFilter(min) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más alto</b>
              <span>{{ dollarFilter(max) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio Promedio</b>
              <span>{{ dollarFilter(avg) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Variación 24hs</b>
              <span>{{ percentFilter(asset.changePercent24Hr) }}</span>
            </li>
          </ul>
        </div>

        <div class="my-10 sm:mt-0 flex flex-col justify-center text-center">
          <button
            class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
          >Cambiar</button>

          <div class="flex flex-row my-5">
            <label class="w-full" for="convertValue">
              <input
                id="convertValue"
                type="number"
                class="text-center bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
              />
            </label>
          </div>

          <span class="text-xl"></span>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
import api from '@/api'
import { dollarFilter, percentFilter } from '@/filters'

export default {
  name: 'CoinDetail',

  data() {
    return {
      asset: {},
      history: []
    }
  },

  setup() {
      return {
          dollarFilter,
          percentFilter
      }
  },

  computed: {
    min() {
      return Math.min(
        ...this.history.map(h => parseFloat(h.priceUsd).toFixed(2))
      )
    },

    max() {
      return Math.max(
        ...this.history.map(h => parseFloat(h.priceUsd).toFixed(2))
      )
    },

    avg() {
      return this.history.reduce((a,b) => a + parseFloat(b.priceUsd), 0) / this.history.length
    }
  },

  created() {
    this.getCoin()
  },

  methods: {
    getCoin() {
      const id = this.$route.params.id

      Promise.all([api.getAsset(id), api.getAssetHistory(id)]).then(
        ([asset, history]) => {
          this.asset = asset
          this.history = history
        }
      )
    }
  }
}
</script>

<style scoped>
td {
  padding: 10px;
  text-align: center;
}
</style>```

Lo bueno de esto es que gracias al ciclo de vida de los componentes, cada vez que la página cargue, aunque sea un Single Page Application, volverá a hacer la llamada a la API correspondiente y actualizará sus valores:D

Estoy confundido, tampoco tengo mucha experiencia con Javascript, pero la función abs no es para obtener el valor absoluto? Porque lo usamos para calcular el promedio?

las rutas dinamicas nos permite tener una un componente asociado a la ruta cuyo contenido siempre va a cambiar segun parametros que podemos introducir en la ruta, esto es ideal a momento de crear sitios con contenido dinamico como blog o ecommerce. como siempre con una explicacion sencilla somos capaces de lograr tener un sitio cada vez mas dimanico.

lo único que no entendí hasta ahora es como usabas la api de coincap… con eso de fetch y promesas… creo personalmente que lo deberías haber explicado

Tuve un problema con la ruta ${url}/assets/${coin}/history?interval=h1&start=${start}&end=${end} del getAssetHistory por la cual tuve que eliminar las constantes que empleamos y utilice la url corta como figura en la documentacion.

function getAssetHistory(coin) {
return fetch(
${url}/assets/${coin}/history?interval=d1
)
.then((res) => res.json())
.then((res) => res.data);
}

Para los que no les funciona cuando navegan a la ruta:
https://localhost:8080/coin/bitcoin (minuto 3:00)
arregle mi problema copiando el codigo del archivo: api.js el cual lo descargue desde los recursos.
Este era mi error antes (no se veia la información de CoinDetail.vue):

Necesito ayuda. He estado buscando el error en mi código por horas y no lo encuentro. El navegador ni siquiera me bota error, simplemente no muestra nada cuando le doy la ruta dinámica. Al parecer no está tomando en cuenta el contenido del segundo template con la condicional del id. Probé borrandolo y colocando la condicional en el prier template y borrando el otro, con eso si compila pero no muestra los datos de la api, no se que más hacer. Anexo mi código, ojalá alguien pueda ayudarme a encontrar el error 😥.

<template>
  <div class="flex-col">
    <template v-if="asset.id">
      <div class="flex flex-col sm:flex-row justify-around items-center">
        <div class="flex flex-col items-center">
         <!--  <img
            :src="
              `https://static.coincap.io/assets/icons/${asset.symbol.toLowerCase()}@2x.png`
            "
            :alt="asset.name"
            class="w-20 h-20 mr-5"
          /> -->
          <h1 class="text-5xl">
            {{ asset.name }}
            <small class="sm:mr-2 text-gray-500">{{ asset.symbol }}</small>
          </h1>
        </div>

        <div class="my-10 flex flex-col">
          <ul>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Ranking</b>
              <span>#{{ asset.rank }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio actual</b>
              <span>{{ dollarFilter(asset.priceUsd) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más bajo</b>
              <span>{{ dollarFilter(min) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más alto</b>
              <span>{{ dollarFilter(max) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio Promedio</b>
              <span>{{ dollarFilter(avg) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Variación 24hs</b>
              <span>{{ percentFilter(asset.changePercent24Hr) }}</span>
            </li>
          </ul>
        </div>

        <div class="my-10 sm:mt-0 flex flex-col justify-center text-center">
          <button
            class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
          >Cambiar</button>

          <div class="flex flex-row my-5">
            <label class="w-full" for="convertValue">
              <input
                id="convertValue"
                type="number"
                class="text-center bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
              />
            </label>
          </div>

          <span class="text-xl"></span>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
import api from '@/api'
import { dollarFilter, percentFilter } from '@/filter'

export default {
  name: 'CoinDetail',

  data() {
    return {
      asset: {},
      history: []
    }
  },

  setup() {
      return {
          dollarFilter,
          percentFilter
      }
  },

  computed: {
    min() {
      return Math.min(
        ...this.history.map(h => parseFloat(h.priceUsd).toFixed(2))
      )
    },

    max() {
      return Math.max(
        ...this.history.map(h => parseFloat(h.priceUsd).toFixed(2))
      )
    },

    avg() {
      return this.history.reduce((a,b) => a + parseFloat(b.priceUsd), 0) / this.history.length
    }
  },

  created() {
    this.getCoin()
  },

  methods: {
    getCoin() {
      const id = this.$route.params.id;

      Promise.all([api.getAsset(id), api.getAssetHistory(id)]).then(
        ([asset, history]) => {
          this.asset = asset
          this.history = history
        }
      )
    }
  }
}
</script>

<style scoped>
td {
  padding: 10px;
  text-align: center;
}
</style>
``

Con async y await

//CoinDetail.vue
 created() {
    this.getCoin();
  },

  methods: {
    async getCoin() {
      const id = this.$route.params.id;
      let [asset, history] = await Promise.all([
        getAsset(id),
        getAssetHistory(id),
      ]);
      this.asset = asset;
      this.history = history;
    },
  },
};

//api.js
const url = "https://api.coincap.io/v2";

const getAssets = async () => {
  try {
    const req = await fetch(`${url}/assets?limit=20`);
    const res = await req.json();
    return await res.data;
  } catch (error) {
    console.error(error);
    throw new Error(error);
  }
};

const getAsset = async (coin) => {
  try {
    const req = await fetch(`${url}/assets/${coin}`);
    const res = await req.json();
    return await res.data;
  } catch (error) {
    console.error(error);
    throw new Error(error);
  }
};

const getAssetHistory = async (coin) => {
  try {
    const now = new Date();
    const end = now.getTime();
    now.setDate(now.getDate() - 1);
    const start = now.getTime();
    const req = await fetch(
      `${url}/assets/${coin}/history?interval=h1&start=${start}&end=${end}`
    );
    const res = await req.json();
    return await res.data;
    
  } catch (error) {
    console.log(error);
    throw new Error(error);
  }
};

export { getAssets, getAsset, getAssetHistory };

Quiza lo hagais luego, pero se podria agregar un 404 en caso de que la api responda igual, dado que estuve poniendo Bitcoin en lugar de bitcoin y la página no funcionaba. Gran curso!

tengo una duda…
estas funciones no me funcionaban bien, hasta que agregue los “…” antes del this

return Math.abs(
        ...this.history.map(h => parseFloat(h.priceUsd).toFixed(2))
      )

es por algo en especial??? 🤨

Math.abs es para valor absoluto. Javascript no calcula el promedio de forma nativa del objeto Math.

Si les paso como yo que tenian dudas con la forma en que calculaban los valores en las propiedades computadas, la funcion Math.min y otras aceptan cualquier cantidad de parametros, por tanto lo que hiso el profe es en vez de iterar el arreglo primero paso todos los valores del array como parametros usando el spread operator, si necesitan ahondar mas en los spread operators dejo un video corto que explica: aqui

Codigo HTML y CSS si alguien no quiere hacerlo con tailwind\<template>    \
        \<template v-if="asset.id">            \
                \
                    \                    \

                    \<small class="subtitle">\</small>                    \

                \
                \
                    \
                        \
  •                         \Ranking\                        \\                    \
  •                     \
  •                         \Precio actual\                        \\                    \
  •                     \
  •                         \Precio más bajo\                        \\                    \
  •                     \
  •                         \Precio más alto\                        \\                    \
  •                     \
  •                         \Precio Promedio\                        \\                    \
  •                     \
  •                         \Variación 24hs\                        \\                    \
  •                     \
                \
                \
                    \<button class="change-button">                    Cambiar                    \</button>                    \
                    \<label for="convertValue">                        \<input                        id="convertValue"                        type="number"                        class="input-field"                        />                    \</label>                    \
                    \\                \
            \
        \</template>    \
\</template> \<script> import api from '@/api' export default {    name:'CoinDetail',     data() {        return {            asset: {}        }    },     created() {        this.getCoin()    },     methods: {        getCoin () {            const id = this.$route.params.id            api.getAsset(id)                .then(asset => this.asset = asset)        }    }} \</script> \<style scoped>     .container {        display: flex;        flex-direction: column;        margin-top: 40px;    }     .content-wrapper {        display: flex;        flex-direction: row;        align-items: center;        justify-content: space-around;        text-align: center;    }     .image-section {    display: flex;    flex-direction: column;    align-items: center;    margin-bottom: 20px;    }     .avatar {    width: 80px;    height: 80px;    margin-right: 20px;    }     h1 {    font-size: 3rem;    }     .subtitle {    margin-right: 10px;    color: #6b7280; /\* Gray color \*/    }     .info-section {    margin: 20px 0;    }     .info-list {    list-style: none;    padding: 0;    margin: 0;    }     .info-list li {    display: flex;    justify-content: space-between;    margin-bottom: 10px;    text-transform: uppercase;    color: #6b7280; /\* Gray color \*/    }     .action-section {    margin-top: 20px;    text-align: center;    }     .change-button {    background-color: #38a169; /\* Green \*/    color: white;    font-weight: bold;    padding: 10px 20px;    border-radius: 8px;    cursor: pointer;    border: none;    transition: background-color 0.3s;    }     .change-button:hover {    background-color: #2f855a; /\* Darker green \*/    }     .input-section {    margin-top: 20px;    }     .input-field {    text-align: center;    background-color: white;    border: 1px solid #d1d5db; /\* Light gray \*/    border-radius: 8px;    padding: 10px;    width: 100%;    box-sizing: border-box;    }     .result-text {    font-size: 1.25rem;    margin-top: 20px;    }\</style> ```js <template>
<template v-if="asset.id">

<small class="subtitle"></small>

  • Ranking
  • Precio actual
  • Precio más bajo
  • Precio más alto
  • Precio Promedio
  • Variación 24hs
<button class="change-button"> Cambiar </button>
<label for="convertValue"> <input id="convertValue" type="number" class="input-field" /> </label>
</template>
</template> <script> import api from '@/api' export default { name:'CoinDetail', data() { return { asset: {} } }, created() { this.getCoin() }, methods: { getCoin () { const id = this.$route.params.id api.getAsset(id) .then(asset => this.asset = asset) } } } </script> <style scoped> .container { display: flex; flex-direction: column; margin-top: 40px; } .content-wrapper { display: flex; flex-direction: row; align-items: center; justify-content: space-around; text-align: center; } .image-section { display: flex; flex-direction: column; align-items: center; margin-bottom: 20px; } .avatar { width: 80px; height: 80px; margin-right: 20px; } h1 { font-size: 3rem; } .subtitle { margin-right: 10px; color: #6b7280; /* Gray color */ } .info-section { margin: 20px 0; } .info-list { list-style: none; padding: 0; margin: 0; } .info-list li { display: flex; justify-content: space-between; margin-bottom: 10px; text-transform: uppercase; color: #6b7280; /* Gray color */ } .action-section { margin-top: 20px; text-align: center; } .change-button { background-color: #38a169; /* Green */ color: white; font-weight: bold; padding: 10px 20px; border-radius: 8px; cursor: pointer; border: none; transition: background-color 0.3s; } .change-button:hover { background-color: #2f855a; /* Darker green */ } .input-section { margin-top: 20px; } .input-field { text-align: center; background-color: white; border: 1px solid #d1d5db; /* Light gray */ border-radius: 8px; padding: 10px; width: 100%; box-sizing: border-box; } .result-text { font-size: 1.25rem; margin-top: 20px; } </style> ```
Codigo HTML y CSS, si alguien no lo quiere hacer con tailwind \<template>    \
        \<template v-if="asset.id">            \
                \
                    \                    \

                    \<small class="subtitle">\</small>                    \

                \
                \
                    \
                        \
  •                         \Ranking\                        \\                    \
  •                     \
  •                         \Precio actual\                        \\                    \
  •                     \
  •                         \Precio más bajo\                        \\                    \
  •                     \
  •                         \Precio más alto\                        \\                    \
  •                     \
  •                         \Precio Promedio\                        \\                    \
  •                     \
  •                         \Variación 24hs\                        \\                    \
  •                     \
                \
                \
                    \<button class="change-button">                    Cambiar                    \</button>                    \
                    \<label for="convertValue">                        \<input                        id="convertValue"                        type="number"                        class="input-field"                        />                    \</label>                    \
                    \\                \
            \
        \</template>    \
\</template> \<script> import api from '@/api' export default {    name:'CoinDetail',     data() {        return {            asset: {}        }    },     created() {        this.getCoin()    },     methods: {        getCoin () {            const id = this.$route.params.id            api.getAsset(id)                .then(asset => this.asset = asset)        }    }} \</script> \<style scoped>     .container {        display: flex;        flex-direction: column;        margin-top: 40px;    }     .content-wrapper {        display: flex;        flex-direction: row;        align-items: center;        justify-content: space-around;        text-align: center;    }     .image-section {    display: flex;    flex-direction: column;    align-items: center;    margin-bottom: 20px;    }     .avatar {    width: 80px;    height: 80px;    margin-right: 20px;    }     h1 {    font-size: 3rem;    }     .subtitle {    margin-right: 10px;    color: #6b7280; /\* Gray color \*/    }     .info-section {    margin: 20px 0;    }     .info-list {    list-style: none;    padding: 0;    margin: 0;    }     .info-list li {    display: flex;    justify-content: space-between;    margin-bottom: 10px;    text-transform: uppercase;    color: #6b7280; /\* Gray color \*/    }     .action-section {    margin-top: 20px;    text-align: center;    }     .change-button {    background-color: #38a169; /\* Green \*/    color: white;    font-weight: bold;    padding: 10px 20px;    border-radius: 8px;    cursor: pointer;    border: none;    transition: background-color 0.3s;    }     .change-button:hover {    background-color: #2f855a; /\* Darker green \*/    }     .input-section {    margin-top: 20px;    }     .input-field {    text-align: center;    background-color: white;    border: 1px solid #d1d5db; /\* Light gray \*/    border-radius: 8px;    padding: 10px;    width: 100%;    box-sizing: border-box;    }     .result-text {    font-size: 1.25rem;    margin-top: 20px;    }\</style>

Tengo una queja. Durante el curso copia y pega el codigo HTML, el cual despues modifica para agregarle la funcionalidad, hasta aquí bien me parece perfecto esto ya que estamos aprendiendo VUE. Pero despues dice “el códugo se los dejo”, pero ese códugo cuando lo descargo ya me trae las modificaciones del video, no pudo haber subido solo los HTML y no todo el proyecto a la sección de recursos? Digo para que podamos seguir el ejercicio, si me genera molestia esto, se que igual el profesor no se dio cuenta hacia el ejercicio y solo subia el proyecto pero ps para que me subes todo el proyecto, solo ponme lo necesario.

api.js

const url = 'https://api.coincap.io/v2'

const getAssets = async () => {
  try {
    const response = await fetch(`${url}/assets?limit=30`)
    const { data } = await response.json()
    return data
  } catch (error) {
    console.error(`¡Ocurrió un error con el API!: ${error}`)
  }
}

const getAsset = async (coin) => {
  try {
    const response = await fetch(`${url}/assets/${coin}`)
    const { data } = await response.json()
    return data
  } catch (error) {
    console.error(`¡Ocurrió un error en la cripto detail!: ${error}`)
    // $router.push('404')
  }
}

const getAssetHistory = async (coin) => {
  
  const now = new Date()
  const end = now.getTime()
  now.setDate(now.getDate() - 1)
  const start = now.getTime()
  try {

    const response = await fetch(
      `${url}/assets/${coin}/history?interval=h1&start=${start}&end=${end}`
    )
    const { data } = await response.json()
    return data
  } catch (e) {
    console.error(`Hubo un error en la Historia: ${e}`)
  }
}

export default {
  getAssets,
  getAsset,
  getAssetHistory,
}

CoinDetail.vue

<template>
  <div class="flex-col">
    <template v-if="asset.id">
      <div class="flex flex-col sm:flex-row justify-around items-center">
        <div class="flex flex-col items-center">
          <img
            :src="`https://static.coincap.io/assets/icons/${asset.symbol.toLowerCase()}@2x.png`"
            :alt="asset.name"
            class="w-20 h-20 mr-5"
          />
          <h1 class="text-5xl">
            {{ asset.name }}
            <small class="sm:mr-2 text-gray-500">{{ asset.symbol }}</small>
          </h1>
        </div>

        <div class="my-10 flex flex-col">
          <ul>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Ranking</b>
              <span>#{{ asset.rank }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio actual</b>
              <span>{{ dollarFilter(asset.priceUsd) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más bajo</b>
              <span>{{ dollarFilter(min) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más alto</b>
              <span>{{ dollarFilter(max) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio Promedio</b>
              <span>{{ dollarFilter(avg) }}</span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Variación 24hs</b>
              <span>{{ percentFilter(asset.changePercent24Hr) }}</span>
            </li>
          </ul>
        </div>

        <div class="my-10 sm:mt-0 flex flex-col justify-center text-center">
          <button
            class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
          >
            Cambiar
          </button>

          <div class="flex flex-row my-5">
            <label class="w-full" for="convertValue">
              <input
                id="convertValue"
                type="number"
                class="text-center bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
              />
            </label>
          </div>

          <span class="text-xl"></span>
        </div>
      </div>
    </template>
  </div>
</template>

<script>
import api from '../api'
import { dollarFilter, percentFilter } from '../filter'

export default {
  name: 'CoinDetail',

  data() {
    return {
      asset: {},
      history: [],
    }
  },

  setup() {
    return {
      dollarFilter,
      percentFilter,
    }
  },

  computed: {
    min() {
      return Math.min(
        ...this.history.map((h) => parseFloat(h.priceUsd).toFixed(2))
      )
    },

    max() {
      return Math.max(
        ...this.history.map((h) => parseFloat(h.priceUsd).toFixed(2))
      )
    },

    avg() {
      return this.history.reduce((a, b) => a + parseFloat(b.priceUsd), 0) / this.history.length
    },
  },

  created() {
    this.getCoin()
  },

  methods: {
    getCoin() {
      const id = this.$route.params.id

      Promise.all([api.getAsset(id), api.getAssetHistory(id)]).then(
        ([asset, history]) => {
          this.asset = asset
          this.history = history
        }
      )
    },
  },
}
</script>

<style scoped>
td {
  padding: 10px;
  text-align: center;
}
</style>

Date: método getTime()

El método getTime() devuelve el valor numérico correspondiente a la hora para la fecha especificada según la hora universal.

Puede utilizar este método para ayudar a asignar una fecha y hora a otro objeto Date. Este método es funcionalmente equivalente al metodo valueOf() (en-US).
Ejemplo:

dateObj.getTime()

Valor Devuelto

El valor devuelto por el método getTime() es un número de milisegundos desde el 1 de enero de 1970 00:00:00 UTC.
Documentación: https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Date/getTime

en las funciones maximo y minimo siempre tengo 0, alguien sabe que puede pasar?

A ver, primero que todo excelente curso y profesor. Segundo.

Que son esos “…” antes del Math.min o Math.max.

Im like wtf?? no me funcionaba el codigo pero con “…”

👌

Hola 😄
¿Alguien podría ayudarme? Me da este error que no puedo solucionar 😦

Tengo mi aplicación funcionando correctamente, pero al correrla me salen estos cinco errores

Será normal o tengo algo incorrecto?

para obtener el id de la url, al ser parte de la url es parte del route, al ser parte del router existe una funcionalidad especifica que se llama $route dentro de la instancia que permite acceder a este tipo de valores, escribiendo simplemente this.$route.params.id

Este es el codigo HTML limpio del minuto 3:00 de este video

[(https://gist.github.com/robertrm0/28e430efd4de6708439037c56e705930)

soluciona si pongo una moneda no existente como evito el console log de error 404

Hay un error en avg. Haces Math.abs y esa función saca el valor absoluto no el promedio.

Error

  • @/views/CoinDetail in ./src/router.js

To install it, you can run: npm install --save @/views/CoinDetail

Si alguien tiene errores al copiar solo CoinDetail.vue es por que faltan funciones en api.js aunque en la consola tambien lo dice

Cuál es la diferencia en el que el vscode me sugiera cambiar la función getAsset a esta nueva forma:

async function getAsset(coin) {
  const res = await fetch(`${url}/assets/${coin}`)
  const res_1 = await res.json()
  return res_1.data
}

Entonces template es como un fragment

Pude solucionar un error al copiar el archivo CoinDetail
este:

escribiendo esto en la terminal:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

cat /proc/sys/fs/inotify/max_user_watches

fs.inotify.max_user_watches=524288

Vue.js3, Si a alguno les aparece este error al inspeccionar (dado que al hacer routerlink para devolverse a la pagina principal quedaban las dos paginas una encima de la otra).
hay les dejo la solución.

<code> 
Uncaught (in promise) TypeError: Cannot read property 'toLowerCase' of undefined

Espero les sirva

<code> 
 <img
            :src="`https://static.coincap.io/assets/icons/${
              asset.symbol && asset.symbol.toLowerCase()
            }@2x.png`"
            :alt="asset.name"
            class="w-20 h-20 mr-5"
          />

Si desean saber cual es la versión de Vue que usan, puede agregar estas líneas de código en tu archivo api.js :

import Vue from "vue";
alert(`Vue version : ${Vue.version}`);

Muy buena la clase, si hay algo que no entienden, busquen en MDN, el programa es una chulada, el profe es una pistola.

profe usted es el mejor este para mi fue el video mas duro de todos y que me toco repasar bastante JavaScript pero después todo tenia sentido muchas gracias

les recomiendo repasen un poco de promeses y funciones math

Se puede extraer las propiedades del parámetro en los params usando object destructuring de esta manera:

const { id } = this.$route.params

  1. crear CoinDetail.vue
  2. en route crear una ruta dinamica
  3. en api js hacer la function getAsset(coin) tambien getAssetHistory
  4. en CoinDetail hacer las operaciones

¡Qué excelente las rutas dinámicas! Aportan una funcionalidad que puede ser utilizada en todos los sistemas.

Súper difícil esta clase

<template>
  <div class="flex-col">
    <template>
      <div class="flex flex-col sm:flex-row justify-around items-center">
        <div class="flex flex-col items-center">
          <img class="w-20 h-20 mr-5" />
          <h1 class="text-5xl">
            <small class="sm:mr-2 text-gray-500"></small>
          </h1>
        </div>

        <div class="my-10 flex flex-col">
          <ul>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Ranking</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio actual</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más bajo</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio más alto</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Precio Promedio</b>
              <span></span>
            </li>
            <li class="flex justify-between">
              <b class="text-gray-600 mr-10 uppercase">Variación 24hs</b>
              <span></span>
            </li>
          </ul>
        </div>

        <div class="my-10 sm:mt-0 flex flex-col justify-center text-center">
          <button
            class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded"
          >
            Cambiar
          </button>

          <div class="flex flex-row my-5">
            <label class="w-full" for="convertValue">
              <input
                id="convertValue"
                type="number"
                class="text-center bg-white focus:outline-none focus:shadow-outline border border-gray-300 rounded-lg py-2 px-4 block w-full appearance-none leading-normal"
              />
            </label>
          </div>

          <span class="text-xl"></span>
        </div>
      </div>
    </template>
  </div>
</template>

Les comparto mi version de la funcion getAsset() utilizando async await y arrow function

const getAsset = async (coin) => {
  try {
    const rawData = await fetch(`${baseUrl}/assets/${coin}`);
    const jsonData = await rawData.json();
    return jsonData.data;
  } catch (error) {
    console.error("The api request was not succesful");
  }
};

Tengo un error con las propiedades computadas, min(), max() me están devolviendo un valor “(error during evaluation)”

Estuve más de 1 hora buscando por que no me funcionaba mi código vi 3 veces el video hasta que en la tercera ocasión vi que en la parte de .then(res => res.json) debía de ponerle al json los () por que es una función .-.

Me aparece el siguiente error, copio el archivo CoinDetail.vue de este repositorio a mi carpeta views pero la consola me muestra esto:

<code>

vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in created hook: “TypeError: api__WEBPACK_IMPORTED_MODULE_7_.default.getAsset is not a function”

found in

—> <CoinDetail> at src/views/CoinDetail.vue
<App> at src/App.vue
<Root>
warn @ vue.runtime.esm.js?2b0e:619
logError @ vue.runtime.esm.js?2b0e:1884
globalHandleError @ vue.runtime.esm.js?2b0e:1879
handleError @ vue.runtime.esm.js?2b0e:1839
invokeWithErrorHandling @ vue.runtime.esm.js?2b0e:1862
callHook @ vue.runtime.esm.js?2b0e:4219
Vue._init @ vue.runtime.esm.js?2b0e:5008
VueComponent @ vue.runtime.esm.js?2b0e:5154
createComponentInstanceForVnode @ vue.runtime.esm.js?2b0e:3283
init @ vue.runtime.esm.js?2b0e:3114
merged @ vue.runtime.esm.js?2b0e:3301
createComponent @ vue.runtime.esm.js?2b0e:5978
createElm @ vue.runtime.esm.js?2b0e:5925
createChildren @ vue.runtime.esm.js?2b0e:6053
createElm @ vue.runtime.esm.js?2b0e:5954
patch @ vue.runtime.esm.js?2b0e:6477
Vue._update @ vue.runtime.esm.js?2b0e:3945
updateComponent @ vue.runtime.esm.js?2b0e:4066
get @ vue.runtime.esm.js?2b0e:4479
Watcher @ vue.runtime.esm.js?2b0e:4468
mountComponent @ vue.runtime.esm.js?2b0e:4073
Vue.$mount @ vue.runtime.esm.js?2b0e:8415
init @ vue.runtime.esm.js?2b0e:3118
createComponent @ vue.runtime.esm.js?2b0e:5978
createElm @ vue.runtime.esm.js?2b0e:5925
patch @ vue.runtime.esm.js?2b0e:6516
Vue.update @ vue.runtime.esm.js?2b0e:3945
updateComponent @ vue.runtime.esm.js?2b0e:4066
get @ vue.runtime.esm.js?2b0e:4479
Watcher @ vue.runtime.esm.js?2b0e:4468
mountComponent @ vue.runtime.esm.js?2b0e:4073
Vue.$mount @ vue.runtime.esm.js?2b0e:8415
eval @ main.js?56d7:13
./src/main.js @ app.js:1373
webpack_require @ app.js:849
fn @ app.js:151
1 @ app.js:1530
webpack_require @ app.js:849
checkDeferredModules @ app.js:46
(anónimo) @ app.js:925
(anónimo) @ app.js:928
Mostrar 11 fotogramas más del código de biblioteca
vue.runtime.esm.js?2b0e:1888 TypeError: api__WEBPACK_IMPORTED_MODULE_7
.default.getAsset is not a function
at VueComponent.getCoin (CoinDetail.vue?264d:123)
at VueComponent.created (CoinDetail.vue?264d:115)
at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
at callHook (vue.runtime.esm.js?2b0e:4219)
at VueComponent.Vue._init (vue.runtime.esm.js?2b0e:5008)
at new VueComponent (vue.runtime.esm.js?2b0e:5154)
at createComponentInstanceForVnode (vue.runtime.esm.js?2b0e:3283)
at init (vue.runtime.esm.js?2b0e:3114)
at merged (vue.runtime.esm.js?2b0e:3301)
at createComponent (vue.runtime.esm.js?2b0e:5978)