react native expo android ios sqlite Cours web Mobile – React Native

Chap 6 : Stockage de données complexes (SQLite)

Nous avons vu dans le chapitre précédent comment stocker en local des données simples.

On va s’attarder cette fois-ci à comment stocker des informations plus nombreuses, plus complexes et ce de façon plus ordonnée, en utilisant des bases de données SQL.

On va réaliser une simple to-do app, avec possibilité d’ajouter une note, ou d’en supprimer, avec une liste permettant de toute les afficher.

 

On ne peut utiliser n’importe quel outils pour persister des données car nous sommes sur un projet Expo, nous empêchant d’accéder aux modules natifs de iOS et Android. SQLite est un moyen fonctionnant avec des projets Expo.

 

Objectifs

  • Stocker localement des informations via une base de donnée SQL

 

react native sqlite

 

Prérequis

Installez le package pour utiliser SQLite :

expo install expo-sqlite

 

Nous l’importerons dans nos fichiers de la manière suivante :

import * as SQLite from 'expo-sqlite';

Base de données & CRUD

CRUD : diminutif correspondant aux requêtes basique, à savoir Create, Read, Update et Delete.

Création de la base de données

Rien de plus simple, une seule ligne suffit :

On donne un nom à notre base, en argument. Celle-ci n’est crée qu’une seule et unique fois. Si on rappelle cette même méthode, on récupère la base de donnée crée auparavant, aucun risque de doublon.

 

Création de la table NOTE

On va maintenant créer notre table. Je vous montre la façon la plus simple de réaliser des transaction SQL vers notre base :

Requête SQL des plus basique avec le mot clé CREATE TABLE. On lui donne un nom de table, ainsi que la définitions de nos colonnes. Un ID auto-incrémenté pour garantir l’unicité de nos données, ainsi qu’un attribut TEXT qui contiendra le contenu de nos notes.

Nos deux attributs ne peuvent être null, et on leur défini un type, soit Text ou Integer.

 

Optimiser les requêtes

Je préfère une autre annotation que la précédente pour questionner la base de données, récupérer nos objets et leurs affecter des transformations. Je trouve plus clair et simple à l’utilisation. On définit notre modèle de requête avec une promesse :

On pourra utiliser ce modèle dans des fonctions async, via un appel par un await. On pourra utiliser des then() et catch() à l’appel du service dans nos vues, permettant par exemple d’afficher à l’utilisateur dans une popup si une note à bien été ajouté ou si dans le cas inverse afficher un message d’erreur avec son origine.

 

Récupérer toute les notes

On définit une interface pour les objets que l’on va récupérer en base. Cela nous facilitera leurs manipulations au sein de notre application :

 

On fait appel à notre modèle pour questionner la base :

On récupère nos objets via la requête. On va itérer sur notre résultat de requête pour re-typer correctement nos objets.

 

Ajouter une note

Seule différence, on va ici passer en argument le contenu de ma note que l’on souhaite persister en base. Pas besoin de lui passer un ID pour la note, car celle-ci est généré automatiquement en base. Nous avons défini cette option tout à l’heure, lors de la création de la table.

Supprimer une note

Quasiment identique au point précédent, sauf qu’ici on lui passe un ID de note à notre fonction, étant donné que c’est l’attribut qui défini l’unicité de mes items dans ma table NOTE :

 

Récupération des items dans la vue

Nous venons de créer notre base ainsi que des opérations CRUD permettant d’interagir avec elle. On va désormais créer une nouvelle vue, avec un champ de texte permettant de donner du contenu à une note, un bouton pour ajouter cette note, une liste scrollable permettant de naviguer sur l’ensemble de nos notes en base, et leur associé à chacun un bouton pour les supprimer.

 

Définition du state de notre vue

On commence par créer le squelette de base de notre vue :

On utilise ici un composant de classe et non un composant fonctionnel. On a besoin d’utiliser un state pour l’affichage dynamique du contenu de notre liste scrollable. On défini un attribut myNoteList correspondant à une liste de l’ensemble de nos notes en base, ainsi qu’un second attribut note correspondant au champ de texte remplissable par l’utilisateur pour créer une nouvelle note. On initialise les deux attributs dans le constructeur.

 

Définition des méthodes de notre vue

On défini les méthodes pour mettre à jour nos éléments, pour ajouter une note, ainsi que la supprimer :

La méthode componentDidMount() est une méthode standardisé de React, permettant d’être appelé une seule et unique fois et ce à la fin de la création du composant. On lui demande à l’ouverture de notre page, d’initialiser notre liste avec le contenu de notre base de données.

 

Définition des éléments visuels de notre vue

Ici rien de bien complexe. J’ai créer un titre de page, un champ de texte avec un bouton pour ajouter une note. Une liste scrollable présentant l’ensemble des notes en base, ainsi qu’une option permettant de les supprimer :

J’ai utilisé des icones Ionicons inclus dans Expo, qui s’adapte en fonction de si vous êtes sur Android ou iOS via la méthode Platform.OS, encapsulé dans des TouchableOpacity, pour rendre un peu plus esthétique mes boutons d’interactions, ainsi que des flexbox pour avoir une touche de responsive design.

 

Conclusion

Vous venez de voir comment créer très simplement une base de donnée pour votre application React Native tournant sous Expo, comment l’interroger et surtout comment récupérer et afficher le résultats dans une belle vue responsive.

 

android ios react native expo Cours web Mobile – React Native

Chap 1 : Environnement, prérequis & Hello world !

Résultat du cours

Objectif

  • Configurer l’environnement de développement
  • Générer le squelette basique d’une application
  • Tester son application sur son smartphone

 

ReactNativeCLI, ExpoCLI…

Nous avons des outils dans le développment web, permettant de développer rapidement un squelette de base pour une application :

  • CLI pour Angular
  • CRA pour React
  • CLI pour React Native

 

Mais pour créer mon premier projet pour découvrir ces technos mobile, je pars non pas sur ReactNative CLI, mais sur ExpoCLI. C’est comme Rails mais pour ReactNative, ou encore Phonegap pour Cordova. Il étends les fonctionnalité de ReactNativeCLI.

Qu’apporte t-il de plus par rapport à React Native CLI et quand l’utiliser ?

