workeet.app Part 2 : Vue est ton ami…

gbtux
11 min readSep 19, 2018

--

Plan:

Abstract:

Dans cette deuxième partie, on attaque les choses sérieuses sur la partie front : mise en place de CoreUI et on va démarrer VueJS.

Bonne lecture !

CoreUI

Comme je l’ai dit dans la première partie, je ne suis nullement designer.

On va donc utiliser CoreUI.

Pour construire notre projet, un conseil : allez voir sur Github les sources du template example, et notamment le répertoire “src” que voici :

Répertoire “src” sur Github

Tiens, on retrouve quelques “trucs” qu’on a déjà dans notre projet :

  • main.js : le fichier “init” du projet (le notre s’appelle app.js)
  • App.vue : le composant principal du projet

Mais voyons aussi le reste :

  • assets : fichiers images / css
  • containers : des composants “contenants”, dans lequel on viendra “injecter” d‘autres composants
  • router : le “router” de l’application (on y reviendra largement)
  • shared : des constantes, des fonctions “partagées” entre plusieurs composants
  • views : nos composants, que l’on viendra “injecter” dans les “containers”
  • _nav.js : juste un fichier qui décrit les entrées du menu principal

On installe et on construit ça pas à pas ? C’est parti

Installation de CoreUI

Puisqu’on a la demo de CoreUI sous la main, on va en profiter pour regarder le fichier package.json, afin de voir les dépendances.

Voici une première salve de dépendances à installer :

npm i --save @coreui/coreui @coreui/icons @coreui/vue bootstrap bootstrap-vue font-awesome perfect-scrollbar simple-line-icons

Analyse du front

Avant d’aller plus loin, il est nécessaire de prendre un peu de recul pour prendre le projet par le bon bout.

D’abord, voyons comment est construite notre page avec nos fameux composants :

Architecture des composants front

Assez classique comme présentation mais néanmoins efficace.

Les noms de composants sont repris de CoreUI.

Maintenant qu’on a une idée de ce qu’il faut construire, on va voir “comment” le construire.

En effet, on a dit plus haut qu’on avait un “container” (ici “DefaultContainer”) et qu’à l’intérieur de celui-ci, on allait “injecter” des composants (des “views”).

Le mécanisme nous permettant de faire cela est une librairie Vue qui s’appelle vue-router.

Comment ça marche ?

L’idée est très simple : quand on va appeler une URL de type http://<monserveur>/contacts, on veut que ça affiche notre page (avec le header, le menu de gauche) mais avec en partie centrale (dans “main class=main”) la liste de nos contacts.

Idem si je clique dans le menu sur “entreprises”, l’URL va se changer en http://<monserveur>/entreprises, et rien ne doit changer sauf la partie centrale qui doit afficher la liste de entreprises.

Avant de commencer à coder, installons la librairie vue-router:

installation de vue-router

App.js

On a tout ce qu’il nous faut pour commencer à construire notre interface.

Editez le fichier assets/js/app.js et transformez-le comme suit :

app.js

Analysons les lignes ajoutées :

  • lignes 3 à 5 : on passe
  • ligne 8 : import de BootstrapVue, le framework graphique derrière CoreUI
  • ligne 11 : import du router. C’est le fichier qui décrira comment se comporte le routage dans notre application; il utilisera évidemment ce qu’on vient de voir sur vue-router
  • ligne 13 : injection des composants de BootstrapVue dans l’objet Vue
  • ligne 17 : injection du router dans l’objet Vue

Oui, je sais, vous voyez du jaune sur la ligne 11 et vous vous doutez qu’il y a un soucis. On va justement le confirmer et le régler.

Pour cela, ouvrez un terminal et assemblez votre projet avec

npm run watch

Note : on a remplacé notre commande npm run dev par npm run watch. Le résultat est le même : on compile le projet. Néanmoins, l’avantage de watch et de surveiller toutes les modifications dans votre projet à la sauvegarde des fichiers et de relancer automatiquement la compilation.

On obtient donc ce résultat :

erreur à la compilation

Notre ligne 11 a bien quelque chose qui ne va pas !

Notre router est bien importé, mais on ne l’a pas créé !

Router

Créons-le, en créant un répertoire assets/js/router, puis en créant un fichier index.js dans ce répertoire nouvellement créé.

Dès que c’est fait, vous verrez que votre build sera “successful”, même si notre index.js est vide ;)

Codons maintenant un prémice de router:

notre router minimal

