20 |
21 |
22 |
23 |
74 |
76 |
77 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | // Template version: 1.3.1
3 | // see http://vuejs-templates.github.io/webpack for documentation.
4 |
5 | const path = require('path')
6 |
7 | module.exports = {
8 | dev: {
9 |
10 | // Paths
11 | assetsSubDirectory: 'static',
12 | assetsPublicPath: '/',
13 | proxyTable: {},
14 |
15 | // Various Dev Server settings
16 | host: 'localhost', // can be overwritten by process.env.HOST
17 | port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
18 | autoOpenBrowser: false,
19 | errorOverlay: true,
20 | notifyOnErrors: true,
21 | poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
22 |
23 | // Use Eslint Loader?
24 | // If true, your code will be linted during bundling and
25 | // linting errors and warnings will be shown in the console.
26 | useEslint: false,
27 | // If true, eslint errors and warnings will also be shown in the error overlay
28 | // in the browser.
29 | showEslintErrorsInOverlay: false,
30 |
31 | /**
32 | * Source Maps
33 | */
34 |
35 | // https://webpack.js.org/configuration/devtool/#development
36 | devtool: 'cheap-module-eval-source-map',
37 |
38 | // If you have problems debugging vue-files in devtools,
39 | // set this to false - it *may* help
40 | // https://vue-loader.vuejs.org/en/options.html#cachebusting
41 | cacheBusting: true,
42 |
43 | cssSourceMap: true
44 | },
45 |
46 | build: {
47 | // Template for index.html
48 | index: path.resolve(__dirname, '../dist/index.html'),
49 |
50 | // Paths
51 | assetsRoot: path.resolve(__dirname, '../dist'),
52 | assetsSubDirectory: 'static',
53 | assetsPublicPath: '/',
54 |
55 | /**
56 | * Source Maps
57 | */
58 |
59 | productionSourceMap: true,
60 | // https://webpack.js.org/configuration/devtool/#production
61 | devtool: '#source-map',
62 |
63 | // Gzip off by default as many popular static hosts such as
64 | // Surge or Netlify already gzip all static assets for you.
65 | // Before setting to `true`, make sure to:
66 | // npm install --save-dev compression-webpack-plugin
67 | productionGzip: false,
68 | productionGzipExtensions: ['js', 'css'],
69 |
70 | // Run the build command with an extra argument to
71 | // View the bundle analyzer report after build finishes:
72 | // `npm run build --report`
73 | // Set to `true` or `false` to always turn it on or off
74 | bundleAnalyzerReport: process.env.npm_config_report
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-nas",
3 | "version": "1.0.0",
4 | "description": "formacion iniciacion a vue",
5 | "author": "karol ",
6 | "private": true,
7 | "scripts": {
8 | "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
9 | "start": "npm run dev",
10 | "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
11 | "e2e": "node test/e2e/runner.js",
12 | "test": "npm run unit && npm run e2e",
13 | "lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
14 | "build": "node build/build.js"
15 | },
16 | "dependencies": {
17 | "node-sass": "^4.11.0",
18 | "sass-loader": "^7.1.0",
19 | "vue": "^2.5.2",
20 | "vue-router": "^3.0.1"
21 | },
22 | "devDependencies": {
23 | "autoprefixer": "^7.1.2",
24 | "babel-core": "^6.22.1",
25 | "babel-eslint": "^8.2.1",
26 | "babel-helper-vue-jsx-merge-props": "^2.0.3",
27 | "babel-loader": "^7.1.1",
28 | "babel-plugin-istanbul": "^4.1.1",
29 | "babel-plugin-syntax-jsx": "^6.18.0",
30 | "babel-plugin-transform-runtime": "^6.22.0",
31 | "babel-plugin-transform-vue-jsx": "^3.5.0",
32 | "babel-preset-env": "^1.3.2",
33 | "babel-preset-stage-2": "^6.22.0",
34 | "babel-register": "^6.22.0",
35 | "chai": "^4.1.2",
36 | "chalk": "^2.0.1",
37 | "chromedriver": "^2.27.2",
38 | "copy-webpack-plugin": "^4.0.1",
39 | "cross-env": "^5.0.1",
40 | "cross-spawn": "^5.0.1",
41 | "css-loader": "^0.28.0",
42 | "eslint": "^4.15.0",
43 | "eslint-config-standard": "^10.2.1",
44 | "eslint-friendly-formatter": "^3.0.0",
45 | "eslint-loader": "^1.7.1",
46 | "eslint-plugin-import": "^2.7.0",
47 | "eslint-plugin-node": "^5.2.0",
48 | "eslint-plugin-promise": "^3.4.0",
49 | "eslint-plugin-standard": "^3.0.1",
50 | "eslint-plugin-vue": "^4.0.0",
51 | "extract-text-webpack-plugin": "^3.0.0",
52 | "file-loader": "^1.1.4",
53 | "friendly-errors-webpack-plugin": "^1.6.1",
54 | "html-webpack-plugin": "^2.30.1",
55 | "inject-loader": "^3.0.0",
56 | "karma": "^1.4.1",
57 | "karma-coverage": "^1.1.1",
58 | "karma-mocha": "^1.3.0",
59 | "karma-phantomjs-launcher": "^1.0.2",
60 | "karma-phantomjs-shim": "^1.4.0",
61 | "karma-sinon-chai": "^1.3.1",
62 | "karma-sourcemap-loader": "^0.3.7",
63 | "karma-spec-reporter": "0.0.31",
64 | "karma-webpack": "^2.0.2",
65 | "mocha": "^3.2.0",
66 | "nightwatch": "^0.9.12",
67 | "node-notifier": "^5.1.2",
68 | "optimize-css-assets-webpack-plugin": "^3.2.0",
69 | "ora": "^1.2.0",
70 | "phantomjs-prebuilt": "^2.1.14",
71 | "portfinder": "^1.0.13",
72 | "postcss-import": "^11.0.0",
73 | "postcss-loader": "^2.0.8",
74 | "postcss-url": "^7.2.1",
75 | "rimraf": "^2.6.0",
76 | "selenium-server": "^3.0.1",
77 | "semver": "^5.3.0",
78 | "shelljs": "^0.7.6",
79 | "sinon": "^4.0.0",
80 | "sinon-chai": "^2.8.0",
81 | "uglifyjs-webpack-plugin": "^1.1.1",
82 | "url-loader": "^0.5.8",
83 | "vue-loader": "^13.3.0",
84 | "vue-style-loader": "^3.0.1",
85 | "vue-template-compiler": "^2.5.2",
86 | "webpack": "^3.6.0",
87 | "webpack-bundle-analyzer": "^2.9.0",
88 | "webpack-dev-server": "^2.9.1",
89 | "webpack-merge": "^4.1.0"
90 | },
91 | "engines": {
92 | "node": ">= 6.0.0",
93 | "npm": ">= 3.0.0"
94 | },
95 | "browserslist": [
96 | "> 1%",
97 | "last 2 versions",
98 | "not ie <= 8"
99 | ]
100 | }
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Descripción Taller Vue.js
2 |
3 | VUEnas a todos!
4 |
5 | En este taller de iniciación a Vue.js veremos cómo construir en pocos pasos una interfaz web sencilla. Hablaremos también sobre la importancia de crear componentes customizables y reutilizables como paradigma a la hora de crear nuestra web.
6 |
7 | Empezaremos super rápido gracias a Vue-CLI y veremos importantes aspectos de Vue, como las directivas, cómo se hace el rendering, variables computadas, routing, gestión de estado...
8 |
9 | Pero lo importante es que vosotros os ensuciéis las manos creando un par de vistas, haciendo componentes, pasando datos, y que completéis al máximo un mini proyecto que os pueda servir en un futuro, ya sea para haceros VUEjstro propio portfolio o para cuando os tengáis que enfrentar a Vue en un trabajo.
10 |
11 |
12 | Para partir de una base, tenemos un diseño: https://invis.io/FAQUGIWU34C
13 |
14 | En el que iremos basando todo nuestro desarrollo...
15 |
16 | 
17 |
18 |
19 |
20 | ## Step 0.0
21 |
22 | 
23 |
24 | En este paso podrás encontrar la aplicación sin VUE, observarás que hay tres archivos básicos:
25 |
26 | 1. index.html
27 | 2. index.js
28 | 3. styles.css
29 |
30 | Ahora podrás comprobar lo fácil que es integrar VUE en esta aplicación, haz click en el siguiente enlace que te llevará al codesanbox:
31 |
32 |
33 | https://codesandbox.io/s/23nl747ly
34 |
35 |
36 | Para añadir VUE, tenemos que importar la librería de vue en nuestro html, y además decirle a Vue dónde queremos que se instancie:
37 |
38 | ``` javascript
39 |
40 | ```
41 |
42 | index.js:
43 |
44 | ``` javascript
45 | new Vue({
46 | el: "#app",
47 | data: {}
48 | });
49 | ```
50 |
51 | Con esto último, estamos instanciando Vue en el *el* (elemento) con id="app". Y ya estaría. Vamos a ver cómo pasar datos desde nuestra instancia de Vue para que se muestren en el html.
52 |
53 |
54 |
55 | ## Step 0.1
56 |
57 | Cuando instanciamos Vue, podemos indicarle así como indicamos el *el*emento donde se encuentra, los datos que tiene, en el elemento *data*.
58 |
59 | ``` javascript
60 | new Vue({
61 | el: "#app",
62 | data: {
63 | logo: "Mis proyectos",
64 | projects: [
65 | {
66 | id: 1,
67 | title: "PROYECTO 1",
68 | text: "descripcion 1"
69 | },
70 | {
71 | id: 2,
72 | title: "PROYECTO 2",
73 | text: "descripcion 2"
74 | }
75 | ]
76 | }
77 | });
78 | ```
79 |
80 | Ahora en la variable "logo" tendremos esa cadena de texto que podemos referenciar desde el html, con la *sintaxis del moustache* **{{ }}**.
81 |
82 | ``` html
83 |
{{ logo }}
84 | ```
85 |
86 | Para hacer lo mismo con el proyecto que vemos, vamos a crear además un array *projects*, donde cada uno tiene un *title* y un *description*. Y duplicaremos el código que renderiza un proyecto, para que ahora muestre *{{projects[0].title}}* y así.
87 |
88 | Para pintar el segundo proyecto tenemos que copiar de nuevo el section y poner la posición 1 en el Array:
89 |
90 | ``` javascript
91 |
92 |
93 |
94 |
95 |
96 |
{{projects[1].title}}
97 |
98 |
99 |
100 |
101 |
102 |
{{projects[1].text}}
103 |
104 |
105 |
106 | ```
107 |
108 | El resultado sería este: https://codesandbox.io/s/25j4kwrwr
109 |
110 | ## Step 0.2
111 |
112 | Ok, ya tenemos visible nuestros dos proyectos. Ahora queremos meter un buscador para poder buscar por el título del proyecto, y que:
113 | 1. En el caso de que hay proyectos que contienen la cadena de texto que buscamos, mostrarlos.
114 | 2. Mostrar "No hay proyectos" si no encuentra ninguno.
115 |
116 | Para esto haremos uso de dos conceptos, el de **variable computada** y el de **directiva**. En concreto, utilizaremos la directiva *v-if* que nos permite mostrar un trozo de html sólo si se cumple la condición. En cuanto a las variables computadas, no son más que funciones que se ejecutan y devuelven un valor, haciendo más fácil el renderizado de expresiones. Así, podemos meter en una función un filter según el input (vamos a llamarle *inputText* del usuario, y devolver el array de proyectos cuyo título coincide.
117 |
118 | ¿Y cómo hacemos que Vue sepa qué está metiendo el usuario en el campo de búsqueda? Pues con otra directiva, *v-model*. Esta hará un doble binding con lo que el usuario entra y la variable que le indiquemos.
119 |
120 | ``` html
121 |
122 | ```
123 |
124 | No se nos debe olvidar meter *inputText* en el campo *data*, para que Vue pueda reaccionar a cambios. Sin esto, Vue no sabe qué campos mirar para volver a renderizar el html.
125 |
126 | ``` javascript
127 | computed: {
128 | filteredProjects: function() {
129 | return this.projects.filter(
130 | p => p.title.toLowerCase().indexOf(this.inputText.toLowerCase()) > -1
131 | );
132 | }
133 | }
134 | ```
135 |
136 | Para indicar si hay proyectos o no, podemos ahora bindear nuestro html con *filteredProjects* en lugar de *projects*. Para saber si mostramos los proyectos o un "No hay proyectos" si la búsqueda no encuentra ninguno, podemos comprobar la longitud de ese array, y lo meteremos como otra computada que referenciaremos desde el html.
137 |
138 | ``` javascript
139 | showProjects: function() {
140 | return this.filteredProjects.length !== 0;
141 | }
142 | ```
143 |
144 | ``` html
145 |
146 |
147 | ...
148 | ```
149 |
150 | El resultado sería: https://codesandbox.io/embed/1v8wxm0l57
151 |
152 | 
153 |
154 | ## Step 1
155 |
156 | Vemos que esto va creciendo considerablemente a medida que vamos metiendo variables y métodos para renderizar. En el mundo real, este caso puede servir para algo muy pequeño, pero lo que nos podemos encontrar es una aplicación completa que queremos desarrollar en Vue. Para ello vamos a hacer uso de un *cli* que iniciará una aplicación por nosotros, y podremos hacer muchas más cosas en ella.
157 |
158 | Lo primero, tener **npm** instalado, y después, instalar **vue-cli**.
159 |
160 | `npm install -g @vue/cli`.
161 |
162 | Si quieres también puedes trabajar sobre nuestro repo de GitHub donde ya hemos hecho algunas cosas por ti :).
163 | https://github.com/karoldesign/VUE-nas-formacion/tree/step-2-computed_and_v-if
164 |
165 |
166 | o si prefieres seguir en codesandbox, puedes partir de aquí con todo lo que llevamos hecho:
167 | https://codesandbox.io/s/ozzlp9j2z
168 |
169 | Y si no, ahora vamos a trasladar todo lo que llevamos hecho a nuestra nueva y flamante aplicación.
170 |
171 | ## Step 2
172 |
173 | En nuestro código html que renderiza proyectos, vemos que estamos duplicando código para cada proyecto. ¿Podemos reutilizarlo para no tenerlo duplicado? Sí. Vamos a hacer uso de la directiva **v-for** para el renderizado de listas, y además, vamos a sacar ese contenido a un componente a parte, que podemos referenciar por su nombre e incluirlo en nuestra instancia de vue.
174 |
175 | Ahora, nuestra instancia además de tener un *data* y un *computed*, tendrá un *components* donde incluiremos nuestro componente (y además lo tendremos que importar).
176 |
177 | ``` javascript
178 | import project from "@/components/project";
179 |
180 | ...
181 |
182 | data: ...,
183 | components: {
184 | project: project
185 | }
186 | ```
187 |
188 | ¿Qué va a tener nuestro proyecto? Pues tendrá el mismo código que teníamos antes, lo único que ahora las variables que teníamos con {{ }} ahora serán **props** que recibe desde el componente padre, es decir, el que lo instancia.
189 |
190 | ``` html
191 |
192 |
193 |
194 |
195 |
{{ title }}
196 |
197 |
198 |
199 |
200 |
201 |
{{ text }}
202 |
203 |
204 | ```
205 |
206 | ¿Y de dónde salen *title* y *text*? Pues cuando definimos el componente, indicamos que recibe un array de *props* con esos nombres. Además, vamos a pasarle un identificador:
207 | ```javascript
208 | export default {
209 | name: 'project',
210 | props: ['title', 'text', 'id'],
211 | mounted () {
212 | },
213 | data () {
214 | return {}
215 | },
216 | methods: {
217 | },
218 | computed: {
219 | }
220 | }
221 | ```
222 |
223 | Resultado: https://codesandbox.io/s/m5owym2l49
224 |
225 | Y desde el padre ya podremos llamarlo, y pasarle props, con otra directiva, **v-bind**.
226 | ```html
227 |
234 | ```
235 |
236 | También podemos hacer uso de la sintaxis que propone Vue para evitarnos escribir todo el rato *v-bind*:
237 | ```html
238 |
245 | ```
246 | Resultado: https://codesandbox.io/s/m3y39kl8px
247 |
248 | ## Step 3
249 |
250 | Vamos ahora a hacer uso de métodos, para poder añadir proyectos y eliminar proyectos. Tenemos ahora dos componentes, el padre (la lista) y el hijo (cada proyecto). La forma de comunicar componentes se debe hacer diferente en cada sentido. Del padre hacia el hijo, hemos visto cómo pasar datos, con las **props** y el **v-bind**. o **:**. Del hijo al padre nos comunicaremos mediante eventos, con la directiva **v-on** o con su otra sintaxis **@**, seguida del nombre del evento.
251 |
252 | Empezamos creando un botón en el componente padre que ejecuta un método cuando se hace click:
253 |
254 | ```html
255 |
256 | ```
257 | Y añadimos el método *add*
258 | ``` javascript
259 | methods: {
260 | add: function() {
261 | const id = this.projects.length + 1;
262 | this.projects.push({
263 | id: id,
264 | title: `PROYECTO ${id}`,
265 | text: `Descripción ${id}`
266 | });
267 | },
268 | }
269 | ```
270 |
271 | Ya podemos añadir proyectos cuando hacemos click. Este caso era fácil, porque toda la gestión del evento estaba en el mismo componente. Ahora vamos a darle funcionalidad al botón de eliminar.
272 |
273 | Dentro de nuestro componente *project* tenemos el botón, así que hacemos que gestione el evento de click:
274 | ```html
275 |
276 | ```
277 | que ejecuta el método *onRemove*, que añadiremos en el componente, para que emita un evento hacia su padre:
278 | ```javascript
279 | onRemove (event) {
280 | this.$emit('remove', this.id)
281 | }
282 | ```
283 |
284 | Ahora tenemos que hacer que el padre escuche el evento 'remove' y haga cosas. Hacemos que en el evento 'remove' ejecute el método también llamado remove (la @ indica el nombre del evento que escucha, y al otro lado del igual, el nombre del método a ejecutar).
285 | ```html
286 |
294 | ```
295 |
296 | Y añadimos el método
297 | ```javascript
298 | methods: {
299 | remove: function(id) {
300 | this.projects = this.projects.filter(p => p.id != id);
301 | },
302 | }
303 | ```
304 |
305 | Resultado: https://codesandbox.io/s/zn29zm8vrx
306 |
307 | ## Step 4
308 |
309 | Guay! Ya podemos añadir y eliminar proyectos. ¿Queremos editarlos? Vamos a ello. Vamos a explicar qué queremos hacer y vosotros lo implementáis:
310 |
311 | Al hacer click en el botón editar (*@click*)
312 |
313 | ```javascript
314 |
315 |
316 |
317 | ```
318 | debemos hacer que se vean inputs (*v-bind*?)
319 | en lugar de los textos (*v-if*?),
320 |
321 | ```javascript
322 |
323 |