Avantage :

  • Pas besoin d’Android Studio ni de Xcode
  • Configuration bien plus simple & rapide ( hello world en moins de une minute )
  • Hot reload, sur smartphone physique et non par simulateur. Oui, via l’appli Expo, vous pouvez tester votre app sur votre smartphone iOS ou Android physique, et ce instantanément. En react native vanilla, vous avez droit au simulateur Android sur Windows, et simulateur iOS sous macOS exclusivement.

Inconvénient :

  • Poids de l’apk/ipa, du à l’ajout de librairie annexes (~50mo le hello world, ça fait mal…)
  • Accès aux modules natifs impossible. Vous ne pouvez ajouter de lib natif via Xcode ou Android studio, car tout est packagé via le sdk Expo. Certaines fonctionnalité sont impossible d’être utilisé, comme les achats in-app ou le bluetooth.

 

En résumé, pour débuter le dév mobile ou pour de simples application, vous devriez commencer par Expo. Pour des utilisateurs expérimentés ou souhaitant des fonctionnalités spécifiques à chaque plateforme, partez sur du ReactNative vanilla.

 

Prérequis

Il vous faudra de base :

  • NodeJS

Installez ensuite la CLI de Expo via une console :

npm install -g expo-cli

Pensez à avoir Python dans vos variables d’environnements, au risque d’avoir une erreur d’installation.

 

Initialisation du squelette

On initialise une nouvelle application via la commande :

expo init "Nom_de_votre_app"

 

Vous aurez l’architecture suivante :

  • assets/ : dossier contenant vos images
  • app.json : fichier de configuration du projet
  • app.tsx : fichier d’entrée de votre application
  • package-json : contient l’ensemble des dépendances et scripts du projet
  • tsconfig.json : fichier de configuration de typescript
  • babel.config.js : fichier de configuration pour la gen du bundle ( une sorte de webpack )

 

Lancement du serveur de dév

Simple :

npm start

Un nouvel onglet dans votre navigateur va s’ouvrir, vous proposant divers options :

 

Pour faire fonctionner le simulateur Android ou iOS, vous allez devoir installer les biblio natives. Mais on peut éviter cela en utilisant notre propre smartphone. On va devoir connecter notre smartphone au même réseau wifi que l’ordinateur sur lequel on développe.

Installez l’application EXPO, disponible sur l’iOS Store et GooglePlay store, selon votre type de device. Une fois l’application lancé sur votre smartphone, vous allez pouvoir scanner le QR code affiché sur la console, comme montré sur la capture d’écran précédent. Cela va permettre de transférer le bundle directement sur le smartphone.

Vous allez pouvoir ainsi voir vos modification en temps réel sur votre téléphone, de l’application que vous codez, à chaque sauvegarde.

 

Icone & Splash screen

Vous pouvez changer l’icone de votre application via le fichier « app.json ».

De même pour le splash screen, qui correspond a l’image qui sera affiché le temps que votre application soit chargé par votre smartphone.

react native chap1 splash screen

 

Conclusion

Nous venons de voir comment réaliser un « HelloWorld » en quelques minutes, et ce directement sur notre smartphone.

On discutera dans le prochain chapitre sur comment générer les fichiers APK et IPA, permettant d’installer et de distribuer notre application.

Cours logiciel - Electron

