Agrego mi solucion al reto, se agrego la funcion eliminar y agregar
Link de github pages
Link del repositorio
_PD. Mi diseño actualmente solo esta optimizado para movil _
Introducción
¿Qué es Vue.js?
Fundamentos de componentes
Configuración del entorno
Primer contacto
Renderizado declarativo
Interpolación de datos
Interpolación de datos 2
Atributos reactivos
Input de usuario
Eventos de usuario
Inputs reactivos
Reactividad
Propiedades computadas
Watchers
Estilos reactivos
Listas y condicionales
Condicionales
Listas
Componentes personalizados
Componentes
Slots
Comunicación entre componentes
Comunicación de componente padre a hijo
Comunicación de componente hijo a padre
Custom v-model
Comunicación con componentes profundos
Componentes en el Virtual DOM
Instancias de componentes
Cierre del curso
Vue progresivo
Esto es solo el comienzo
Aún no tienes acceso a esta clase
Crea una cuenta y continúa viendo este curso
Aportes 12
Preguntas 0
Agrego mi solucion al reto, se agrego la funcion eliminar y agregar
Link de github pages
Link del repositorio
_PD. Mi diseño actualmente solo esta optimizado para movil _
Aqui mi avance hasta el momento,
Link de github pages
Link del repositorio
He aplicado un poco de los conocimientos adquiridos en la ruta de ARquitectura de frontend para el diseño.
Utilice el v-for para el listado de los tweets, para entrar a esta seccion se le puede dar click a el boton de google, apple o siguiente
 {
return {
addPost:{
title: '',
description: '',
}
, editPost: false
, openDoor: false
, posts: [
{ title: "Post 1", description: "Description 1"}
, { title: "Post 2", description: "Description 2" }
, { title: "Post 3", description: "Description 3" }
, { title: "Post 4", description: "Description 4" }
]
, style: {
backgroundColor: '#eca1a6'
}
, text: "Close door"
, userName: ''
};
},
computed: {
openDoorComputed() {
return this.openDoor ? "Close" : "Open";
}
, styleComputed(){
return {
backgroundColor: this.openDoor ? '#b5e7a0' : '#eca1a6'
}
}
, styleComputed2(){
return this.openDoor ? 'open' : 'closed';
}
},
methods: {
AddPostToTheList(){
this.posts.push({
title: this.addPost.title
, description: this.addPost.description
});
}
, DeletePost(index, element){
// mutabilidad aqui seria para no mutar directamente el array
// let aTemp = this.posts.map(function(item, index){
// return item;
// });
// aTemp.splice(index, 1);
// this.posts = aTemp;
this.posts.splice(index, 1);
// console.log(index, element, this.posts, aTemp);
}
},
watch: {
openDoor(value, oldValue) {
if(value){
this.text = "Cierra sesión";
}
else{
this.text = "Inicia sesión";
this.userName = '';
this.editPost = false;
}
}
},
template: `
<div class="container" :class="styleComputed2">
<h2>{{ text }}{{ userName }}{{post.title}}{{post.title}}{{post.description}}</script>
<style>
html, body {
height: 100vh;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}
#appVue3, .container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
}
button {
margin-top: 24px;
border: none;
background-color: white;
padding: 8px 24px;
border-radius: 12px;
}
.addPost{
background-color: #54964a;
border: 1px solid #ccc;
padding: 7px;
}
.btnAdd {
border: none;
background-color: #ced2cf;
border-radius: 3px;
color: #16512a;
font-weight: bold;
margin-top: 3px;
padding: 1px 3px;
}
.closed {
background-color: #eca1a6;
}
.containerEdit{
display: flex;
flex-direction: row;
gap: 16px;
width: 100%;
}
.delPost{
background-color: #54964a;
border: 1px solid #ccc;
padding: 7px;
}
.open {
background-color: #b5e7a0;
}
.list {
display: flex;
flex-direction: column;
}
.item{
border: 1px solid #1c2263;
}
.itemDel {
border-bottom: 1px solid #1c2263;
font-size: 14px;
margin-bottom: 7px;
}
.menuLogin{
align-items: center;
display: flex;
flex-direction: row;
gap: 7px;
}
.title{
font-weight: bold;
font-size: 1.5rem;
}
</style>
Esta es mi solución al reto:
const vm = Vue.createApp({
data(){
return {
text : "Accede a tu cuenta",
open : false,
username : "",
posts : [
{
title: "Titulo 1",
description : "Lorem ipsum..."
},
{
title: "Titulo 2",
description : "Lorem ipsum..."
},
{
title: "Titulo 3",
description : "Lorem ipsum..."
},
{
title: "Titulo 4",
description : "Lorem ipsum..."
}
],
newPost: {
title: '',
description: ''
}
};
},
methods: {
addPost() {
const setPost = [];
if ( ( this.newPost.title != "" ) && (this.newPost.description != "") ) {
setPost['title'] = this.newPost.title;
setPost['description'] = this.newPost.description;
this.posts.push(setPost);
this.newPost.title = "";
this.newPost.description = "";
}
},
removePost(i){
this.posts.splice(i, 1);
}
},
watch : {
open(value){
if (value) {
this.text = "Cierra sesión";
} else {
this.text = "Accede a tu cuenta";
this.username = '';
}
}
},
computed: {
label(){
return this.open ? "Salir" : "Acceder";
},
styles(){
return this.open ? 'open' : 'closed';
}
},
template: `
<div class="container" :class="styles">
<h2>{{ text }}</h2>
<div v-if="open">
<p>Hola, {{ username }} </p>
<div class="new-post">
<h4 class="new-post-heading">Agregar nueva publicación</h4>
<input type="text" placeholder="Título de la publicación" class="new-post-title" v-model="newPost.title">
<textarea placeholder="Descripción" v-model="newPost.description"></textarea>
<button class="new-post-publish" @click="addPost">Publicar</button>
</div>
<div class="list">
<div v-for="(item, i) in posts" :key="i" class="item">
<h4 class="title">{{ item.title }} <a href="javascript:void(0)" class="item-remove" @click="removePost(i)">X</a></h4>
<p>{{ item.description }}</p>
</div>
</div>
</div>
<div v-else>
<div>Username</div>
<input type="text" v-model="username">
</div>
<button @click="open = !open">{{ label }}</button>
</div>
`
}).mount("#app");
El codigo de mi CRUD:
posts: [
{
title: "Titulo 1",
description: "Lorem ipsum 1",
},
{
title: "Titulo 2",
description: "Lorem ipsum 2",
},
{
title: "Titulo 3",
description: "Lorem ipsum 3",
},
{
title: "Titulo 4",
description: "Lorem ipsum 4",
},
{
title: "Titulo 5",
description: "Lorem ipsum 5",
},
],
newPost: {
title: "",
content: "",
},
postToModify: {
modifying: false,
modifiedTitle: "",
modifiedContent: "",
indexToModify: null,
},
removePost(index) {
this.posts = this.posts.filter(
(item) => this.posts.indexOf(item) !== index
);
},
createPost() {
if (!this.newPost.title || !this.newPost.content) return;
this.posts = [
...this.posts,
{ title: this.newPost.title, description: this.newPost.content },
];
},
editPost(index) {
this.postToModify.modifying = true;
let postToModify = this.posts.filter(
(item) => this.posts.indexOf(item) === index
);
this.postToModify = {
...this.postToModify,
modifiedTitle: postToModify[0].title,
modifiedContent: postToModify[0].description,
indexToModify: index,
};
console.log(postToModify, this.postToModify);
},
modifyPost(indexToModify) {
this.posts = this.posts.map((item, i) =>
i !== indexToModify
? item
: {
...item,
title: this.postToModify.modifiedTitle,
description: this.postToModify.modifiedContent,
}
);
this.postToModify.modifying = false;
},
<div class='list'>
Tus Posts:
<div v-bind:key='index' v-for='(post, index) in posts' class='item'>
<h4>{{post.title}}</h4>
<p>{{post.description}}</p>
<button v-on:click="removePost(index)" style='margin-bottom:12px'>Eliminar post</button>
<button v-on:click='editPost(index)'>Editar Post</button>
</div>
</div>
<div v-if='postToModify.modifying'>
<h5>Titulo del Post</h5>
<br />
<input v-model='postToModify.modifiedTitle' type='text' />
<h5>Contenido del Post</h5>
<input v-model='postToModify.modifiedContent' type='text' />
<br />
<button v-on:click='modifyPost(postToModify.indexToModify)'>Finish editing!</button>
</div>
<div>
<h3>Crear nuevo post</h3>
<h5>Titulo del Post</h5>
<br />
<input type='text' v-model='newPost.title' />
<h5>Contenido del Post</h5>
<input type='text' v-model='newPost.content'/>
<br />
<button v-on:click='createPost()'>Post it!</button>
</div>
Mi solución al reto:
<script>
import { RouterLink, RouterView } from "vue-router";
import HelloWorld from "@/components/HelloWorld.vue";
export default {
data() {
return {
text: "Accede a tu cuenta",
open: false,
username: "",
nuevoPost: {
title: null,
description: null,
},
agregarPostTemplate: false,
mensaje: null,
posts: [
{
title: "Titulo 1",
description: "Lorem ipsum...",
},
{
title: "Titulo 2",
description: "Lorem ipsum...",
},
{
title: "Titulo 3",
description: "Lorem ipsum...",
},
{
title: "Titulo 4",
description: "Lorem ipsum...",
},
],
};
},
watch: {
open(value) {
if (value) {
this.text = "Cierra sesión";
} else {
this.text = "Accede a tu cuenta";
this.username = "";
}
}
},
computed: {
label() {
return this.open ? "Salir" : "Acceder";
},
styles() {
return this.open ? ["open"] : ["closed"];
},
},
methods: {
borrarPost(title, i) {
return this.posts.findIndex((post) => post.title === title) === i
? this.posts.splice(i, 1)
: this.posts;
},
agregarPost() {
if (this.nuevoPost.title === null && this.nuevoPost.description === null) {
this.mensaje = "Debes darle un título y una descripción a tu nuevo post para agregarlo"
return
}
this.posts.push({
title: this.nuevoPost.title,
description: this.nuevoPost.description
});
this.agregarPostTemplate = false;
},
},
};
</script>
<template>
<div class="container" :class="styles">
<h2>{{ text }}</h2>
<div v-if="agregarPostTemplate">
<form @submit.prevent="agregarPost">
<label for="">Agregar post</label> <br />
<label>Título</label>
<input
type="text"
placeholder="Título"
v-model="nuevoPost.title"
/><br />
<label>Descripción</label
><input
type="text"
placeholder="Descripción"
v-model="nuevoPost.description"
/><br />
<button
type="button"
@click="agregarPost"
@keypress.enter="agregarPost"
>
Agregar
</button>
<p style="color: red; font-size: 10px;" v-show="mensaje">{{ mensaje }}</p>
<p @click="agregarPostTemplate = false">⬅️</p>
</form>
</div>
<div v-else-if="open && !agregarPostTemplate">
<div class="add-item-container">
<div>
<p>Hola, {{ username }}</p>
</div>
<div>
<p @click="agregarPostTemplate = true">🖊️</p>
</div>
</div>
<div class="list">
<div v-for="(item, i) in posts" :key="i" class="item">
<div class="item-container">
<div class="title">{{ item.title }}</div>
<p>{{ item.description }}</p>
<p @click="borrarPost(item.title, i)">❌</p>
</div>
</div>
</div>
</div>
<div v-else>
<div>Username</div>
<input type="text" v-model="username" />
</div>
<button @click="open = !open">
<div v-if="!open">Acceder</div>
<div v-else>Salir</div>
</button>
</div>
</template>
<style>
html,
body {
height: 100vh;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}
#app,
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
}
button {
margin-top: 24px;
border: none;
background-color: white;
padding: 8px 24px;
border-radius: 12px;
}
.closed {
background-color: #eca1a6;
}
.open {
background-color: #b5e7a0;
}
.list {
display: flex;
flex-direction: column;
}
.item {
border: 1px solid black;
}
.title {
font-weight: bold;
font-size: 1.2rem;
}
.item-container {
padding: 1rem;
min-width: 10rem;
}
.add-item-container {
display: flex;
justify-content: space-between;
}
.item-container p {
width: 100%;
}
</style>
El desafío de esta clase lo resolví con dos methods removeItem(idx) y addItem(postTitle, postDesc). Los argumentos que reciben dichos methods (data) son ingresados por el usuario desde la vista definida en el template (v-model). Debajo les comparto el fragmento de código con la solución:
<script>
const vm = Vue.createApp({
data() {
return {
idx: 0,
postTitle: 'Default title',
postDesc: 'Default description'
};
},
methods: {
removeItem(idx) {
if(idx > -1 && idx < this.posts.length) {
this.posts.splice(idx, 1);
}
},
addItem(postTitle, postDesc) {
this.posts.push( {title: postTitle, desc: postDesc} );
}
},
template:
`
<div class="container">
<div>{{ today }}{{ sessionSubject }}{{ username }}{{ item.title }}{{ item.desc }}</script>
En las vue.js dev tools pueden ver el índice del elemento sobre el que están iterando, me parece muy útil
comparto mi reto :
<!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>Document</title>
</head>
<body>
<script src="https://unpkg.com/[email protected]"></script>
<div id="app">
</div>
<style>
html, body {
height: 100vh;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}
#app, .container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
}
button {
margin-top: 24px;
border: none;
background-color: white;
padding: 8px 24px;
border-radius: 12px;
}
.closed {
background-color: #eca1a6;
}
.open {
background-color: #b5e7a0;
}
.list {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
}
.item {
margin-top: 15px;
width: 100%;
flex-direction: column;
display: flex;
justify-content: center;
align-items: center;
}
.title {
font-size: 24px;
font-weight: bold;
}
</style>
<script>
const vm = Vue.createApp({
data() {
return {
text: "Accede a tu cuenta",
open:false,
username: "",
title: "",
body:"",
posts: [{
title: "Titulo 1",
body: "Contenido 1",
id: 1
},
{
title: "Titulo 2",
body: "Contenido 2",
id: 2
},
{
title: "Titulo 3",
body: "Contenido 3",
id: 3
}]
};
},
watch :{
open(value){
if(value) {
this.text = "Cierra Sesion";
} else {
this.username = "";
this.text = "Accede a tu cuenta";
}
}
},
computed: {
label() {
return this.open ? "Salir" : "Acceder";
},
styles() {
return this.open ? ['open'] : ['closed'];
}
},
methods: {
addPost(){
this.posts.push({
title: this.title,
body: this.body,
id: this.posts.length + 1
});
this.title = "";
this.body = "";
},
removePost(id){
this.posts = this.posts.filter(post => post.id !== id);
},
},
template: `<div class="container" :class="styles">
<h2>{{text}}{{username}}{{item.title}}{{item.body}}</script>
</body>
</html>
const vm = Vue.createApp({
data() {
return {
text: "Acceder a tu cuenta",
open: false,
username: "",
posts: [
{
title: 'Titulo 1',
description: 'Lorem ipsum 1'
},
{
title: 'Titulo 2',
description: 'Lorem ipsum 2'
},
{
title: 'Titulo 3',
description: 'Lorem ipsum 3'
},
{
title: 'Titulo 4',
description: 'Lorem ipsum 4'
},
],
new_post: {
title: "",
description: ""
}
}
},
methods: {
save() {
this.posts.push({
title: this.new_post.title,
description: this.new_post.description
});
this.new_post.title = "";
this.new_post.description = "";
},
destroy(item) {
this.posts.splice(item, 1);
}
},
watch: {
open(value) {
if (value) {
this.text = "Cerrar sesión"
} else {
this.text = "Acceder a tu cuenta"
this.username = "";
}
}
},
computed: {
label() {
return this.open ? "Salir" : "Acceder"
},
styles() {
return this.open ? ['open']: ['close']
}
},
template: `
<div class="container" :class="styles">
<h2>{{ text }}</h2>
<div v-if="open">
<p>Hola, {{ username }}</p>
<div class="list">
<div class="item" v-for="(post, i) in posts" :key="i">
<div class="title">{{ post.title }}</div>
<p>{{ post.description }}</p>
<a href="#" @click="destroy(i)">Eliminar</a>
</div>
</div>
</div>
<div v-else>
<div>Username</div>
<input type="text" v-model="username" />
</div>
<div v-if="open">
<input v-model="new_post.title" type="text" placeholder="Title">
<input v-model="new_post.description" type="text" placeholder="Description">
<button @click="save">Agregar</button>
</div>
<button @click="open = !open">
<div v-if="!open">Acceder</div>
<div v-else>Salir</div>
</button>
</div>
`
}).mount("#app");
Aquí el pequeño crud de los post
<!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>Document</title>
</head>
<body>
<script src="https://unpkg.com/[email protected]"></script>
<div id="app"></div>
<script>
const vm = Vue.createApp({
data() {
return {
text: "Accede a tu cuenta",
open: false,
username: "",
titlepost: '',
descriptionpost: '',
posts: [{
title: "Titulo 1",
description: "Lorem ipsum..."
}, {
title: "Titulo 2",
description: "Lorem ipsum..."
}]
};
},
watch: {
open(value) {
if (value) {
this.text = "Cierra sesión";
} else {
this.text = "Accede a tu cuenta";
this.username = "";
}
}
},
computed: {
label() {
return this.open ? "Salir" : "Acceder";
},
styles() {
return this.open ? ['open'] : ['closed'];
}
},
methods: {
addnew(){
this.posts.unshift({
title: this.titlepost,
description: this.descriptionpost
})
this.titlepost = ''
this.descriptionpost = ''
},
deletepost(item){
this.posts.splice(item, 1);
}
},
template: `
<div class="container" :class="styles">
<h2>{{ text }}{{ username }}{{ item.title }}{{ item.description }}</script>
<style>
html, body {
height: 100vh;
margin: 0;
font-family: Arial, Helvetica, sans-serif;
}
#app, .container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 100%;
height: 100%;
}
button {
margin-top: 24px;
border: none;
background-color: white;
padding: 8px 24px;
border-radius: 12px;
}
.closed {
background-color: #eca1a6;
}
.open {
background-color: #b5e7a0;
}
.list {
display: flex;
flex-direction: column;
margin-top: 10px;
}
.item {
display: flex;
border: 1px solid black;
}
.item-button{
width: 100%;
display: flex;
justify-content: end;
align-items: center;
}
.title {
font-weight: bold;
font-size: 1.2rem;
width: 100%;
}
.formadd {
}
</style>
</body>
</html>
Vue envuelve los métodos de mutación de una matriz observada para que también activen las actualizaciones de vista. Los métodos envueltos son: push(), pop(), shift(), unshift(), splice(), sort(), reverse().
Generalmente los objetos js vienen con un id
que identifica cada items:
posts: [
{
id: '45',
title: 'Títlo 1',
desc: 'Lorem ipsum, dolor sit amet... 1'
},
{
id: '46',
title: 'Títlo 2',
desc: 'Lorem ipsum, dolor sit amet... 2'
},
{
id: '47',
title: 'Títlo 3',
desc: 'Lorem ipsum, dolor sit amet... 3'
},
],
La directiva :key
también puede tener como identificador el id
del objeto, edecir que puede usarse como:
<div v-for="(post, index) in posts" :key="index">
y también como:
<div v-for="post in posts" :key="post.id">
Si quieren agregar un método que adicione un post, lo pueden hacer con .push() por ejemplo, si quieren eliminar un post, pueden suar dos métodos de acuerdo a si lo queremos eliminar por el index o por el id de cada post.
methods: {
deletePostForIndex(index) {
if (index >= 0 && index < this.posts.length) {
this.posts.splice(index, 1);
} else {
console.log('No existe el índice');
}
},
deletePostForId(id) {
const findIndex = this.posts.findIndex((post) => post.id === id);
if (findIndex >= 0 && findIndex < this.posts.length) {
this.posts.splice(findIndex, 1);
} else {
console.log('No existe el índice');
}
},
},
¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.