Avez-vous déjà voulu apprendre à créer une application d'une seule page avec Sinatra et Knockout.js? Eh bien, aujourd'hui est le jour où vous apprenez! Dans cette première section d'une série en deux parties, nous passons en revue le processus de création d'une application de liste de tâches d'une page où les utilisateurs peuvent afficher leurs tâches, les trier, les marquer comme terminées, les supprimer, les rechercher et les ajouter. nouvelles tâches.
Selon leur site web:
Sinatra est un DSL permettant de créer rapidement des applications Web en Ruby avec un minimum d'effort..
Sinatra vous permet de faire des choses comme:
get "/ task / new" do erb: form end
Il s’agit d’une route qui traite les demandes GET pour "/ task / new" et rend une erb
formulaire nommé form.erb
. Nous n'utiliserons pas Sinatra pour le rendu des modèles Ruby; au lieu de cela, nous l’utiliserons uniquement pour envoyer des réponses JSON à notre frontal géré Knockout.js (et à certaines fonctions utilitaires de jQuery comme $ .ajax
). Nous n'utiliserons erb que pour rendre le fichier HTML principal.
Knockout est un framework JavaScript Model-View-ViewModel (MVVM) qui vous permet de conserver vos modèles dans des objets "observables" spéciaux. Il maintient également votre interface utilisateur à jour, en fonction de ces objets observés.
-ToDo / -app.rb -models.rb --views / -index.erb - public / --- scripts / - knockout.js - jquery.js - app.js --- styles / - styles.css
Voici ce que vous allez construire:
Nous commencerons par définir notre modèle, puis nos actions CRUD dans Sinatra. Nous nous baserons sur DataMapper et SQLite pour le stockage persistant, mais vous pouvez utiliser n’importe quel ORM que vous préférez..
Ajoutons un modèle de tâche à la modèles.rb
fichier:
DataMapper.setup (: default, 'sqlite: ///path/to/project.db') Classe Tâche include DataMapper :: Propriété de la ressource: id, Propriété série: complete, Propriété booléenne: description, Propriété du texte: created_at, Propriété DateTime : updated_at, DateTime end DataMapper.auto_upgrade!
Ce modèle de tâches consiste essentiellement en quelques propriétés différentes que nous voulons manipuler dans notre application de tâches à faire..
Ensuite, écrivons notre serveur Sinatra JSON. dans le app.rb
fichier, nous allons commencer par demander quelques modules différents:
Requiert 'rubygems' Requiert 'Sinatra' Requiert 'Data_mapper' Requiert File.dirname (__ FILE__) + '/models.rb' Requiert 'json' Requiert 'Date'
L'étape suivante consiste à définir des valeurs globales par défaut. en particulier, nous avons besoin d'un type MIME envoyé avec chacun de nos en-têtes de réponse pour spécifier que chaque réponse est JSON.
avant que content_type 'application / json' se termine
le avant
la fonction d'assistance s'exécute avant chaque correspondance d'itinéraire. Vous pouvez également spécifier des itinéraires correspondants après avant
; Si, par exemple, vous vouliez exécuter des réponses JSON uniquement si l'URL finissait par ".json", vous utiliseriez ceci:
avant% r . + \. json $, tapez content_type 'application / json' end
Ensuite, nous définissons nos itinéraires CRUD, ainsi qu’un itinéraire pour desservir nos index.erb
fichier:
get "/" do content_type 'html' erb: index end get "/ tasks" do @tasks = Tâche.all @ tasks.to_json poste suivante "/ tasks / new" do @task = Tâche.new @ task.complete = false @ task.description = params [: description] @ task.created_at = DateTime.now @ task.updated_at = null fin "/ tasks /: id" do @task = Task.find (params [: id]) @task. complete = params [: complete] @ task.description = params [: description] @ task.updated_at = DateTime.Maintenant si @ task.save : task => @task,: status => "success". to_json else : task => @task,: status => "failure". to_json end end delete "/ tasks /: id" do @task = Task.find (params [: id]) si @ task.destroy : task = > @task,: status => "success". to_json else : task => @task,: status => "échec". to_json end end
Alors le app.rb
Le fichier ressemble maintenant à ceci:
require 'rubygems' require 'sinatra' require 'data_mapper' require File.dirname (__ FILE__) + '/models.rb' require 'json' require 'Date' avant do content_type 'application / json' end get "/" do content_type " html 'erb: index end obtenir "/ tasks" do @tasks = Task.all @ tasks.to_json message "/ tasks / new" do @task = Task.new @ task.complete = false @ task.description = params [ : description] @ task.created_at = DateTime.now @ task.updated_at = null si @ task.save : task => @task,: status => "success". to_json sinon : task => @task,: status => "échec". to_json end end put "/ tasks /: id" do @task = Task.find (params [: id]) @ task.complete = params [: complet] @ task.description = params [ : description] @ task.updated_at = DateTime.Maintenant si @ task.save : task => @task,: status => "success". to_json else : task => @task,: status => "échec" .to_json end end delete "/ tasks /: id" do @task = Task.find (params [: id]) if @ task.destroy : task => @task,: status => "success". to_json else : tâche => @task,: status => "échec" .to_json end end
Chacune de ces routes correspond à une action. Une seule vue (la vue "Toutes les tâches") regroupe toutes les actions. Rappelez-vous: en Ruby, la valeur finale est renvoyée implicitement. Vous pouvez explicitement revenir plus tôt, mais quel que soit le contenu renvoyé par ces itinéraires, la réponse sera envoyée par le serveur..
Ensuite, nous commençons par définir nos modèles dans Knockout. Dans app.js
, Placez le code suivant:
fonction Tâche (données) this.description = ko.observable (data.description); this.complete = ko.observable (data.complete); this.created_at = ko.observable (data.created_at); this.updated_at = ko.observable (data.updated_at); this.id = ko.observable (data.id);
Comme vous pouvez le constater, ces propriétés sont directement mappées sur notre modèle dans modèles.rb
. UNE ko.observable
maintient la valeur mise à jour à travers l'interface utilisateur lorsqu'elle change sans qu'il soit nécessaire de faire appel au serveur ou au DOM pour suivre son état.
Ensuite, nous allons ajouter un TaskViewModel
.
function TaskViewModel () var t = this; t.tasks = ko.observableArray ([]); $ .getJSON ("/ tasks", fonction (raw) var taches = $ .map (raw, fonction (item) retour nouvelle tâche (item)); self.tasks (tâches);); ko.applyBindings (new TaskListViewModel ());
C'est le début de ce qui sera la viande de notre application. Nous commençons par créer un TaskViewModel
fonction constructeur; une nouvelle instance de cette fonction est transmise au knockout applyBindings ()
fonction à la fin de notre dossier.
À l'intérieur de notre TaskViewModel
est un appel initial pour récupérer des tâches de la base de données, via l’URL "/ tasks". Ceux-ci sont ensuite mappés dans le ko.observableArray
, qui est mis à t.tasks
. Ce tableau est le coeur de la fonctionnalité de notre application.
Nous avons donc maintenant une fonction de récupération qui affiche les tâches. Créons une fonction de création, puis créons notre vue de modèle réelle. Ajoutez le code suivant au TaskViewModel
:
t.newTaskDesc = ko.observable (); t.addTask = function () var newtask = nouvelle tâche (description: this.newTaskDesc ()); $ .getJSON ("/ getdate", function (data) newtask.created_at (data.date); newtask.updated_at (data.date); t.tasks.push (newtask); t.saveTask (newtask); t. newTaskDesc ("");); t.saveTask = fonction (tâche) var t = ko.toJS (tâche); $ .ajax (url: "http: // localhost: 9393 / tasks", tapez: "POST", données: t). done (function (data) task.id (data.task.id); )
Knockout fournit une capacité d'itération pratique…
Tout d'abord, nous avons mis newTaskDesc
comme observable. Cela nous permet d’utiliser facilement un champ de saisie pour saisir une description de tâche. Ensuite, nous définissons notre Ajouter une tâche()
fonction, qui ajoute une tâche à la observableArray
; il appelle le saveTask ()
fonction en passant dans le nouvel objet de tâche.
le saveTask ()
fonction est agnostique quant au type de sauvegarde qu’elle effectue. (Plus tard, nous utilisons le saveTask ()
fonction pour supprimer des tâches ou les marquer comme complètes.) Remarque importante: nous nous appuyons sur une fonction pratique pour saisir l’horodatage actuel. Ce ne sera pas le exact horodatage enregistré dans la base de données, mais il fournit certaines données à placer dans la vue.
Le parcours est très simple:
get "/ getdate" do : date => DateTime.now .to_json end
Il convient également de noter que l'id de la tâche n'est pas défini tant que la demande Ajax n'est pas terminée, car nous devons l'affecter en fonction de la réponse du serveur..
Créons le code HTML que notre code JavaScript nouvellement créé contrôle. Une grande partie de ce fichier provient du fichier d'index HTML5 boilerplate. Cela va dans le index.erb
fichier:
Faire Créer une nouvelle tâche
Rechercher des tâches
Tâches incomplètes restantes:
Supprimer toutes les tâches terminées
ID de base de données La description date ajoutée Date modifiée Achevée? Effacer X
Prenons ce modèle et complétons les liaisons que Knockout utilise pour maintenir l'interface utilisateur synchronisée. Pour cette partie, nous couvrons la création de tâches. Dans la deuxième partie, nous aborderons des fonctionnalités plus avancées (y compris la recherche, le tri, la suppression et le marquage comme étant terminé)..
Avant de poursuivre, donnons un peu de style à notre page. Étant donné que ce tutoriel ne concerne pas les CSS, nous allons simplement insérer ceci et continuer tout droit. Le code suivant se trouve dans le fichier CSS Boilerplate HTML5, qui inclut une réinitialisation et quelques autres éléments..
section width: 800px; marge: auto 20px; table largeur: 100%; th curseur: pointeur; tr border-bottom: 1px solide #ddd; tr.complete, tr.complete: nth-child (impair) background: # efffd7; couleur: #ddd; tr: nième enfant (impair) background-color: #dedede; td padding: 10px 20px; td.destroytask background: #ffeaea; couleur: # 943c3c; poids de police: gras; opacité: 0,4; td.destroytask: survoler curseur: pointeur; fond: #ffacac; couleur: # 792727; opacité: 1; .fifty width: 50%; input background: #fefefe; box-shadow: insert 0 0 6px #aaa; rembourrage: 6px; bordure: aucune; largeur: 90%; marge: 4px; entrée: focus contour: aucun; encadré: encadré 0 0 6px rgb (17, 148, 211); -webkit-transition: 0.2s tous; arrière-plan: rgba (17, 148, 211, 0,05); input [type = submit] background-color: # 1194d3; background-image: -webkit-gradient (linéaire, gauche en haut, gauche en bas, de (RGB (17, 148, 211)) à (RGB (59, 95, 142)); image d'arrière-plan: -webkit-linear-gradient (haut, rgb (17, 148, 211), rgb (59, 95, 142)); image d'arrière-plan: -moz-linear-gradient (haut, rgb (17, 148, 211), rgb (59, 95, 142)); image d'arrière-plan: -o-linéaire-gradient (haut, rgb (17, 148, 211), rgb (59, 95, 142)); image d'arrière-plan: -ms-linear-gradient (haut, rgb (17, 148, 211), rgb (59, 95, 142)); image d'arrière-plan: gradient linéaire (en haut, rgb (17, 148, 211), rgb (59, 95, 142)); filter: progid: DXImageTransform.Microsoft.gradient (GradientType = 0, StartColorStr = '# 1194d3', EndColorStr = "# 3b5f8e"); rembourrage: 6px 9px; border-radius: 3px; couleur: #fff; text-shadow: 1px 1px 1px # 0a3d52; bordure: aucune; largeur: 30%; input [type = submit]: survoler background: # 0a3d52; .floatleft float: left; .floatright float: right;
Ajoutez ce code à votre styles.css
fichier.
Couvrons maintenant le formulaire "nouvelle tâche". Nous allons ajouter liaison de données
Attributs au formulaire pour que les liaisons Knockout fonctionnent. le liaison de données
L'attribut indique comment Knockout maintient l'interface utilisateur synchronisée et permet la liaison d'événement et d'autres fonctionnalités importantes. Remplacez le formulaire "nouvelle tâche" par le code suivant.
Créer une nouvelle tâche
Nous allons les parcourir un par un. Tout d’abord, l’élément de formulaire a une liaison pour le soumettre
un événement. Lorsque le formulaire est soumis, le Ajouter une tâche()
fonction définie sur le TaskViewModel
exécute. Le premier élément d’entrée (qui est implicitement de type = "text") contient le valeur
du ko.observable newTaskDesc
que nous avons défini plus tôt. Tout ce qui se trouve dans ce champ lors de la soumission du formulaire devient celui de la tâche. la description
propriété.
Nous avons donc un moyen d’ajouter des tâches, mais nous devons les afficher. Nous devons également ajouter chacune des propriétés de la tâche. Parcourons les tâches et ajoutons-les dans le tableau. Knockout fournit une capacité d'itération pratique pour faciliter cela; définir un bloc de commentaires avec la syntaxe suivante:
X
En Ruby, la valeur finale est retournée implicitement.
Ceci utilise la capacité d'itération de Knockout. Chaque tâche est spécifiquement définie sur le TaskViewModel
(t.tasks
), et il reste synchronisé sur l’interface utilisateur. L'ID de chaque tâche est ajouté uniquement une fois l'appel de base de données terminé (car il n'existe aucun moyen de nous assurer que l'ID correct de la base de données est écrit jusqu'à ce qu'elle soit écrite), mais l'interface n'a pas besoin de refléter des incohérences telles que celles-ci..
Vous devriez maintenant pouvoir utiliser carabine app.rb
(bijou installer fusil de chasse
) depuis votre répertoire de travail et testez votre application dans le navigateur à l'adresse http: // localhost: 9393. (Remarque: assurez-vous d'avoir bijou installer
Examinez toutes vos dépendances / bibliothèques requises avant d’essayer d’exécuter votre application.) Vous devriez pouvoir ajouter des tâches et les voir apparaître immédiatement..
Dans ce didacticiel, vous avez appris à créer une interface JSON avec Sinatra, puis à mettre en miroir ces modèles dans Knockout.js. Vous avez également appris à créer des liaisons pour maintenir notre interface utilisateur synchronisée avec nos données. Dans la suite de ce didacticiel, nous ne parlerons que de Knockout et expliquerons comment créer des fonctionnalités de tri, de recherche et de mise à jour..