Dans la première partie de cette mini-série, nous avons créé la structure de base d'une application de tâche utilisant une interface JSON de Sinatra pour une base de données SQLite, ainsi qu'une interface frontale optimisée par Knockout qui nous permet d'ajouter des tâches à notre base de données. Dans cette dernière partie, nous aborderons certaines fonctionnalités légèrement plus avancées de Knockout, notamment le tri, la recherche, la mise à jour et la suppression..
Commençons là où nous nous sommes arrêtés; voici la partie pertinente de notre index.erb
fichier.
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
Le tri est une tâche courante utilisée dans de nombreuses applications. Dans notre cas, nous souhaitons trier la liste de tâches par n’importe quel champ d’en-tête de notre table de liste de tâches. Nous allons commencer par ajouter le code suivant au TaskViewModel
:
t.sortedBy = []; t.sort = fonction (champ) if (t.sortedBy.length && t.sortedBy [0] == champ && t.sortedBy [1] == 1) t.sortedBy [1] = 0; t.tasks.sort (function (premier, suivant) if (! suivant [champ] .call ()) retour 1; retour (suivant [champ] .call () < first[field].call()) ? 1 : (next[field].call() == first[field].call()) ? 0 : -1; ); else t.sortedBy[0] = field; t.sortedBy[1] = 1; t.tasks.sort(function(first,next) if (!first[field].call()) return 1; return (first[field].call() < next[field].call()) ? 1 : (first[field].call() == next[field].call()) ? 0 : -1; );
Knockout fournit une fonction de tri pour les tableaux observables
Tout d'abord, nous définissons un trié par
tableau en tant que propriété de notre modèle de vue. Cela nous permet de stocker si et comment la collection est triée.
Suivant est le Trier()
une fonction. Il accepte un champ
argument (le champ que nous voulons trier) et vérifie si les tâches sont triées selon le schéma de tri actuel. Nous voulons trier en utilisant un type de processus "à bascule". Par exemple, triez une fois par description et les tâches sont classées par ordre alphabétique. Triez à nouveau par description et les tâches sont classées par ordre alphabétique inverse. Ce Trier()
La fonction prend en charge ce comportement en vérifiant le schéma de tri le plus récent et en le comparant à ce que l'utilisateur souhaite trier..
Knockout fournit une fonction de tri pour les tableaux observables. Il accepte une fonction en tant qu'argument qui contrôle la manière dont le tableau doit être trié. Cette fonction compare deux éléments du tableau et renvoie 1
, 0
, ou -1
à la suite de cette comparaison. Toutes les valeurs similaires sont regroupées (ce qui sera utile pour regrouper des tâches complètes et incomplètes).
Remarque: les propriétés des éléments du tableau doivent être appelées plutôt que simplement accédées; ces propriétés sont en fait des fonctions qui renvoient la valeur de la propriété si elles sont appelées sans aucun argument.
Ensuite, nous définissons les liaisons sur les en-têtes de table..
ID de base de données La description date ajoutée Date modifiée Achevée? Effacer
Ces liaisons permettent à chacun des en-têtes de déclencher un tri basé sur la valeur de chaîne transmise; chacun de ces cartes directement à la Tâche
modèle.
Ensuite, nous voulons pouvoir marquer une tâche comme terminée, et nous y parviendrons simplement en cochant la case associée à une tâche particulière. Commençons par définir une méthode dans le TaskViewModel
:
t.markAsComplete = function (tâche) if (task.complete () == true) task.complete (true); else task.complete (false); task._method = "put"; t.saveTask (tâche); retourne vrai;
le markAsComplete ()
La méthode accepte la tâche sous forme d'argument, qui est automatiquement transmis par Knockout lors de l'itération d'une collection d'éléments. Nous basculons ensuite le Achevée
propriété, et ajouter un ._method = "put"
propriété à la tâche. Ceci permet DataMapper
utiliser le HTTP METTRE
verbe par opposition à POSTER
. Nous utilisons ensuite notre pratique t.saveTask ()
méthode pour enregistrer les modifications dans la base de données. Enfin, nous revenons vrai
car revenant faux
empêche la case à cocher de changer d'état.
Ensuite, nous changeons la vue en remplaçant le code de case à cocher dans la boucle de tâches par ce qui suit:
Cela nous dit deux choses:
Achevée
est vrai.markAsComplete ()
fonction du parent (TaskViewModel
dans ce cas). Cela passe automatiquement la tâche en cours dans la boucle. Pour supprimer une tâche, nous utilisons simplement quelques méthodes pratiques et appelons saveTask ()
. Dans notre TaskViewModel
, ajoutez ce qui suit:
t.destroyTask = function (task) task._method = "delete"; t.tasks.destroy (tâche); t.saveTask (tâche); ;
Cette fonction ajoute une propriété similaire à la méthode "put" pour compléter une tâche. Le intégré détruire()
La méthode supprime la tâche transmise du tableau observable. Enfin, en appelant saveTask ()
détruit la tâche; c’est-à-dire tant que le ._méthode
est réglé sur "supprimer".
Nous devons maintenant modifier notre vision. ajoutez ce qui suit:
X
La fonctionnalité est très similaire à la case à cocher complète. Notez que le class = "destroytask"
est purement à des fins de style.
Ensuite, nous voulons ajouter la fonctionnalité "supprimer toutes les tâches complètes". Tout d’abord, ajoutez le code suivant à la TaskViewModel
:
t.removeAllComplete = function () ko.utils.arrayForEach (t.tasks (), function (tâche) if (task.complete ()) t.destroyTask (tâche););
Cette fonction itère simplement sur les tâches pour déterminer celles qui sont complètes, et nous appelons la destroyTask ()
méthode pour chaque tâche complète. À notre avis, ajouter ce qui suit pour le lien "supprimer tout le texte terminé".
0 "> Supprimer toutes les tâches terminées
Notre liaison par clic fonctionnera correctement, mais nous devons définir tâches complètes ()
. Ajouter ce qui suit à notre TaskViewModel
:
t.completeTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function (tâche) return (task.complete () && task._method! = "delete")); );
Cette méthode est un calculé propriété. Ces propriétés renvoient une valeur calculée "à la volée" lors de la mise à jour du modèle. Dans ce cas, nous renvoyons un tableau filtré contenant uniquement des tâches complètes non marquées pour suppression. Ensuite, nous utilisons simplement ce tableau longueur
propriété pour masquer ou afficher le lien "Supprimer toutes les tâches terminées".
Notre interface doit également afficher le nombre de tâches incomplètes. Semblable à notre tâches complètes ()
fonction ci-dessus, nous définissons un tâches incomplètes ()
fonctionner dans TaskViewModel
:
t.incompleteTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function (tâche) return (! task.complete () && task._method! = "delete")) ;);
Nous avons ensuite accès à ce tableau filtré calculé dans notre vue, comme ceci:
Tâches incomplètes restantes:
Nous voulons attribuer un style aux éléments terminés différemment des tâches de la liste, et nous pouvons le faire à notre avis avec Knockout. css
contraignant. Modifier le tr
balise d'ouverture dans notre tâche arrayForEach ()
boucle à la suivante.
Cela ajoute un
Achevée
Classe CSS à la ligne de la table pour chaque tâche si saAchevée
la propriété estvrai
.
Dates de nettoyage
Débarrassons-nous de ces vilaines chaînes de date Ruby. Nous allons commencer par définir un
format de date
fonctionner dans notreTaskViewModel
:t.MONTHS = ["Jan", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Sept", "Oct", "Oct", "Nov", " Déc"]; t.dateFormat = function (date) if (! date) return "rafraichir pour voir la date du serveur"; var d = new Date (date); return d.getHours () + ":" + d.getMinutes () + "," + d.getDate () + "" + t.MONTHS [d.getMonth ()] + "," + d.getFullYear () ;Cette fonction est assez simple. Si, pour une raison quelconque, la date n’est pas définie, il suffit d’actualiser le navigateur pour extraire la date dans le champ initial.
Tâche
fonction de récupération. Sinon, nous créons une date lisible par l'homme avec le code JavaScript simple.Rendez-vous amoureux
objet à l'aide de laMOIS
tableau. (Remarque: il n'est pas nécessaire de mettre le nom du tableau en majuscule.MOIS
, bien sûr; c’est simplement une façon de savoir qu’il s’agit d’une valeur constante qui ne devrait pas être modifiée.)Ensuite, nous ajoutons les modifications suivantes à notre vue pour le
créé à
etupdated_at
Propriétés:Cela passe le
créé à
etupdated_at
propriétés à laformat de date()
une fonction. Encore une fois, il est important de se rappeler que les propriétés de chaque tâche ne sont pas des propriétés normales. ce sont des fonctions. Afin de récupérer leur valeur, vous devez appeler la fonction (comme indiqué dans l'exemple ci-dessus). Remarque:$ root
est un mot clé, défini par Knockout, qui fait référence au ViewModel. leformat de date()
La méthode, par exemple, est définie comme une méthode de la racine ViewModel (TaskViewModel
).
Recherche de tâches
Nous pouvons effectuer des recherches dans nos tâches de différentes manières, mais nous allons garder les choses simples et effectuer une recherche frontale. Gardez toutefois à l'esprit qu'il est probable que ces résultats de recherche seront basés sur une base de données au fur et à mesure de la croissance des données, dans un souci de pagination. Mais pour le moment, définissons notre
chercher()
méthode surTaskViewModel
:t.query = ko.observable ("); t.search = fonction (tâche) ko.utils.arrayForEach (t.tasks (), fonction (tâche) if (tâche.description () && t.query () ! = "") task.isvisible (task.description (). toLowerCase (). indexOf (t.query (). toLowerCase ())> = 0); sinon si (t.query () == "" ) task.isvisible (true); else task.isvisible (false);) renvoie true;Nous pouvons voir que cela parcourt le tableau des tâches et vérifie si
t.query ()
(valeur observable régulière) se trouve dans la description de la tâche. Notez que cette vérification s’effectue réellement dans le setter fonction pour letâche.isvisible
propriété. Si l'évaluation estfaux
, la tâche n'est pas trouvée et leest visible
la propriété est définie surfaux
. Si la requête est égale à une chaîne vide, toutes les tâches sont définies pour être visibles. Si la tâche n'a pas de description et que la requête est une valeur non vide, la tâche ne fait pas partie du jeu de données renvoyé et est masquée..Dans notre
index.erb
fichier, nous avons configuré notre interface de recherche avec le code suivant:La valeur d'entrée est définie sur le
ko.observable requête
. Ensuite, nous voyons que lekeyup
événement est spécifiquement identifié en tant quevalueUpdate
un événement. Enfin, nous avons défini une liaison d’événement manuelle surkeyup
exécuter la recherche (t.search ()
) une fonction. Aucune soumission de formulaire n'est nécessaire; la liste des éléments correspondants s'affichera et peut toujours être triée, supprimée, etc. Par conséquent, toutes les interactions fonctionnent à tout moment.
Code final
index.erb
Faire Créer une nouvelle tâche
Rechercher des tâches
Tâches incomplètes restantes:
0 "> 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 app.js
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); this.isvisible = ko.observable (true); function TaskViewModel () var t = this; t.tasks = ko.observableArray ([]); t.newTaskDesc = ko.observable (); t.sortedBy = []; t.query = ko.observable ("); t.MONTHS = [" janvier "," février "," mars "," avril "," mai "," juin "," juin "," août "," sept. "," Oct "," Nov "," Dec "]; $ .getJSON (" http: // localhost: 9393 / tasks ", fonction (brute) var taches = $ .map (brute, fonction (élément) retourne une nouvelle tâche (item)); t.tasks (tâches);); t.incompleteTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), function (task) return (! task.complete () && task._method! = "delete"));); t.completeTasks = ko.computed (function () return ko.utils.arrayFilter (t.tasks (), fonction ( task) return (task.complete () && task._method! = "delete")); // Opérations t.dateFormat = function (date) if (! date) return "actualiser pour voir le serveur date "; var d = new Date (date); renvoyer d.getHours () +": "+ d.getMinutes () +", "+ d.getDate () +" "+ t.MONTHS [d.getMonth ()] + "," + d.getFullYear (); t.addTask = function () var newtask = nouvelle tâche (description: this.newTaskDesc ()); $ .getJSON ("/ getdate", fonction (data) newtask.created_at (data.date); newtask.up du_daté (data.date); t.tasks.push (newtask); t.saveTask (newtask); t.newTaskDesc (""); ); t.search = fonction (tâche) ko.utils.arrayForEach (t.tasks (), fonction (tâche) if (tâche.description () && t.query ()! = "") tâche.isvisible (tâche .description (). toLowerCase (). indexOf (t.query (). toLowerCase ())> = 0); autrement if (t.query () == "") task.isvisible (true); autre task.isvisible (false);) renvoie true; t.sort = fonction (champ) if (t.sortedBy.length && t.sortedBy [0] == champ && t.sortedBy [1] == 1) t.sortedBy [1] = 0; t.tasks.sort (function (premier, suivant) if (! suivant [champ] .call ()) retour 1; retour (suivant [champ] .call () < first[field].call()) ? 1 : (next[field].call() == first[field].call()) ? 0 : -1; ); else t.sortedBy[0] = field; t.sortedBy[1] = 1; t.tasks.sort(function(first,next) if (!first[field].call()) return 1; return (first[field].call() < next[field].call()) ? 1 : (first[field].call() == next[field].call()) ? 0 : -1; ); t.markAsComplete = function(task) if (task.complete() == true) task.complete(true); else task.complete(false); task._method = "put"; t.saveTask(task); return true; t.destroyTask = function(task) task._method = "delete"; t.tasks.destroy(task); t.saveTask(task); ; t.removeAllComplete = function() ko.utils.arrayForEach(t.tasks(), function(task) if (task.complete()) t.destroyTask(task); ); t.saveTask = function(task) var t = ko.toJS(task); $.ajax( url: "http://localhost:9393/tasks", type: "POST", data: t ).done(function(data) task.id(data.task.id); ); ko.applyBindings(new TaskViewModel());Notez le réarrangement des déclarations de propriété sur le
TaskViewModel
.
Conclusion
Vous avez maintenant les techniques pour créer des applications plus complexes!
Ces deux tutoriels vous ont guidé tout au long du processus de création d’une application d’une page avec Knockout.js et Sinatra. L'application peut écrire et récupérer des données, via une simple interface JSON, et possède des fonctionnalités qui vont au-delà des simples actions CRUD, telles que la suppression en masse, le tri et la recherche. Avec ces outils et exemples, vous avez maintenant les techniques pour créer des applications beaucoup plus complexes!