Me gustaria que fuera mas practico, ir estructurando en orden el codigo y explicar que se hace en cada seccion.
Por ejemplo: con esta funcion guardamos el path de la url actual y en ese momento hacer una prueba imprimiendo la ruta por consola.
Introducción y bienvenida al curso
Aviso importante: este curso se dará de baja dentro de un mes
Lo que aprenderás para crear Router para SPA
La lógica detrás de nuestro enrutador
Conceptos de SPA Routing y nuestro primer servidor
Desglose del proyecto del curso y explicación del SPA Routing
Implementando routing del lado del cliente
Creando una aplicación sencilla en html
Creando el servidor de nuestra app
Creando nuestro archivo de rutas
Lógica de nuestro ruteador
Creación de la función loadInitialRoute
Haciendo match entre la URL y una ruta
Creando la función load routes
Actualizando nuestro index.html para agregar la nueva funcionalidad de routing
Cierre del curso y conclusiones
Cierre del curso y conclusiones
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Convierte tus certificados en títulos universitarios en USA
Antes: $249
Paga en 4 cuotas sin intereses
Termina en:
Aportes 38
Preguntas 8
Me gustaria que fuera mas practico, ir estructurando en orden el codigo y explicar que se hace en cada seccion.
Por ejemplo: con esta funcion guardamos el path de la url actual y en ese momento hacer una prueba imprimiendo la ruta por consola.
Tenía un problema, se me generaba el error “Uncaught TypeError: Cannot read property ‘template’ of undefined”. Esto se generaba porque se cargaba la ruta “localhost:8000/index.html”, la cual no estaba en nuestro archivo routes.js, entonces hay 2 maneras de solucionarlo, o agregas esa ruta a routes.js o simplemente borras el “index.html” de la url y tu página cargará sin problemas.
Realmente no me funcionó como el profe lo hizo. Aquí va mi solución, espero les sirva:
Me molesta como en las clases de programación empiezan a escribir código y hay que seguirlo, se saltan por completo la lógica. El método de Freddy con la tablet donde primero explica TOOOOOODOOOO lo que pasa en el programa y luego empieza a codear es perfecto, no se porque no se lo exigen a todos los profesores, sobre todos los de js…que insisto…empiezan a agregar código y por alguna razón piensan que los que estamos aprendiendo les podemos seguir el ritmo.
La solución creo que esta un poco rebuscada, les comparto mi solución
Path.js
const PATHS = {
home: {
path: "/",
template: "<h1>Home 🏠</h1>",
},
about: {
path: "/about",
template: "<h1>Sobre mi 👨🏻💻</h1>",
},
contact: {
path: "/contact",
template: "<h1>Contacto 📲</h1>",
},
error: {
path: "/",
template: "<h1>Error ⛔️</h1>",
},
}
Router.js
class Router {
/**
* Metodo inicial.
*
* @return {void}.
*/
constructor(paths) {
this.paths = paths;
this.initRouter();
}
/**
* Permite inicializar el router
*
* @return {void}.
*/
initRouter() {
const { location: { pathname = "/" } } = window;
const URI = pathname === "/" ? "home" : pathname.replace("/", "");
this.load(URI);
}
/**
* Permite iniciar la carga de paginas.
*
* @return {void}.
*/
load(page = "home") {
const { paths } = this;
const { path, template } = paths[page] || paths.error;
const $CONTAINER = document.querySelector("#content");
$CONTAINER.innerHTML = template;
window.history.pushState({ }, "Genial", path);
}
}
const ROUTER = new Router(PATHS);
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Manejo de Routers</title>
</head>
<body>
<header>
<nav>
<ul>
<li>
<button id="Home" onclick="ROUTER.load('home')">Home</button>
</li>
<li>
<button id="About" onclick="ROUTER.load('about')">About</button>
</li>
<li>
<button id="Contact" onclick="ROUTER.load('contact')">Contact</button>
</li>
</ul>
</nav>
</header>
<section>
<div id="content">
</div>
</section>
<footer></footer>
<script src="./Paths.js"></script>
<script src="./Router.js"></script>
</body>
</html>
Con esa explicación del final me quedó más claro lo que estábamos haciendo! 😄
mi ejemplo de lo aprendido en este curso yo agregue botones con class de **bootstrap **
Yepaaaaa!!! al fin se probara el código, vaya mas mierda de profe
Hola compañeros, me pasó que si escribo una ruta que no existe, el programa se rompe, para resolver el error, agregué en la función _matchUrlToRoute una condición antes del return que devuelve un objeto con solo la propiedad template
if (matchedRoute === undefined) {
return {
template: '<h1>Not found</h1>'
};
}
return matchedRoute;
Me ha gustado mucho el curso y creo que es bastante útil: Felicito al profesor por su entusiasmo y la materia aportada pero creo que debería haberlo preparado mejor. Por ejemplo, ir probando resulatdos parciales y no esperar hasta el final para que todo funcione con el último return. Algunas explicaciones también dejan algo que desear. Espero que se tome mi comentario como una critica constructiva alejada por supuesto de la intencionalidad de comunuicar una verdad absoluta y de nuevo le doy las gracias al profesor por haberme ayudaddo a aprender. Pero Platzi no es cualquier academia sino la puerta abierta del conocimiento digital a muchísima gente. Si entre todos conseguimos que sea aún mejor de lo que es, nos beneficiaremnos por partida boble o tal vez triple.
Saludos
Vicent
Un gran curso la verdad, me gusta la lógica con la que el profesor trabaja y aprendí nuevos conceptos de JS. Me funcionó todo perfecto, acá les dejo mi código.
index.html
routes.js
router.js
index.js
He creado mi propia solucion porque no me iba el evento onclick
<body>
<header>
<ul>
<li>
<button data-router="">Home</button>
</li>
<li>
<button data-router="portfolio">PorFolio</button>
</li>
<li>
<button data-router="aboutme">About Me</button>
</li>
<li>
<button data-router="contact">Contact</button>
</li>
</ul>
</header>
<div id="root"></div>
<script src="./index.js" type="module"></script>
</body>
const routerOutElm = document.querySelector("#root");
import Router from "./router.js";
import { routes } from "./routes.js";
/* Seleccionamos los botones */
const btnListRoute = [...document.querySelectorAll("[data-router]")];
/* Genero eventos Cliks del menu de navegación */
btnListRoute.forEach((btn) => {
btn.addEventListener("click", () => {
router.loadRoute(btn.dataset.router);
});
});
/* Declaro la instancia de Router */
const router = new Router(routes);```
Resultado de la ventana Ayuda del curso.
Si les sirve. Me tope con el error “router is not defined”, y me encontré con que el error o mi error se estaba en el onclick, que utilizábamos en html.
En lugar de eso lo hice de esta forma.
se olvido de dejar el codigo para poder amalizarlo mejor
Tengo aun este error, ya trate de debuggear pero no encontre como solucionarlo, compañeros en esta clase llegaron a tener el mismo problema, uso VS con Live Server
router.js:14 Uncaught TypeError: Cannot read property 'template' of undefined
at Route.loadRoute (router.js:14)
at Route._loadInitialRoute (router.js:36)
at new Route (router.js:4)
at index.js:1
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Curso/Taller SPA</title>
</head>
<body>
<header>
<ul>
<li><Button onclick="router.loadRoute('')">Home</Button></li>
<li><Button onclick="router.loadRoute('contact')">Contacto</Button></li>
<li><Button onclick="router.loadRoute('about')">Acerca de mi</Button></li>
</ul>
</header>
<div data-router>
<!-- Builder -->
</div>
<script src="router.js"></script>
<script src="routes.js"></script>
<script src="index.js"></script>
</body>
</html>
const routes = [
{
path: '/',
template: '<h1>Hola</h1>'
},
{
path: '/contact',
template: '<h1>Contacto</h1>'
},
{
path: '/about',
template: '<h1>Acerca de mi</h1>'
},
{
path: '/index.html',
template: '<h1>Home</h1>'
}
];
class Route {
constructor(routes) {
this.routes = routes;
this._loadInitialRoute();
}
loadRoute(...urlSegs) {
const matchedRoute = this._matchUrlToRoute(urlSegs);
// debugger
const url = `/${urlSegs.join('/')}`;
history.pushState({}, 'This works', url);
// debugger
const routerOutElm = document.querySelectorAll('[data-router]')[0];
routerOutElm.innerHTML = matchedRoute.template;
}
_matchUrlToRoute(urlSegs) {
const matchedRoute = this.routes.find(route => {
const routePathSegs = route.path.split('/').slice(0);
// debugger
if (routePathSegs.length !== urlSegs.length) {
// debugger
return false;
}
// debugger
return routePathSegs
.every((routePathSeg, i) => routePathSeg === urlSegs[i])
});
if (matchedRoute === undefined) {
return {
template: '<h1>Not found</h1>'
};
}
return matchedRoute;
}
_loadInitialRoute() {
const pathNameSplit = window.location.pathname.split('/');
const pathSegs = pathNameSplit.length > 1 ? pathNameSplit.slice(1) : '';
this.loadRoute(...pathSegs)
}
}
const router = new Route(routes);
Trate de arreglarlo con una validacion sugerida, sin embargo NO funciona
Una pequeña acotación para MEJORAR el rendimiento en su código. En el condicional donde se comparan los arrays, hay un pequeño error:
const matchedRoute = this.routes.find( (route) => {
const routePathSegs = route.path.split("/").slice(1)
// ESTE
if(routePathSegs.length !== urlSegs.length){
return false
}
return routePathSegs
.every((routePathSeg, i) => routePathSeg === urlSegs[i])
})
Acá lo que se explicó es que se querian comparar los strings de ambos arrays, tanto de routePathSegs
como urlSegs
, pero si debuggean su código y ven lo que hace paso a paso, se darán cuenta que nunca entra al condicional porque así como estaroutePathSegs.length !== urlSegs.length
Lo que hace es comparar el tamaño del array en sí y por como han sido trabajados con los métodos slice() y split() siempre tendran un length de 1, así que esa comparación no funciona.
Entonces, porque si funciona el código en general? por el metodo every(), donde se comparan que si sean iguales.
.
Para optimizar el código y entre al if cuando sean de tamaños distintos solo hace falta agregar:
if(routePathSegs[0].length !== urlSegs[0].length){
return false
}
Como les dije, siempre es un array de un elemento así que su índice siempre será 0. Con este cambio los valores diferentes entraran al condicional, retornaran false y permitirán la siguiente iteración, sin necesidad de pasar por el every()
Hola todos, espero estén bien, yo al momento de ejecutar el código tengo este error, alguien que me de una mano, se los agradecería mucho.
Para sacarle provecho a este codigo habira que crear templates en archivos externos y pasarlos en vez de escribir el template directamente en el archivo routes.js
Despues de seguir todo el curso, no me funciona. He repasado el codigo de los archivos y en consola tengo este error:
router.js:13 Uncaught TypeError: Cannot read property 'template' of undefined at Router.loadRoute (router.js:13) at Router._loadInitialRoute (router.js:36) at new Router (router.js:4) at index.js:1
Lo modifique para que tenga un template de error404 si no exite la pagina dentro de los array de routes. aqui
Este es mi aporte, corrige lo que no funcionaba y le agregue not found en caso de que la ruta no este disponible.
class Router {
constructor (routes) {
this.routes = routes;
//Para cargar ruta inicial del proyecto
this._loadInitialRoutes();
}
loadRoutes(...urlSegs){
const matchedRoute = this._matchUrlToRoute(urlSegs);
const url = `/${urlSegs.join('/')}`;
window.history.pushState({},'this word',url);
const routerOutElem = document.querySelectorAll(['#data-router'])[0];
routerOutElem.innerHTML = matchedRoute.template;
}
_matchUrlToRoute(urlSegs){
const notFound = this.urlNotFound();
const matchedRoute = this.routes.find ( route => {
const routePathSeges = route.path.split('/').slice(1);
if (routePathSeges === ''){
return '/'
}else if (routePathSeges[0] === urlSegs[0] ){
return route;
}
})
// En caso de no encontrar la ruta retorna 404
const urlMatch = matchedRoute !== undefined ? matchedRoute : notFound
return urlMatch;
}
// Busco el path 404
urlNotFound() {
const notFound = this.routes.find(route => {
return route.path ==='/404';
})
return notFound;
}
_loadInitialRoutes(){
const pathNameSplit = window.location.pathname.split('/');
const pathSegs = pathNameSplit.length > 1 ? pathNameSplit.slice(1) : '';
this.loadRoutes(...pathSegs);
}
}
Es mejor modularizar los scripts, así sólo llamas uno.
KeMonito!
Si tienen una ruta como /user/andrea/picture2 tienen que pasarle un array a router.loadRoute(), en este ejemplo sería router.loadRoute([‘user’, ‘andrea’, ‘picture2’])
tengo el siguiente error:
Uncaught ReferenceError: routes is not defined
at index.js:1
revise el código pero no encuentro lo malo
index
const router = new Router(routes);
routes
const route = [
{
path: '/',
template: '<h1>Hola</h1>'
},
{
path: '/contacto',
template: '<h1>contacto</h1>'
},
{
path: '/aboutme',
template: '<h1>About me</h1>'
}
];
router
class Router{
constructor(routes) {
this.routes= routes;
this._loadInitialRoute();
}
loadRoute(...urlSegs){
const matchedRoute = this._matchUrlToRoute(urlSegs);
const url = `/${urlSegs.join('/')}`;
history.pushState({},'this works', url);
const routerOutElm = document.querySelectorAll('[data-router]')[0];
routerOutElm.innerHTML = matchedRoute.template;
}
_matchUrlToRoute(urlSegs) {
const matchedRoute = this.routes.find(route =>{
const routePathSegs = route.path.split('/').slice(1)
if (routePathSegs.lenght !== urlSegs.leng){
return false;
}
return routePathSegs
.every((routePathSeg, i) => routePathSeg === urlSegs[i]);
});
return matchedRoute;
}
_loadInitialRoute() {
const pathNameSplit =window.location.pathname.split('/');
const pathSegs = pathNameSplit.length > 1 ? pathNameSplit.slice(1): '';
this.loadRoute(...pathSegs);
}
}
su ayuda 😒
No funca 😌
Hola, me sale el error Uncaught TypeError: matchedRoute is undefined. Ya estoy cansado de leer el código. alguien tuvo este problema?
alguien me puede explicar esta línea ?
no entiendo porque el objeto vacío y el mensaje
history.pushState({}, 'this works', url)
Hola!
Primero si tuvieron errores a la hora de cargar rutas específicas, puede ser porque están usando Live-server, en es caso deben de usar el servidor propuesto por el Ricardo! 😎
Segundo yo realicé una implementación simplificando un poco más todo, los invito a que la miren en el Repositorio
Y les comparto el código del Router
y el Index
Así quedó
¿al colocar en los scripts src=’…/’ no saldría del entorno de desarrollo donde se encuentra trabajando?, si no me equivoco hay un error allí.
Demasiados errores en el código, deberían de pasar un filtro mínimo antes de poder subir el curso. Aquí el código que me funciono usando Live Server
index.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cliente de nuestro SPA Router</title>
</head>
<body>
<header>
<ul>
<li><button onclick="router._loadRoute('index.html')">Home</button></li>
<li><button onclick="router._loadRoute('contacto')">Contacto</button></li>
<li><button onclick="router._loadRoute('aboutme')">About me</button></li>
</ul>
</header>
<div data-router>
</div>
<script src="/router.js"></script>
<script src="/routes.js"></script>
<script src="/index.js"></script>
</body>
</html>
routes.js
const routes = [{
path: '/index.html',
template: '<h1> Hola </h1>'
},
{
path: '/contacto',
template: '<h1> Contacto </h1>'
},
{
path: '/aboutme',
template: '<h1> About Me </h1>'
},
];
router.js
class Router {
constructor(routes) {
this.routes = routes;
this._loadInitialRoute();
}
_loadRoute(...urlSegs) {
const matchedRoute = this._matchUrlToRoute(urlSegs);
const url = `/${urlSegs.join('/')}`;
history.pushState({}, 'this works', url);
const routerOutElm = document.querySelectorAll('[data-router]')[0];
routerOutElm.innerHTML = matchedRoute.template;
}
_matchUrlToRoute(urlSegs) {
const matchedRoute = this.routes.find(route => {
const routePathSegs = route.path.split('/').slice(1);
if (routePathSegs.length !== urlSegs.length) {
return false;
}
return routePathSegs.every((routePathSegs, i) => routePathSegs === urlSegs[i])
});
return matchedRoute;
}
_loadInitialRoute() {
const pathNameSplit = window.location.pathname.split('/');
const pathSegs = pathNameSplit.length > 1 ? pathNameSplit.slice(1) : '';
this._loadRoute(...pathSegs);
}
}
index.js
const router = new Router(routes);
Con el error, que soluciono Ricador en el minuto 3:20, me ha funcionado. aquí mi implementación.
Index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Router</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;300;600&display=swap" rel="stylesheet">
<style>
body, * {
margin:0px;
padding: 0px;
}
body{
background: rgb(254,254,254);
background: radial-gradient(circle, rgba(254,254,254,1) 0%, rgba(227,225,225,1) 35%, rgba(228,228,228,1) 100%);
font-family: 'Montserrat', sans-serif;
position: relative;
}
header{
width: 100vw;
height: 80px;
background-color: #fed049;
}
header ul {
display: flex;
flex-wrap: nowrap;
justify-content: space-evenly;
list-style: none;
height: 100%;
align-items: center;
}
header ul li span{
font-size: 24px;
cursor: pointer;
}
section{
width: 100%;
position: relative;
}
section .wrapper{
width: 80%;
margin: 0 auto;
text-align: center;
position: relative;
padding: 5% 10%;
}
section .wrapper h1{
font-size: 40px;
}
section .wrapper p{
margin: 35px 10px 15px;
padding: 0px 15px;
}
</style>
</head>
<body>
<header>
<ul>
<li class="home"><span>Home</span></li>
<li class="aboutus"><span>About Us</span></li>
<li class="contactus"><span>Contact Us</span></li>
</ul>
</header>
<section>
<div class="wrapper">
<h1> Welcome goide :) </h1>
<h2> Make a Router with JavaScript Vanilla.</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sed justo in metus imperdiet interdum. Aliquam vestibulum in est vitae semper. In ornare rhoncus mi, a malesuada enim eleifend ac. Duis non elit eros. Maecenas fringilla semper lorem, et interdum nibh blandit sit amet. Ut quam nisi, elementum eget maximus a, pretium hendrerit quam. Integer congue congue turpis eget suscipit. Sed a sollicitudin est, malesuada varius erat. Aliquam tincidunt nisl id nibh placerat, auctor auctor odio tincidunt. Proin commodo aliquam libero mollis lacinia.
</p>
</div>
</section>
<script src="/src/router.js"></script>
<script src="/src/routes.js"></script>
<script src="/src/index.js"></script>
</body>
</html>
Routes.js
const routes = [
{
path: '/',
template: `Welcome goide :)`
},
{
path: '/about-us',
template: `About Us`
},
{
path: '/contact-us',
template: `Contact Us`
}
];
Router.js
class Router{
constructor(routes){
this.routes = routes;
this._loadInitialRoutes();
}
loadRoute(...urlSegs){
const matchedRoute = this._matchUrlToRoute(urlSegs);
const url = `/${urlSegs.join('/')}`;
window.history.pushState({}, 'this works', url);
const routerOutElm = document.querySelector('h1');
routerOutElm.innerHTML = matchedRoute.template;
}
_matchUrlToRoute(urlSegs){
const matchedRoute = this.routes.find( route => {
const routePathSegs = route.path.split('/').slice(1);
if(routePathSegs.length !== urlSegs.length){
return false;
}
// El emtodo every nos permite, Determina si todos los elementos en el array satisfacen una condición.
// o sea verifica cada elemento del array que todos sean true, en este caso es una letra, las verifica letra por letra
return routePathSegs.every((routePathSeg, i) => routePathSeg === urlSegs[i]);
});
return matchedRoute;
}
_loadInitialRoutes(){
// vamos a usar el metodo split, El método split() divide un objeto de tipo String en un array (vector) de cadenas mediante la separación de la cadena en subcadenas.
// en este caso: split('/') -> nos separa todo lo de la parte derecha luego del slash de la url
const pathNameSplit = window.location.pathname.split('/');
// Segmentar nuestro path
// el metdo slice, devuelve una copia de una parte del array dentro de un nuevo array empezando por inicio hasta fin (fin no incluido).
const pathSegs = (pathNameSplit.length > 1) ? pathNameSplit.slice(1) : '';
this.loadRoute(...pathSegs);
}
}
Index.js
const router = new Router(routes);
const pathHome = () => {
console.log('Home')
router.loadRoute('');
}
const pathAbout = () => {
console.log('About - Us ')
router.loadRoute('about-us');
}
const pathContact = () => {
console.log('Contact - Us ')
router.loadRoute('contact-us');
}
const home = document.querySelector('.home');
home.addEventListener('click', pathHome);
const about = document.querySelector('.aboutus');
about.addEventListener('click', pathAbout);
const contact = document.querySelector('.contactus');
contact.addEventListener('click', pathContact)
Podemos agregar una pagina 404 si en el return de _matchUrlToRoute agregamos esto
return matchedRoute ? matchedRoute : this.routes[this.routes.length - 1]
y en la parte de routes agregamos al final un template de 404
platzi terminaran siendo un libro digital, te dan toda la info, pero nuevamente vuelves tu a tener que estudiar todo hasta que lo entiendas, por lo menos los creadores de contenido de youtube, se toman hasta 2 -3 horas explicando las funcionalidades de las cosas, aca nuevamente graban lo que leen de otra pantalla y listo, curso terminado, estudien ahora si.
f
Les comparto mis apuntes de todo el curso por si se pierden en algún momento: https://app.gitbook.com/@afgallegoz/s/creacion-de-router-spa/
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?