Quelques explications:

  • lignes 1 et 2 : import de Vue et vue-router
  • ligne 5 : import du container
  • ligne 7 : référencement du router dans l’objet Vue
  • Déclaration de notre classe Router avec :

→le mode “hash” : on aura des URL de type : http://monserveur/#contacts

→ une liste de routes

Ici, on a 2 routes de configurées :

  • Une route racine “/”, dont on indique que si elle est appelée, on redirigera sur la route “/dashboard”.
  • Une route “/dashboard”, fille de “/”. Quand elle sera appelée, on chargera le composant de la route “mère” (DefaultContainer) dans lequel on viendra injecter le composant lié à la route : Dashboard

Relancez votre npm run watch pour prendre en compte le nouveau fichier du router, et constatez le résultat : comme précédemment, nos 2 fichiers manquent : DefaultContainer.vue et Dashboard.vue

DefaultContainer.vue

Créez donc un répertoire assets/js/containers et créez à l’intérieur un fichier DefaultContainer.vue comme suit :

<template>

</template>

<script>
export default {
name: "DefaultContainer"
}
</script>

Note : pour ceux utilisant PhpStorm (ou WebStorm), un clic droit sur e répertoire, “New > Vue Component” existe, qui crée le fichier avec un squelette de composant à l’intérieur.

Créez aussi un répertoire assets/js/views et un composant Dashboard.vue à l’intérieur comme suit :

<template>

</template>

<script>
export default {
name: "Dashboard.vue"
}
</script>

On vient donc de créer 2 composants. Génial !

Bon ok, ils sont un peu vides, mais ça nous permet de voir comment est structuré un composant :

  • à l’intérieur des balises <template></template> la partie …. template ;) En d’autres termes, la partie visuelle du composant, avec son code HTML ou d’autres composants (qui eux aussi vont produire du HTML)
  • une partie <script></script>, qui est la logique du composant : ses données rattachées, ses méthodes, ses évènements, ce qu’il se passe quand il est chargé, etc….

Commençons par remplir un peu notre composant container (DefaultContainer) :

<template>
<div class="app">
<AppHeader fixed>
<SidebarToggler class="d-lg-none" display="md" mobile />
<b-link class="navbar-brand" to="#">
<img class="navbar-brand-full" src="img/brand/logo.svg" width="89" height="25" alt="CoreUI Logo">
<img class="navbar-brand-minimized" src="img/brand/sygnet.svg" width="30" height="30" alt="CoreUI Logo">
</b-link>
<SidebarToggler class="d-md-down-none" display="lg" />
</AppHeader>
<div class="app-body">
<AppSidebar fixed>
<SidebarHeader/>
<SidebarForm/>
<SidebarNav :navItems="nav"></SidebarNav>
<SidebarFooter/>
<SidebarMinimizer/>
</AppSidebar>
<main class="main">
<Breadcrumb :list="list"/>
<div class="container-fluid">
<h3>ici la partie centrale ??</h3>
</div>
</main>
</div>
<TheFooter>
<!--footer-->
<div>
<a href="https://coreui.io">Workeet</a>
<span class="ml-1">&copy; 2018 workeetLabs.</span>
</div>
<div class="ml-auto">
<span class="mr-1">Powered by</span>
<a href="https://coreui.io">CoreUI for Vue</a>
</div>
</TheFooter>
</div>
</template>

<script>

import { Header as AppHeader, SidebarToggler, Sidebar as AppSidebar, Footer as TheFooter, Breadcrumb, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav } from '@coreui/vue'

export default
{
name: "DefaultContainer",
components: {
AppHeader, SidebarToggler, AppSidebar, TheFooter, Breadcrumb, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav
}
}
</script>

Ici, pas de panique, on va expliquer chaque composant, mais testons d’abord si ça marche (npm run watch), et testons dans le navigateur…..Ahhhh

Hello Symfony ;)

Rien n’a changé. Pourtant, j’appelle bien mon URL de base, qui redirige bien vers /dashboard. Mais aucun des composants du DefaultContainer ne s’affiche !

Pourquoi ? La réponse est encore une fois simple ! (oui bon pas tant que ça…)

Souvenez-vous, on a démarré notre application Vue avec ce code :

new Vue({
el: '#app',
router,
template: '<App/>',
components: {
App
}
})

On lui a dit que le composant chargé au démarrage était …. App.vue.

Sauf qu’on a pas modifié App.vue !

Modifiez-le comme suit :

<template>
<router-view></router-view>
</template>