Chap 5 : Customisation de l’UI (light & dark…

Nous avons vu au chapitre précédent comment créer notre éditeur de texte, comment communiquer du render au main process via le module IPC afin d’interagir avec les fichiers disques via les API de NodeJS.

Pour ce cours-ci nous allons nous consacrer sur une fonctionnalité apprécié en ce moment, à savoir comment avoir un thème sombre et clair, et comment enregistrer ces données-ci afin de les récupérer à chaque ouverture du logiciel. Une rapide ouverture à la programmation réactive Observer/Observable via la librairie RxJS afin d’implémenter une fonctionnalité pour améliorer l’UX de notre logiciel, afin de renseigner le nom du fichier texte actuellement ouvert par l’utilisateur.

Ce cours sera donc principalement consacré à l’amélioration de l’UI pour l’utilisateur.

Le code source concernant ce chapitre est disponible sur mon Github.

Résultat du cours

 

Mise en place du système de persistance

Je vous propose d’ajouter une fonctionnalité, permettant de sauvegarder l’état du thème choisit, pour qu’il puisse être à jour à chaque lancement de l’application et que l’on ne soit pas obligé de le re-sélectionner. D’une manière général, je vous propose un moyen simple afin de sauvegarder et charger des paramètres utilisateurs en cache.

Pour sauvegarder des données, vous pouvez très bien créer une base de données plutôt traditionnel afin de stocker vos informations nécessaire au fonctionnement de l’application. Mais pour notre application qui reste extrêmement minimaliste, je vous propose la sérialisation des data dans un simple fichier JSON. Si vous souhaitez néanmoins rester sur une BD traditionnel, vous devriez regarder vers SQLite database, ou encore IndexedDB sur NPM.

 

Première chose, on va installer un simple module nous permettant de gagner du temps quand à la persistance de fichier JSON.

  • npm install electron-store –save

Souvenez vous que nos fichiers seront disponible dans le dossier %Appdata% de votre ordinateur, dans le dossier du nom de votre application.

On commence par vérifier si notre fichier JSON de sauvegarde existe au préalable sur l’ordinateur. Si celui-ci existe on ne fait rien en particulier. Dans le cas contraire, on va initialiser les valeurs des nos couples Clés/Valeurs. On souhaite avoir comme paramètre sauvegardé, le type de thème sélectionné ( Light, Dark ou Custom ) mais aussi les codes couleurs hexadécimal de nos différentes parties de notre application customizable, via le thème ‘Custom’. En effet, autant les thèmes Light et Dark sont pré-défini au sein de l’application et non modifiable, autant le thème Custom est modifiable lui.

On ajoute dans notre main process de Electron les bons imports qui vont bien :

L’argument ‘name‘ représente le nom du fichier JSON de sauvegarde, lors de la création de notre objet ‘storage‘.

 

On définit ensuite une fonction qui sera appelé dans notre fonction ‘createWindow‘ :

Comme je vous ai dit plus haut, si le fichier ‘settings.json‘ existe déjà, on ne fait rien. Dans le cas inverse, on va le créer et initialiser :

  • SETTINGS : clé
    • THEME_TYPE : clé, contient le type de thème à appliquer au lancement de notre app
    • CUSTO_PALETTE : clé, contient d’autres clés qui sont nos variables CSS avec leur code couleur hexadécimal qui leur sont associé

 

Thème clair & sombre

Il existe plusieurs façon de créer son propre système de thème pour son application. Nous allons voir une méthode très simple qui utilise les variables CSS. Au lieu d’affecter une couleur en dur à un composant HTML via son fichier CSS ( code en hexadécimal ou RGB ), on va désormais lui affecter une variable. C’est celle si que l’on va aller modifier selon no thème. Cela nous permet ainsi de bind plusieurs couleurs au même composant. Je vous propose de créer un thème clair, foncé, et un custom, auquel on pourra lui affecter au sein même de notre application une couleur modifiable à la volée.

 

Création des différents modèles de thèmes 

Premièrement, on va créer  une énumération définissant nos différents type de thème que l’on peut choisir:

 

Ensuite, nous  allons créer une interface permettant de créer des objets du même type :

 

Dans le même fichier précédent, on va maintenant créer 3 objects correspondant à nos 3 thèmes. Nous allons leur affecter un type ( Light, Dark ou Custom ) et leur définir l’ensembles des propriétés CSS que l’on souhaite modifier. Les propriétés CSS ont été réduite volontairement pour gagner en lisibilité, vous pouvez retrouver l’ensemble des propriétés du projet via son dépôt Github. Si on souhaite modifier par exemple seulement la couleur de fond de notre application :

Le mot clés export nous permet d’y accéder en dehors de ce fichier.

 

Mise en place de l’interface pour la sélection du thème

Pour la vue de notre page de paramètres, faisons quelque chose de simple. On va disposer d’un groupe de bouton radio, permettant une sélection unique du thème. Cela nous rampement de choisir entre Light, Dark et Custom. Les deux premiers thèmes ayant des couleurs définit dans notre application, seul le Custom pourra être modifiable, histoire de laisser un maximum de liberté à l’utilisateur dans le choix de ses couleurs. Pour la vue, rien de bien complexe :

On ajoute juste une condition, dans le cas ou l’utilisateur choisit le thème ‘Custom’, on affiche un sélecteur de couleur pour chaque item modifiable de notre application. Pensez d’ailleurs à installer ce module que j’apprécie fortement, un color picker simple qui permet de choisir une couleur en héxadecimal, en RGB ou via un arc-en-ciel de couleur, et qui propose une multitude de fonctionnalités via son api, installable via :

  • npm install material-community-components –save

Petit aperçu du module :

Plutôt sympa non ?😎

 

Passons maintenant au contrôleur de notre page de paramètres. Nous devons définir des attributs pour lier nos boutons précédemment expliqués :

  • availableTheme : Tableau contenant les thèmes disponible à la séléction, responsable de la création des boutons radio
  • aliasTypeTheme : cet alias est un bind à notre énumération précédente, qui contient les trois type de thèmes. Cela nous permet de pouvoir faire dans la vue notre comparaison, et afficher le color picker si et seulement si l’utilisateur à sélectionné le thème Custom
  • activeTheme : c’est le thème actuellement activé

 

Concernant le constructeur du composant, on va créer une instance de ElectronService, permettant de discuter du render process au main process et vice versa :

On initialise nos thèmes disponible pour créer nos boutons radio.

 

Système pour initialiser un thème au démarrage de l’app via Electron-store

On va déléguer la gestion des préférence utilisateurs à un service, un singleton. Créons le via la CLI de Angular :

  • ng generate service ThemeManager

On va lui définir un attribut correspond au thème actif, et un second correspondant lui aussi au thème actif, mais qui sera un BehaviorSubject ( le but de celui-ci et de mettre à jour le thème actif au lancement de l’application , lorsque celle-ci chargement au démarrage les préférences de l’utilisateur) :

 

Concernant le constructeur du service, on va créer une instance de ElectronService permettant de contacter le main process :

On initialise par des valeurs random nos deux précédents attributs (je vous l’accorde niveau QUALITÉ du code, on a fait mieux hein 😅). et on envoi une notification au main process sur le canal ‘loadUserSettings‘.

 

On va définir dans le main process la méthode permettant de lire dans notre fichier JSON de sauvegarde, les données des préférences (définit par notre clé ‘SETTINGS’, et de l’envoyer au render process via le cannal ‘responseLoadUserSettings‘:

 

On réceptionne dans le thème manager les données d’initialisation envoyé par le main process :

On actualise notre thème actif, la palette de couleurs pour le thème ‘CUSTOM’,  et on met à jour notre BehaviorSubject pour mettre la vue à jour concernant le thème actif, et enfin on appelle une méthode permettant d’appliquer notre thème :

On parcours les variables CSS du thème actuel, et on va les appliquer à notre document. Voilà, notre thème est appliqué au démarrage ! 😄

 

La dernière étape consiste à mettre à jour le modèle de nos boutons radio suite au chargement du thème initial. On ajoute une fonction dans le constructeur du composant de la page paramètres, afin qu’il soit notifié dès que le thème actif de notre service ThemeManager change. Voilà pourquoi je l’ai définit en tant que Observable, pour lui attacher un observateur. On va pouvoir subscribe a ce Subject de la façon suivante :

 

Système pour appliquer le thème courant

Maintenant que au lancement de notre application nos préférences sont lues et appliqués, on souhaite pouvoir changer de thème durant l’utilisation de notre application. On retourne sur le contrôleur de notre page de paramètre, et on ajoute la fonction suivante :

Cela va nous permettre de mettre à jour le thème actif pour nos boutons radio mais aussi de l’appliquer, via le ThemeManager.

 

Système pour sauvegarder le thème courant

Nouvelle fonctionnalités qui peut être cool, c’est de pouvoir enregistrer l’état courant de notre application, concernant le thème actif actuellement. Très simple, on va ajouter une seule ligne à la méthode expliqué juste avant, changeTheme() disponible dans le contrôleur de notre page ‘SETTINGS‘ :

Cela permet à chaque changement de thème, ou de couleur sur le color picker pour le thème CUSTOM ( car cette méthode d’actualisation est aussi appelé lors d’une modification de couleurs ) d’envoyer une notification à notre main process sur le canal ‘saveSettings‘, avec notre thème actuel en data.

Dans le main process, nous ajoutons une méthode permettant de réceptionner le thème actuel :

Et on met à jour notre fichier JSON contenant nos préférences utilisateurs, via Electron-store.

 

 

Indication de la page en cours d’exécution

Vous allez avoir besoin d’un nouveau module, permettant de faire de la programmation réactive :

  • npm install rxjs –save

Histoire d’améliorer l’interface utilisateur, je vous propose d’ajouter au centre de notre footer, une indication pour rappeler à l’utilisateur sur quel page il se situe au sein de notre application. Pour cela, on va définir une énumération contenant l’ensemble des pages de notre application :

 

Ainsi lorsque l’utilisateur va changer de page via les boutons de navigation situé en haut de notre application, on demandera une mise à jour du footer. Et pour cela, on va avoir besoin d’un service. On va créer ce service depuis la CLI de Angular :

  • ng generate service FooterUpdateService

 

Ce service va nous permettre d’échanger des données entre nos différents composants. Il se compose de la façon suivante :

On définit un BehaviorSubject. Ceci est un type de la librairie RxJs, qui propose de réaliser de la programmation réactive. Cela est grosso modo de la programmation qui suit le paradigme Observable et Observer. Ce menuStateSubject est un observable. Le ‘behavior‘ indique que c’est un observable, qui sera initialisé dès sa création avec un état pré-défini. Vous pouvez le voir dans le constructeur du service, on lui définit l’état HOME. Si vous souhaitez de pas lui définir d’état en particulier à sa création, vous pouvez utiliser simplement un Subject à la place du BehaviorSubject.

On a une première méthode, updateMenuStateSubject, permettant de lui envoyer un nouvel état.

La méthode getMenuStateObservable permet de renvoyer notre observable, nous l’utiliserons juste après.

 

Le but de ces observables, et d’y attacher des observateurs, ou juste de pouvoir subscribe au sujet. Cela permet de notifier à chaque changement d’état de notre Observable,  ses observateurs et de pouvoir faire des actions en conséquence.

 

On va ajouter dans le contrôleur de notre Header, une fonction permettant de mettre à jour l’état de notre Observable, définit dans le service précédent :

N’oubliez pas d’ajouter au constructeur de notre contrôleur Header,  l’appel au singleton de notre service via ‘public footerService: FooterServiceUpdate‘. On ajoute un argument à cette fonction, du type de l’énumération précédemment crée, afin de déterminer quelle page on ouvre.

 

On ajout ensuite le bind (click) dans notre vue sur chacun de nos boutons, qui nous permettent de changer de page. Ils permettent dorénavant de mettre à jour notre observable :

 

On ajoute dans la vue du footer une variable permettant d’afficher le contenu d’une variable qui sera définit dans son contrôleur :

 

On va ajouter dans le contrôleur du footer, l’attribut précédemment utilisé pour l’affichage de l’état courant du menu :

 

Et voilà le résultat !

Résultat de l’actualisation du footer

 

 

Conclusion

Nous venons de voir quelques pistes afin d’améliorer l’UI pour notre application, ainsi qu’une piste pour sauvegarder les préférences de l’utilisateur. Nous aurons aussi vu une rapide approche sur la bibliothèque RxJS, permettant de suivre le paradigme Observable/Observer.

 

Le 6 eme et dernier chapitre de cette formation est consacré au build & package de notre application, afin de pouvoir la distribuer comme n’importe quel logiciel.

 

Cours logiciel - Electron

Chap 4 : Affichage pour l’édition, écriture de fichier…

Nous avons vu au chapitre précédent comment réaliser un simple appel à NodeJS via le module de communication IPC, afin d’éffectuer un appel au système de fichiers pour nous remonter des fichiers textes disponible dans un dossier précis.

Nous allons maintenant nous attaquer à une nouvelle fonctionnalité, nous permettant lors qu’un clique sur un fichier affiché dans notre explorateur de fichier, d’afficher son contenu dans un nouvel espace.

Le code source concernant ce chapitre est disponible sur mon Github.

Résultat du chapitre

Affichage d’un fichier

Notifier le main process

On va reset les classes permettant de surligner le fichier sélectionné, sur l’ensemble de nos fichiers listé dans notre explorateur.

On va ensuite activer seulement le surlignage pour le fichier actuellement sélectionné.

On envoi une notification au main process via le module IPC, sur le canal pingDisplayFile, en lui envoyant en donnée le chemin relatif de ce fichier.

 

Réponse du main process

On écoute sur ce même canal avec le module IPC.

On va lire le contenu du fichier, via son path précédemment envoyé, via le module fs de Nodejs.

Je récupère aussi le nombre de lignes que contient le fichier, cela nous permettra d’indiquer les lignes dans notre éditeur de texte.

Je créer un objet qui réunit ces deux résultats, afin de l’envoyer au render process via le canal responseFileContent.

 

Affichage du fichier dans le render process

La première fonction me permet de mettre mon attribut fileContent à jour via le nouveau fichier précédemment envoyé, ainsi que l’attribut lineNumber pour le compteur de nombre de ligne.

La seconde fonction va permettre de sauvegarder notre nouveau contenu affiché, et de l’enregistrer sur notre disque à la place de notre ancien fichier. On envoi une notification via le canal saveFile, en lui donnant en paramètre un object qui contient notre fichier, son contenu ainsi que son chemin d’accès.

 

On va ensuite construire notre page HTML pour affiche d’une part le contenu de notre fichier d’une façon éditable, mais aussi d’avoir à proximité un compteur de ligne pour se référer.

Cette disposition de flexbox me permet d’avoir deux colonne, via fxLayout= »row » :

  • lineCounterDisplay : est la partie pour le compteur de ligne. Le trick est de créer une division par chiffre de ligne via le bind angular *ngFor. Pour cela j’ai créer un tableau qui contient n item qui est le nombre de ligne du fichier, et qui sont incrémenté de 1 entre chaque item. Le contenu est bindé via J’ai réaliser via la fonction suivante :

 

  • textEditorDisplay : est la partie pour l’édition du fichier texte. Rien de bien spécial, c’est juste un textArea, qui permet d’afficher et de taper du texte. On le bind avec [(ngModel)] pour l’associer à l’attribut de notre contrôleur qui contient les données du fichier texte, envoyé auparavant par notre main process. On utilisera un second bind, (input), qui sera affecté à la fonction précédemment présenté. Celle-ci permet de déclencher la fonction à chaque changement fait dans le textArea. Cela permet de recalculer en temps réel le compteur de ligne à afficher.

 

Sauvegarde du fichier sur le disque

On utile le module fs de NodeJS pour sauvegarder notre fichier fraîchement modifié.

 

Conclusion

Vous pouvez rendre plus complexe votre éditeur de fichiers en y ajoutant certaines autres fonctionnalités tel que :

  • Une sauvegarde automatique via un timer, ou même à chaque ajout/suppression du moindre caractère ?
  • Proposer des outils afin de customiser la couleurs, taille, ou type de police ?

 

Direction pour le prochain chapitre qui abordera comment créer des thèmes clair et sombre ( pour reposer les yeux la nuit ), comment gérer le stockage de données utilisateurs, et enfin un rapide tour sur la programmation réactive via le paradigme Observer/Observable via la bibliothèque RxJS !

 

 

Cours logiciel - Electron

Chap 3 : Explorateur de fichier redimensionable, lecture de…

Préface

Nous avons vu au chapitre précédent comment réaliser la barre d’outils principal du logiciel.

Nous allons maintenant rentrer dans le vif du sujet pour la réalisation d’un explorateur de fichier texte basique.

Le code source concernant ce chapitre est disponible sur mon Github.

Résultats du cours

 

Explorateur de fichiers redimensionable

C’est tout bête, mais il va arriver à un moment ou à un autre que l’on tombe sur des noms de fichiers plus ou moins long, donc certains pourrait être tronqué. Autant faire quelque chose de propre, et donner la possibilité à notre explorateur d’être redimensionable par l’utilisateur.

Depuis le dernier chapitre, j’ai légèrement modifié l’architecture du projet afin de le rendre plus maintenable pour la suite des tutoriels. En voici une présentation simplifié :

Diagramme général
  • Module App
    • appComponent.ts
    • appComponent.html
    • appComponent.scss
    • appRouting.ts
    • Module Settings
      • settingsComponent.ts
      • settingsComponent.html
      • settingsComponent.scss
      • settingsRouting.ts
    • Module Home
      • homeComponent.ts
      • homeComponent.html
      • homeComponent.scss
      • homeRouting.ts
    • Module Projects
      • projectsComponent.ts
      • projectsComponent.html
      • projectsComponent.scss
      • projectsRouting.ts
        • File Explorer
          • fileExplorerComponent.ts
          • fileExplorerComponent.html
          • fileExplorerComponent.scss
        • File Editor
          • fileEditorComponent.ts
          • fileEditorComponent.html
          • fileEditorComponent.scss

 

 

Pour la partie graphique de notre file explorer, il va être composé d’une toolbar pour permettre d’ouvrir un dossier, une partie pour afficher nos fichier en colonne, et enfin un grabber nous permettant de modifier la taille de notre fenêtre :

On va utilise une directive de angular pour nous permettre de bind la largeur de notre file explorer [style.width.px]=’divWidth’.

Pour la partie de style, rien de bien sorcier, juste de prévoir de changer le curseur de souris lorsque l’on passera sur le grabber afin de faire remarquer à l’utilisateur que la fenêtre est modifiable :

 

La partie la plus intéressante et ou se passe la magie de notre grabber est dans notre contrôleur. On va définir :

  • un boolean, pour savoir si on a le clique enfoncé ou non,
  • une largeur de fenêtre,
  • une ancienne largeur de fenêtre pour connaître les déplacements.

 

Dernière étape, on va devoir ajouter des listener d’actions :

  • Un pour savoir quand on bouge la souris,
  • un autre pour savoir quand on enfonce le clic,
  • et un dernier pour savoir quand on relâche le clic

Dans l’ordre, cela va nous pouvoir de modifier la taille en temps réel, de savoir quand effectuer cette action, et savoir quand l’arrêter :

 

 

Explorateur basique de fichier texte

On souhaite avoir  un système qui puisse lister l’ensemble des fichiers textes d’un dossier, et nous l’afficher sous une forme de liste au sein de notre logiciel. Cela nous permettra d’un simple clique de pouvoir ouvrir tel ou tel fichier texte à modifier.

 

Demande de fichier depuis le Frontend

On va ajouter un bouton nous permettant à son click, de notifier le backend afin d’ouvrir une fenêtre de dialogue pour sélectionner un dossier, dans lequel on souhaite récupérer l’ensemble des fichiers textes qui y sont situé. Rien de bien complexe pour la partie graphique, juste un bouton contenant une icone, qui à son click sera bindé avec l’appel de ‘openFolderDirectory()’. L’utilisation de la flexbox nous permet de positionner le boutton sur le côté gauche de la div.

 

Dans le contrôleur on va ajouter la fonction précédente. Celle-ci fait appel au module ipcRender, qui permet la communication de message du renderProcess vers le mainProcess. Il permet d’envoyer des messages via la fonction ‘send’, ou d’écouter via ‘on’. Cela fonctionne exactement comme des sockets, si vous en avez déjà utilisé. Dans notre cas on va juste envoyer un message vide, une sorte de ping pour exécuter une fonction.

 

Traitement de la demande par le Backend

On va réceptionner la notification venant du front via le module ipcMain dans notre backend, avec sa méthode ‘on’.  La fonction prend un argument ‘event’, permettant de renvoyer un message, ainsi qu’un second argument ‘message’ contenant des données envoyé. Mais rappelez vous, on a juste ping sans envoyer le donnée, cet argument sera donc vide.

Le module dialog va nous permettre de créer une fenêtre de dialogue, avec en argument notre fenêtre principal, suivit d’un dictionnaire d’options permettant de définir si l’on souhaite ouvrir un dossier, un fichier, de définir un nom de fenêtre en particulier, etc.

Cette fonction nous renvoi une promesse, sur laquelle on va pouvoir lui attache deux bloc :

  • .then() : est appelé si l’ouverture du dialog se passe correctement,
  • .catch() : est appelé si une erreur est lancé lors de son ouverture.

Dans le cas ou on a une erreur, on la remonte simplement dans la console du back.

Dans le cas ou tout se passe bien, on va vérifier que l’utilisateur à bien choisit un dossier et n’a tout simplement pas annulé son action par la fermeture de boite de dialogue.  On va alors appelé le module fs de Node qui permet de réaliser des opérations de lectures et d’écritures sur le disque. On va dans un premier temps récupérer l’ensemble des fichiers contenu dans le dossier renseigné pour l’utiliser, puis lui appliquer un filtre via une regexp, permettant de garder seulement les fichiers dont leurs noms se termine par .txt.

On va utiliser le premier argument pour pouvoir répondre au front, en lui envoyant dans un nouveau canal, un tableau contenant les noms de fichiers textes étant dans son dossier de sélection.

 

Affichage de la réponse dans le Frontend

On va réceptionner la réponse venant du Backend via une fonction écoutant sur le même canal que celui utilisé pour l’émission. On va récuperer le tableau de données, et l’associer à un attribut de composant que l’on aura déclaré au préalable.

 

Maintenant que l’on a nos données, on a plus qu’a les afficher comme une liste dans notre vue. On utilisera une mat list item, et on utilisera le bind *ngFor de Angular pour parcourir l’ensemble des items de notre tableau de données.

 

Un petit plus pour l’ésthetisme

Vous avez vu le binding [ngClass] ? C’est une directive de Angular, permettant de lui associer une classe CSS en plus (highlight, dans mon cas), si la condition file.highlight est respecté, soit si et seulement si le booléan est à true. Lorsque l’utilisateur clique sur un fichier, une fonction sera appelé. Celle-ci mettra à jour le boolean de l’ensemble des fichiers à faux, et mettra à true sur celui qui a été sélectionné.

Si vous regarder le code, concrètement cela permet lors d’une sélection d’un fichier, de lui ajouter une couleur plus clair que les autres, pour renseigner de façon plus jolie à l’utilisateur, sur quel fichier il est. Cela aura d’avantage de sens lors du prochain chapitre vous verrez 😉

 

Conclusion

Vous pouvez rendre plus complexe votre explorateur en y ajoutant certaines fonctionnalités comme :

  • Pouvoir remonter dans le dossier parente : pour cela vous n’avez qu’a juste ajouter un bouton ‘parent’, qui va ré-appeler notre fonction de ping du backend, mais en lui envoyant un chemin avec un niveau plus haut.
  • Possibilité d’ajouter un logo à côté de chaque fichier, en fonction de leur type. Vous aurez juste besoin d’une fonction qui split le nom d’un fichier, et qui compare l’extension, et affiche un type d’icone en fonction de celle-ci. A faire soit directement dans la vue avec un binding *ngSwitch, soit d’ajouter un nouvel attribut dans notre FileType.

 

Le prochain chapitre portera sur l’ouverture d’un fichier texte dans notre éditeur, pour pouvoir le modifier et le sauvegarder sur le disque.
 

 

Cours logiciel - Electron

Chap 0 : Présentation de Electron

Présentation

Electron est un framework permettant de développer des applications bureaux multi plateforme ( Linux, Windows, MacOS ) avec des technologies web ( HTML, CSS et Typescript/Javascript ). Il est open source et permet de réaliser très rapidement des applications. Vous pensez que cela n’est pas possible ? Et pourtant vous en utilisez surement sans même le savoir ; Atom, Visual studio, Slack pour n’en citer que les plus gros.

Vous allez donc développer votre application comme si vous développiez un site web.

 

Composition de Electron

composition electron nodejs chronium

 

Electron embarque plusieurs outils/bibliothèque pour permettre d’avoir les mêmes accès qu’un logiciel développé avec un langage plus adapté et/ou de plus bas niveau :

  • Chronium : c’est le navigateur open source qui sert de base au célèbre Chrome de Google. Il va assurer le rendu visuel de l’application.
  • NodeJS : c’est un environnement d’exécution de code javascript. Il permet l’accès au système de fichier de l’ordinateur, ainsi que le réseau.
  • APIs Natives : permet l’accès aux fonctions natives, propres à chacun des OS.

 

Fonctionnement global

Le développement en est extrêmement simplifié, mais aussi accéléré, car vous aurez accès à plus de 300 000 modules sur NPM. C’est une sorte d’hébergeur de module, qui permet de réaliser certaines tâches. Vous ajoutez donc en quelques secondes de nouvelles fonctionnalité sur votre application.

D’autant plus que vous pouvez ajouter un framework pour le frontend pour structurer votre application : Angular, React, VueJS…

 

Vous allez avoir deux processus différent pour faire fonctionner une application tournant sous Electron :

 

  • Main process

C’est le point d’entrée de votre application. Il va contrôler le cycle de vie de l’application. Vous aurez tout les accès depuis ce processus, via les API native ou de NodeJS. Il peut aussi créer de nouveau processus de rendu, ainsi que de démarrer et de quitter l’application.

Ce processus est unique

 

  • Render process

Il va être responsable de la vue de votre application, par le biais d’affichage de vos pages HTML/CSS. Vous aurez accès au javascript pour gérer les contrôleurs et interactions. Mais attention, pas d’accès direct au système.

Chacun des processus de rendu sont indépendant les uns des autres. Si un crash, il n’affecte pas ses voisins. Il peut être caché, permettant d’exécuter du code en arrière plan.

Ce processus peut être multiple.

 

/!\ L’ensemble des fonctionnalités disponible par l’API de Electron ne sont pas forcement accessible depuis les deux types processus. Certains ne seront garanti que dans un seul des deux type de processus.

 

Communication entre Render et Main process

Electron à mit en place un module, appelé IPC, permettant de réaliser une communication ainsi qu’un échange de données entre main et render process, qui est appelable depuis chacun des processus. Cette communication fonctionne sous forme de canaux, et l’échange est bi-latéral. Celle-ci s’apparente à des sockets.

 

Architecture d’une application Electron

Le schéma suivant montre d’une façon simplifié le fonctionnement de base d’une appli Electron.

Architecture simplifiée
Architecture simplifiée
  1. Le package.json est le point d’entrée de votre application. Il va indiquer à Electron ou est le main process,
  2. Le main.js définit votre processus principal. Il va créer la fenêtre graphique pour y appeler le render process.
  3. Le index.html définit votre vue.
  4. Le module IPC permet l’échange d’informations entre les divers processus.

 

Conclusion

Points positifs

  • Stack web facile à apprendre
  • Dév rapide ( hot reload, console chronium, modules NPM… )
  • Cross-platform

Points négatifs

  • Consommation excessive de RAM
  • Taille du bundle ( ~100Mo pour un simple ‘Hello World !’ )

 

Maintenant que vous voyez le fonctionnement global d’un projet sous Electron, je vous propose d’expérimenter vous même, et de réaliser un traitement de texte basique sur le chapitre suivant.

 

 

Cours logiciel - Electron

Chap 2 : Barre d’outils, appel de l’API de…

Préface

Nous avons vu au chapitre précédent comment créer une frameless window basique.

Nous allons y ajouter quelques fonctionnalités de base que doit avoir un logiciel, via sa barre d’outils.

Le code source concernant ce chapitre est disponible sur mon Github.

Résultats du cours

 

Hot reloading du backend

Autant le hot reload des pages web du front se font automatiquement via le module Webpack contenu dans Angular, autant le backend ne s’effectue pas. Pour cela on va ajouter un module npm dans notre projet :

  • npm install electron-reload

Il va nous permettre de créer un nouveau main process de Electron, en lui donnant simplement en argument le chemin de l’exécutable de electron.

Nous lui ajouterons un argument pour permettre un hard reset du module, ce qui évite d’avoir des processus de Electron fantôme qui peuvent persister.

Et enfin un dernier argument, nous permettant de pouvoir injecter des arguments au lancement de electron, et dans notre cas de garder dans notre environnement de dév, le lancement des dev tools de chronium.

 

Barre d’outils via l’API de Electron

Définition de la vue, via des flexbox 

On va introduire des notions de responsive design qui est propre aux stack du web. Ceci nous permet de rendre adaptable la vue d’une page en fonction de la hauteur et largeur de l’écran de l’utilisateur, et ainsi d’en modifier sa disposition. On parle alors de Flexbox. Celles-ci sont déclaré dans les pages CSS, et permette de définir des règles de disposition entre chaque éléments ( des <div> par exemple ). Cela peut définir des règles pour indiquer comment tel ou tel élément doit grossir, réduire, ou encore se disposer en ligne ou colonne avec ses éléments voisin. Un petit module que j’apprécie et qui est disponible sur NPM, va nous permettre d’induire ces flexbox, directement dans les balises du code HTML de la page :

  • npm install @angular/flex-layout

 

On souhaite avoir une barre d’outils comme ceci :

  • Partie gauche :
    • Une icone du logiciel avec un bouton d’accueil
  • Partie du milieu :
    • Le nom du logiciel
  • Partie droite :
    • Une barre d’outils avec des boutons permettant de réduire, de minimiser/maximiser la fenêtre, ainsi qu’un dernier pour fermer la fenêtre

 

1 – Contener Global

On va commencer par créer un contener global ( notre mat-toolbar ), qui va prendre le maximum d’espace possible de son parent, définit par la directive fxFill :

 

2 – Création des 3 sous conteners (définit précédemment)

On utilise la directive fxLayout=’row’ afin de créer 3 conteners sur la même ligne. Quand a fxLayoutAlign=’space-between’, elle va nous permettre de définir le type d’espacement entre chacun d’eux. Celle-ci nous permet de les espacer au maximum des un aux autres.

 

3- Alignement vertical d’un des trois sous conteners

On souhaite qu’ils soient aligné au milieu ce leur ligne. On va donner l’exemple pour le contener de droite. Pour cela, on va ajouter au contener précédent, la directive fxLayout=’column’ pour pouvoir créer des conteners de façon vertical ( rappeler vous que le row permet d’aligner des conteners de façon horizontal), avec le bon fxLayoutAlign=’center’ qui va bien, pour permettre de les aligner au milieu au seins de celui-ci.

Si on reprend notre cheminement depuis le début, on doit normalement avoir un contener fixé à droite de la barre, et qui sera aligné au milieu concernant son axe vertical. On souhaite maintenant avoir y incorporer 3 bouttons d’actions.

 

4 – Boutons d’actions

On va recréer un contener de type row cette fois-ci, nous permettant de grouper l’ensemble de nos trois boutons de façon horizontal. En effet avec le point précédent, nous étions dans un contener de type column, et donc aligné sur l’axe vertical, chose que l’on ne souhaite pas.

Vous n’avez plus qu’a ajouter vos trois boutons, avec l’appel aux fonctions qui seront déclaré dans le contrôleur, via la directive de angular, (click)=’votre_fonction()’.

 

Pour un peu d’esthétisme, j’ai rajouté une classeclass=button hoverBtnWhite, lié dans le fichier CSS, permettant qu’au passage de la souris, la couleur de l’icone et de son background change.

 

 

Définition du contrôleur, via le service Electron

Maintenant que la partie graphique est mise en place, on va passer au contrôleur, permettant d’ajouter des actions à nos jolis boutons 😉

On va ajouter un nouveau module, nous permettant d’accéder à l’API de Electron directement depuis notre contrôleur.

  • npm install ngx-electron --save

On l’importe dans notre module principal, soit App :

import {NgxElectronModule} from 'ngx-electron';

Et on le déclare dans la partie des imports :

import: [ NgxElectronModule ]

Dans notre composant Header.ts, nous aurons besoin d’importer le module ElectronService. En créer un attribut de classe, l’instancier lors de la construction du composant, et se servir du module REMOTE de l’API de Electron. On va alors pouvoir utiliser via ce module, les utilitaires du process main, depuis le render process. Le code suivant vous montre comment lier nos boutons créer précedemment pour leur affecter respectivement les actions  suivantes :

  • fermer la fenêtre,
  • réduire la fenêtre,
  • maximiser,
  • unmaximiser.

 

Materials icons en offline

Si vous utilisez des icons de la librairie Material, soit celle de base de Angular, vous allez les télécharger à chaque lancement de l’app. Cependant, le jour ou vous voulez déployer votre application hors ligne, plus rien de marche, et les messages d’erreurs ne sont pas tellement explicite, vous êtes obligé d’aller chercher dans les requetes HTTP. Pire si comme moi vous avez du déployer une app offline sur iPad, sans n’avoir de console de développeur de iOS, alors autant prévoir les choses à l’avance. On va utiliser un module disponible sur NPM pour pouvoir toujours les avoir dans notre app :

  • npm install material-design-icons-iconfont --save

Et ajoutez les lignes suivantes dans votre fichier de style globale de votre app, soit style.scss :

 

Conclusion

Nous venons de voir comment appeler l’API de Electron depuis notre front en Angular, pour lui ajouter des fonctionnalités simple d’un logiciel.

Nous verrons au prochain chapitre comment créer un explorateur de fichier simple, pour présenter le module ipc de Electron, permettant de communiquer et d’échanger des données entre main et render process.

 

 

Cours logiciel - Electron

Chap 1 : Fenêtre principale, frameless bar, icon, lancement…

Préface

Je vais vous présenter comme installer votre environnement de développement, et créer votre première fenêtre, avec quelques astuces de dév, vous permettant d’accélérer vos rendus.

Le code source concernant ce chapitre est disponible sur mon Github.

Résultat du cours

 

Installation des pré-requis

Vous devrez avoir NodeJS d’installé. Je vous renvoi sur un précédent article qui vous explique les démarches à suivre.

 

Initialisation d’un nouveau projet

On va initialiser une nouvelle application angular avec tout le squelette de base qui va bien, afin de nous faire gagner du temps :

  • ng new Nom_de_votre_app

note : ng est une commande de la CLI de Angular

Sélectionner Yes pour avoir le routing du module de base,

Sélectionner ensuite votre langage pour les feuilles de style, je prends SCSS pour ma part.

 

On va ajouter Electron à notre projet via :

  • npm install --save-dev electron

 

Création de la fenêtre principale

On va créer un fichier main.js. C’est lui qui va servir de fichier principal pour créer notre fenêtre, avec le code suivant :

On va ensuite mettre à jour notre fichier package.json pour lui indiquer le point principal d’entrée pour Electron an ajoutant la ligne :

"main": "main.js"

 

De retour sur la console, déplacer vous au sein du projet, vous allez pouvoir lancer votre application dans electron via la commande :

  • electron .

 

Tadaaaaaaaaaaaaa. Ça ne casse pas 3 pattes à un canard, mais ça à le mérite d’être du développement plutôt rapide ! 😂

Première fenêtre sous Electron !

 

Liaison du serveur de dév de Angular vers Electron

Nous n’avons fait que lier de façon statique la page index.html. Cependant, pour le bon fonctionnement de Angular, il va nous falloir un serveur HTTP qui gère le typescript. Celui de base de Angular fonctionne très bien.

On va modifier notre fichier main.js pour qu’il prenne non plus un fichier en entrée, mais une URL qui pointe vers notre serveur de développement.

Lancer depuis une console le serveur de développement de Angular :

  • ng serve

 

Lancer ensuite depuis une seconde console electron. Vous avez désormais accès à Angular depuis votre application Electron. Vous pouvez avoir accès au rechargement à chaud ( mise à jour de l’UI en direct dès une modification du code ) directement dans Electron.

Angular tourne dans Electron

 

Lancement parallèle 

Pour éviter d’avoir deux console, on va pouvoir automatiser le lancement de Electron et Angular depuis un script.

Dans le fichier package.json, rajoutez les scripts suivants :

La première permet de lancer electron. La seconde quant à elle permet de lancer le serveur local de développement de Angular et Electron de façon concurrentielle.

 

Customization de la fenêtre

C’est dans l’ère du temps, donnons un peu de style à notre application 😎

On va supprimer la barre d’outils toute moche, et y incorporer une toolbar un poil plus joli, qui nous permettra de déplacer notre app, ainsi que de l’agrandir via une double tap.

Ajoutons la librairie de base pour les material UI :

  • ng add @angular/material

Nous n’avons pas besoin de HammerJS, mais belle et bien cependant des browser animations pour Angular material, qui seront à préciser à la suite de cette commande.

 

On va ajouter notre toolbar à notre fichier HTML du composant App

<mat-toolbar class="menu">My App</mat-toolbar>

 

Ajouter à notre module principal App le bon import pour la librairie Material :

import { MatToolbarModule} from '@angular/material'

 

Et ajouter MatToolbarModule dans la déclaration du Module principal, dans la partie Import

 

 

Lancement instantané de l’app ( white blank screen )

Quand on lance l’application, on a un écran blanc temporaire qui s’affiche. C’est une chose que l’on ne souhaite pas avoir au sein de notre application une fois buildé. C’est pour cela que l’on va modifier notre fichier main.js pour demander à Electron d’afficher notre fenêtre seulement une fois que celle-ci sera entièrement chargé, ce qui nous donnera l’impression d’avoir une ouverture quasi instantané.

Pour cela on va créer notre fenêtre et demander à Electron de la cacher dans un premier temps. Ajoutez la ligne suivante en paramètre de création lors de l’appel de la méthode :

  • show: false

On va ensuite rajouter un event, qui sera appelé une fois que la fenêtre sera prête :

Notre application va désormais se lancer directement.

 

Icone de l’application

Pour changer l’icone de votre application sur le bureau de windows, ajouter l’option lors de la création de la fenêtre avec le lien pointant vers une image placé dans votre dossier d’assets :

  • icon: './src/assets/icon/icon_transparent.png'

 

Conclusion

Nous venons de créer très simplement et rapidement une simple fenêtre, avec un chouette esthétisme.

Le prochain chapitre va permettre d’y ajouter de nouvelles fonctionnalités, concernant la barre d’outils.