<script>

export default {
name: 'app'
}
</script>

Heu c’est quoi ce “router-view” ?

Souvenez-vous encore une fois. Quand on a créé le router, on a dit qu’on chargeait la route “/”, redirigée vers “/dashboard”.

Or, /dashboard nous dit qu’il injecte le composant de base (DefaultContainer) et un autre composant (Dashboard).

Oui mais …. où ? dans quelle div ?

C’est ici qu’intervient “router-view”. Ce “composant” va juste injecter le composant donné par le router, soit le composant “DefaultContainer”.

Preuve en est : maintenant modifié, rafraichissez a page de votre navigateur..

talaaaahhhh…

on avance…

Bon c’est pas génial, mais on avance. Au moins, il y a quelques trucs qui ressemblent à ce que contient DefaultContainer. Toutefois, il semble manquer les CSS et les images (logo,…)

Pour charger les CSS, on va les ajouter à notre composant de base : App.

Modifiez le fichier App.vue comme suit :

<template>
<router-view></router-view>
</template>

<script>

export default {
name: 'app'
}
</script>

<style lang="scss">
// CoreUI Icons Set
@import '~@coreui/icons/css/coreui-icons.min.css';
/* Import Font Awesome Icons Set */
$fa-font-path: '~font-awesome/fonts/';
@import '~font-awesome/scss/font-awesome.scss';
/* Import Simple Line Icons Set */
$simple-line-font-path: '~simple-line-icons/fonts/';
@import '~simple-line-icons/scss/simple-line-icons.scss';
/* Import Flag Icons Set */
@import '~flag-icon-css/css/flag-icon.min.css';
/* Import Bootstrap Vue Styles */
@import '~bootstrap-vue/dist/bootstrap-vue.css';
// Import Main styles for this application
@import 'assets/scss/style';
</style>

Arghhh non, encore un problème ! C’est Webpack qui me dit que ma compilation ne fonctionne plus !

CssSyntaxError

Après quelques recherches, on a en effet oublié de dire à Webpack de prendre en compte le pré-processing CSS.

Pour cela, 2 actions à faire :

  1. Décommentez la ligne .enableSassLoader() dans webpack.config.js
  2. Installer les dépendances nécessaires à ce pre-processing
npm i --save-dev sass-loader node-sass

Bon maintenant ça à l’air de marcher, mais il ne trouve pas certains fichiers.

Rendez-vous sur https://github.com/coreui/coreui-free-vue-admin-template/tree/master/src/assets et copiez tout le répertoire “scss” dans votre répertoire assets à la racine de votre projet.

Installez aussi flag-icon-css que l’on a oublié lors de l’installation de CoreUI

npm i --save flag-icon-css

relancez “npm run watch” ….ça marche !!

Vérifions dans le navigateur (http://localhost:8000)

on y arrive presque !

C’est pas encore parfait, mais on avance pas à pas (c’es un peu l’idée du tuto ;)

Et si on regarde dans la console du navigateur (F12), on voit qu’on a quelques erreurs :

La première, c’est Vue qui nous dit qu’il ne trouve pas la propriété ou la méthode “nav” dans DefaultContainer.

En effet, ligne 15 du composant, on déclare un nouvel élément tel que :

<SidebarNav :navItems=”nav”></SidebarNav>

C’est le menu de gauche (fond noir), et on lui dit que les éléments du menu sont contenus dans la liste “nav”, mais on a pas déclaré cette liste.

Nav

Si vous vous souvenez de l’analyse réalisée sur la structure du template Vue de CoreUI, on a parlé d’un fichier _nav.js contenant les éléments de navigation.

C’est lui qui nous manque.

Créons le dans assets/js comme suit:

export default {
items: [
{
name: 'Dashboard',
url: '/dashboard',
icon: 'icon-speedometer'
},
{
title: true,
name: 'Business',
class: '',
wrapper: {
element: '',
attributes: {}
}
},
{
name: 'Contacts',
url: '/contacts',
icon: 'icon-people'
},
{
name: 'Entreprises',
url: '/entreprises',
icon: 'icon-briefcase'
}
]
}

Familiarisez-vous avec ce fichier : dès que vous voudrez ajoutez une entrée de menu : c’est ici!

Notez les icones prises dans “simple-line-icons”

J’ai pris un peu d’avance ici, en ajoutant en plus du lien vers “/dashboard”, un lien vers “/contacts” et “/entreprises”. On les voit dans le menu mais si on clique dessus : page blanche (on verra comment rediriger en cas d’erreur vers /dashboard)

Maintenant que nos entrées de menu sont prêtes, il faut les importer dans DefaultContainer.

Votre fichier DefaultContainer.vue doit être modifié comme suit :

<script>

import { Header as AppHeader, SidebarToggler, Sidebar as AppSidebar, Footer as TheFooter, Breadcrumb, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav } from '@coreui/vue'
import
nav from '../_nav'

export default
{
name: "DefaultContainer",
components: {
AppHeader, SidebarToggler, AppSidebar, TheFooter, Breadcrumb, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav
},
data () {
return {
nav: nav.items
}
}
}
</script>

Notez l’apparition de “import nav from ‘../_nav” (le fichier qu’on vient de créer), et de la partie data () {….}

data décrit juste les “données” liées au composant. Ici on déclare une donnée “nav” (tiens ce sera pas la liste qui nous manquait ????) qui va prendre le tableau “items” du fichier _nav.js.

List

Maintenant qu’on a réglé notre première erreur, voyons la 2ème: list, elle aussi contenue dans DefaultContainer.

On retrouve notre variable “list” line 20 :

<Breadcrumb :list="list"/>

List est en fait une liste d’éléments à passer au composant Breadcrumb pour que celui-ci affiche un fil d’ariane.

Modifiez votre composant DefaultContainer comme suit :

<template>
... code inchangé
</template>
<script>

import { Header as AppHeader, SidebarToggler, Sidebar as AppSidebar, Footer as TheFooter, Breadcrumb, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav } from '@coreui/vue'
import
nav from '../_nav'

export default
{
name: "DefaultContainer",
components: {
AppHeader, SidebarToggler, AppSidebar, TheFooter, Breadcrumb, SidebarFooter, SidebarForm, SidebarHeader, SidebarMinimizer, SidebarNav
},
data () {
return {
nav: nav.items
}
},
computed: {
list () {
return this.$route.matched.filter((route) => route.name || route.meta.label )
}
}
}
</script>

“computed” est une nouvelle notion. Il s’agit d’une variable dont la valeur n’est pas “brute”, mais va dépendre d’un traitement : en bref, une propriété “calculée” (voir https://fr.vuejs.org/v2/guide/computed.htm).

Rafraichissez votre page :

Plus que les images !

Notre page est maintenant à eu près complète, mais il manque encore des images!

Créez un répertoire assets/img pour accueillir nos images, et copiez le contenu de https://github.com/coreui/coreui-free-vue-admin-template/tree/master/public/img/brand dans un nouveau répertoire brand dans le répertoire img que vous venez de créer.

Modifiez ensuite webpack.config.js en ajoutant ces lignes :

.configureFilenames({
images: '[path][name].[ext]',
})

Cela permet de ne pas utiliser le versionning des assets pour les images.

Modifiez ensuite app.js comme suit :

require('../css/app.css');

import Vue from 'vue'
import
BootstrapVue from 'bootstrap-vue'

import
App from './App'
import
router from './router'

Vue.use(BootstrapVue)

const imagesContext = require.context('../img', true, /\.(png|jpg|jpeg|gif|ico|svg|webp)$/);
imagesContext.keys().forEach(imagesContext)


new Vue({
el: '#app',
router,
template: '<App/>',
components: {
App
}
})

Notez l’apparition de …imagesContext pour charger les images.

Rechargez votre page :

Yeeeessssss !

Vous êtes contents ? Oui ? Ben pas moi !

Parce que si on a bien notre DefaultContainer d’affiché, où est passé notre composant Dashboard ?

Alors OK pour l’instant il est vide, mais remplissez-le et vous ne verrez rien de plus.

Pour afficher notre composant en lieu et place de “ici la partie centrale”, modifiez évidemment Dashboard.vue (dans la partie <template></template>), mais surtout remplacez

<h3>ici la partie centrale ??</h3>

Par

<router-view></router-view>

Et voici le résultat :

c’est fini ! (pour aujourd’hui)

Notre projet a enfin avancé sur la partie front.

Ce fut un peu chaotique, mais c’est évidemment pour vous montrer la démarche, et dans ce chapitre, nous avons vu pas mal de notions.

Dans la prochaine partie, nous créerons un “vrai” écran avec une liste de contacts que l’on ira chercher via Ajax dans la base de données.

A bientôt !

--

--

gbtux

Love Symfony, Vue, Linux, and Web Software Architecture.