Een Todo List-app maken met Node.js en Geddy

In deze driedelige tutorial duiken we diep in het maken van een to-do list management-app in Node.js en Geddy. Dit is het tweede deel in de serie, waarin we een eenvoudig te doen lijstbeheer-app zullen maken.


Samenvatting

Als een snelle opfriscursus hebben we Node en Geddy voor het laatst geïnstalleerd, een nieuwe app gemaakt en geleerd hoe de server moet worden opgestart. In deze zelfstudie bouwen we verder op wat we de vorige keer hebben gedaan. Zorg er dus voor dat je die hebt voltooid voordat je verdergaat.


Het genereren van de Todo-resource

Geddy heeft een ingebouwde brongenerator; dit zal ons in staat stellen om automatisch een model, controller, views en routes voor een specifieke resource te genereren. Onze takenlijst-app heeft maar één bron: Te doen. Om het te genereren, gewoon CD in de directory van uw app (cd-pad / naar / uw / todo_app) en loop:

geddy resource todo

U moet deze bestanden nu aan uw app toevoegen:

  • app / modellen / todo.js
  • app / controllers / todos.js
  • app / views / todos /
    • index.html.ejs
    • show.html.ejs
    • edit.html.ejs
    • add.html.ejs

Jouw config / router.js moet dit ook als bijlage hebben:

router.resource ( 'todos');

Wat het allemaal doet

Als je nieuw bent bij MVC lijkt dit allemaal een beetje intimiderend voor je. Maar maak je geen zorgen, het is heel eenvoudig als je er eenmaal achter bent.

modellen / todo.js: Dit bestand is waar we onze zullen definiëren Te doen model. We zullen een aantal eigenschappen definiëren die dat allemaal zijn Te doenhebben. We zullen hier ook enkele gegevensvalidaties schrijven.

controllers / todos.js: Dit bestand is waar alle / Todos / routes eindigen. Elke actie in deze controller heeft een bijbehorende route:

GET / todos / => index POST / todos / => create GET / todos /: id => show PUT / todos /: id => update DELETE / todos /: id => remove GET / todos /: id / add = > voeg GET / todos /: id / edit => edit toe

views / todos /: Elk bestand hier komt overeen met een van de KRIJGEN routes die we je hierboven hebben laten zien. Dit zijn de sjablonen die we gebruiken om de voorkant van de app te genereren. Geddy maakt gebruik van EJS (ingebed JavaScript) omdat het sjableltaal is. Het moet er bekend uitzien als je ooit PHP of ERB hebt gebruikt. Kortom, u kunt elk JavaScript dat u wilt gebruiken in uw sjablonen.

Voeling krijgen voor de routes

Nu we een aantal codes hebben gegenereerd, laten we controleren of we alle routes hebben die we nodig hebben. Start de app opnieuw (Geddy) en wijs uw browser naar http: // localhost: 4000 / todos. Je zou zoiets moeten zien

Ga je gang en probeer dat voor de ander KRIJGEN routes ook:

  • http: // localhost: 4000 / todos / iets
  • http: // localhost: 4000 / todos / add
  • http: // localhost: 4000 / todos / iets / bewerken

Alles goed? Oké, laten we doorgaan.


Het Todo-model maken

In Geddy (en de meeste andere MVC-frameworks), gebruik je modellen om het soort gegevens te definiëren waarmee je app zal werken. We hebben zojuist een model voor onze gegenereerd Te doens, dus laten we eens kijken wat dat ons gaf:

var Todo = function () // Some commented out code; // Een beetje meer commentaarcode Todo = geddy.model.register ('Todo', Todo);

Modellen zijn vrij eenvoudig in Geddy. We maken gewoon een nieuwe constructorfunctie voor onze Te doens en registreer het als een model in geddy. Laten we enkele eigenschappen voor onze definiëren Te doens. Verwijder alle uitgelichte code en voeg deze toe aan de contructorfunctie:

var Todo = function () this.defineProperties (title: type: 'string', required: true, id: type: 'string', required: true, status: type: 'string', vereist : true); ;

Onze Te doens heeft een titel, een id en een status en alle drie zijn vereist. Laten we nu een aantal validaties instellen voor onze Te doens.

var Todo = function () this.defineProperties (title: type: 'string', required: true, id: type: 'string', required: true, status: type: 'string', vereist : true); this.validatesPresent (titel); this.validatesLength ('title', min: 5); this.validatesWithFunction ('status', functie (status) retourstatus == 'open' || status == 'done';); ;

We valideren dat de titel aanwezig is, dat de titel een minimale lengte van 5 tekens heeft en we gebruiken een functie om te valideren dat de status ofwel Open of gedaan. Er zijn nogal wat valitation-functies ingebouwd, ga je gang en bekijk het project op http://github.com/mde/geddy voor meer informatie over hen.


De Todo-modeladapter maken

Nu we ons todo-model hebben opgezet, kunnen we ergens een plek maken om onze modellen op te slaan. Ten behoeve van deze zelfstudie bewaren we de gegevens gewoon in het geheugen. We hangen een todos-array af van onze global Geddy object om de gegevens in te bewaren. In het volgende deel van deze serie zullen we beginnen deze vast te houden in een database.

Uw init.js-bestand bewerken

Open je config / init.js het dossier. Alles wat er nu zou moeten zijn, is een globale niet-afgevangen uitzonderingsbehandelaar:

// Voeg niet-afgevangen-uitzondering-handler toe in prod-achtige omgevingen als (geddy.config.environment! = 'Development') process.addListener ('uncaughtException', function (err) geddy.log.error (JSON.stringify (err) ));); 

Direct na dat codeblok laten we onze array van de Geddy globaal:

geddy.todos = [];

Daar hebben we nu een plek om onze te bewaren Te doens. Houd er rekening mee dat dit zich in het geheugen van uw toepassing bevindt. Het verdwijnt dus als u de server opnieuw opstart.

De Model-adapter maken

Een model-adapter biedt de basis opslaan, verwijderen, laden, en allemaal methoden die een model nodig heeft. Onze gegevensbron is vrij eenvoudig (slechts een array!), Dus het schrijven van onze modeladapter zou ook vrij eenvoudig moeten zijn.

Maak een map aan in lib riep model_adapters en maak een bestand aan lib / model_adapters riep todo.js. Laten we dat bestand openen en wat boilerplate code toevoegen:

var Todo = nieuw (function () ) (); exporteert. Tood = Taken;

Het enige dat we hier doen, is het instellen van een nieuw leeg object dat geëxporteerd moet worden naar wat uiteindelijk dit bestand vereist. Als u meer wilt weten over hoe de methode van Node werkt, heeft dit artikel een goed overzicht. In dit geval onze init.js bestand zal het vereiste doen.

Vereist de modeladapter in init.js

Daarom hebben we een nieuw Todo-model-adapterobject opgezet. Het is op dit moment behoorlijk kaal, maar daar komen we snel bij. Voor nu moeten we teruggaan naar init.js en een code toevoegen, zodat deze bij het opstarten in onze app wordt geladen. Na de geddy.todos = []; in config / init.js voeg deze twee regels toe:

geddy.model.adapter = ; geddy.model.adapter.Todo = require (process.cwd () + '/lib/model_adapters/todo').Todo;

We hebben een leeg model-adapterobject gemaakt en de Todo-modeladapter eraan toegevoegd.


Todos opslaan

Nu we onze model- en modeladapter op hun plaats hebben, kunnen we beginnen aan de app-logica. Laten we beginnen met het toevoegen van taken aan onze takenlijst.

Bewerk de opslagmethode op de adapter om een ​​nieuwe instantie op te slaan

Wanneer u met gegevens werkt, moet u eerst de modeladapter gebruiken. We moeten een exemplaar van ons Todo-model kunnen opslaan in onze geddy.todos-array. Dus open lib / model_adapters / todo.js en voeg een opslagmethode toe:

var Todo = new (function () this.save = function (todo, opts, callback) if (typeof callback! = 'function') callback = function () ; todo.saved = true; geddy. todos.push (todo); terugbellen (null, todo);) ();

Het enige wat we moeten doen is de eigenschap van het exemplaar instellen op true en het item in de array geddy.todos duwen. In Node is het het beste om alle I / O op een niet-blokkerende manier te doen, dus het is een goed idee om de gewoonte aan te nemen om callbacks te gebruiken om gegevens door te geven. Voor deze zelfstudie maakt het niet zoveel uit, maar als we later dingen blijven doen, zal het van pas komen. U zult merken dat we ervoor hebben gezorgd dat de callback een functie is. Als we dat niet doen en opslaan gebruiken zonder terug te bellen, krijgen we een foutmelding. Laten we nu doorgaan naar de actie voor het maken van de controller.

Bewerk de create-actie om een ​​todo-instantie op te slaan

Ga je gang en kijk eens naar de creëren actie in app / controllers / todos.js:

this.create = function (req, resp, params) // Sla de resource op en geef indexpagina this.redirect weer (controller: this.name); ;

Best simpel, toch? Geddy heeft het voor je uitgepraat. Dus laten we het een beetje aanpassen:

this.create = function (req, resp, params) var self = this, todo = geddy.model.Todo.create (title: params.title, id: geddy.string.uuid (10), status: 'open '); todo.save (functie (err, data) if (err) params.errors = err; self.transfer ('add'); else self.redirect (controller: self.name);) ; ;

Eerst maken we een nieuw exemplaar van het Todo-model met geddy.model.Todo.create, het doorgeven van de titel die ons formulier naar ons zal posten, en het instellen van de standaardinstellingen voor het ID en de status.

Vervolgens noemen we de opslagmethode die we hebben gemaakt op de modeladapter en leiden we de gebruiker terug naar de / todos-route. Als het de validatie niet heeft doorstaan ​​of als er een fout optreedt, gebruiken we de controller overdracht methode om het verzoek terug te sturen naar de toevoegen actie.

Bewerk add.html.ejs

Nu is het tijd voor ons om de sjabloon toe te voegen. Kijk eens naar app / views / todos / add.html.ejs, het zou er zo uit moeten zien:

params

    <% for (var p in params) %>
  • <%= p + ': ' + params[p]; %>
  • <% %>

Dat hebben we niet nodig

    voor onze use case, dus laten we er vanaf nu van af. Maak jouw add.html.ejs er uitzien als dit:

    <%= partial('_form', params: params); %>

    Een intro voor partials

    Partials bieden u een eenvoudige manier om code te delen tussen uw sjablonen.

    U zult merken dat we een gedeeltelijke in deze sjabloon gebruiken. Partials bieden u een eenvoudige manier om code te delen tussen uw sjablonen. Onze sjablonen voor toevoegen en bewerken gaan allebei hetzelfde formulier gebruiken, dus laten we dit formulier nu gedeeltelijk maken. Maak een nieuw bestand in de views / todos / map genoemd _form.html.ejs. We gebruiken een onderstrepingsteken om eenvoudig te bepalen of deze sjabloon gedeeltelijk is. Open het en voeg deze code toe:

    <% var isUpdate = params.action == 'edit' , formTitle = isUpdate ? 'Update this To Do Item' : 'Create a new To Do Item' , action = isUpdate ? '/todos/' + todo.id + '?_method=PUT' : '/todos' , deleteAction = isUpdate ? '/todos/' + todo.id + '?_method=DELETE' :", btnText = isUpdate ? 'Update' : 'Add' , doneStatus = isUpdate ? 'checked' :", titleValue = isUpdate ? todo.title :", errors = params.errors; %> 
    <%= formTitle %>
    <% if (errors) %>

    <% for (var p in errors) %>

    <%= errors[p]; %>
    <% %>

    <% %>
    <% if (isUpdate) %>
    <% %>
    <% if (isUpdate) %> <% %>

    Whoa, dat is veel code daar! Laten we kijken of we er doorheen kunnen lopen. Aangezien er twee verschillende sjablonen zijn die dit deel gaan gebruiken, moeten we ervoor zorgen dat het formulier er in beide goed uitziet. Het grootste deel van deze code is eigenlijk boilerplate van Twitter's Bootstrap. Dit is wat ervoor zorgt dat deze app er meteen goed uitziet (en ook op mobiele apparaten!).

    Om deze app er nog beter uit te laten zien, kunt u het CSS-bestand gebruiken dat is meegeleverd in de download van de demo-app.

    Het eerste dat we deden, was een aantal variabelen instellen die we konden gebruiken. In de toevoegen actie die we doorstaan ​​a params object naar beneden naar de sjabloon in de reageren method call. Dit geeft ons een paar dingen - het vertelt ons naar welke controller en actie dit verzoek is gerouteerd en geeft ons alle queryparameters die in de URL zijn doorgegeven. We hebben de isUpdate variabele om te zien of we momenteel bezig zijn met de updateactie en vervolgens stellen we nog enkele variabelen in om onze weergavecode op te schonen.

    Vanaf daar was alles wat we deden een formulier maken. Als we de actie toevoegen, geven we het formulier gewoon weer zoals het is. Als we de bewerkingsactie uitvoeren, vullen we het formulier in om de gebruiker de velden te laten bijwerken.

    Merk op dat het formulier een verzendt POST verzoek aan de / Todos / met een _method = PUT parameter. Geddy gebruikt de standaardmethode negeerparameter om u te laten verzenden LEGGEN en DELETE vraagt ​​vanuit de browser op zonder JavaScript te gebruiken. (aan de voorkant tenminste!)

    Het laatste kleine detail dat we moeten bekijken is die knop "Verwijderen". We gebruiken html5's formaction attribuut om de actie voor dit formulier te wijzigen. Je zult merken dat deze knop is formaction verzendt een POST verzoek tot de / Todos /: id route met een _method = WISSEN parameter. Dit raakt de verwijderen actie op de controller, die we later zullen zien.

    Start uw server opnieuw op (Geddy) en bezoek http: // localhost: 4000 / todos / add om uw sjabloon in actie te zien. Maak een To Do-item terwijl je bezig bent.


    Een lijst van alle Todos

    Nu we To Do-items van gebruikersinvoer hebben toegevoegd aan onze geddy.todos-array, moeten we ze waarschijnlijk ergens vermelden. Laten we beginnen op de allemaal methode in de model-adapter.

    Bewerk de methode all op de adapter om alle taken weer te geven

    Laten we openen lib / model_adapters / todo.js nogmaals en voeg een toe alle methoden recht boven deopslaan 'methode:

    this.all = function (callback) callback (nul, geddy.todos); 

    Dit is waarschijnlijk de eenvoudigste model-adapter methode die we vandaag zullen creëren, het enige dat het doet is een callback accepteren en het een fout noemen (wat altijd null is, we zullen deze methode in de volgende tutorial upgraden), en geddy.todos.

    Bewerk de indexactie om alle todos weer te geven

    Doe open /app/controllers/todos.js nogmaals en kijk eens naar de inhoudsopgave actie. Het zou er ongeveer zo uit moeten zien:

    this.index = function (req, resp, params) this.respond (params: params); ;

    Dit deel is heel eenvoudig, we gebruiken alleen het allemaal methode die we zojuist hebben gedefinieerd op de model-adapter om alle te krijgen Te doens en render ze:

    this.index = function (req, resp, params) var self = this; geddy.model.adapter.Todo.all (function (err, todos) self.respond (params: params, todos: todos);); ;

    Dat is het voor de controller, nu op het uitzicht.

    Bewerk index.html.ejs

    Neem een ​​kijkje op /app/views/todos/index.html.ejs, het zou er als volgt uit moeten zien:

    params

      <% for (var p in params) %>
    • <%= p + ': ' + params[p]; %>
    • <% %>

    Lijkt veel op de template add.html.ejs niet. Nogmaals, we hebben de params boilerplate hier niet nodig, dus haal dat eruit en laat je index.html.ejs-sjabloon er als volgt uitzien:

    Te doen lijst

    Maak een nieuwe taak

    <% if (todos && todos.length) %> <% for (var i in todos) %>

    / Bewerken "><%= todos[i].title; %>

    <%= todos[i].status; %>

    <% %> <% %>

    Deze is ook vrij eenvoudig, maar deze keer hebben we een lus in onze sjabloon. In de header daar hebben we een knop toegevoegd om nieuwe taken toe te voegen. In de loop genereren we een rij voor elk Te doen, weergeven van de titel (als een link naar die is) Bewerk pagina) en de status ervan.

    Ga naar http: // localhost: 4000 / todos voor meer informatie.


    Een Todo bewerken

    Nu hebben we een link naar de Bewerk pagina, we moeten het waarschijnlijk laten werken!

    Maak een laadmethode in de modeladapter

    Open uw modeladapter opnieuw (/lib/model_adapters/todo.js). We gaan toevoegen in a laden methode zodat we een specifieke kunnen laden Te doen en gebruik het op onze bewerkpagina. Het maakt niet uit waar je het toevoegt, maar laat het nu tussen de allemaal methode en de opslaan methode:

    this.load = function (id, callback) for (var i in geddy.todos) if (geddy.todos [i]. id == id) return callback (null, geddy.todos [i]);  callback (message: "To Do not found", null); ;

    Deze laadmethode neemt een id en een callback. Het loopt door de items in geddy.todos en controleert om te zien of het huidige item is ID kaart komt overeen met de doorgegeven ID kaart. Als dit het geval is, wordt de callback aangeroepen en wordt de Te doen item terug. Als het geen overeenkomst vindt, roept het de callback met een fout op. Nu moeten we deze methode gebruiken in de showactie van de todos-controller.

    Bewerk de bewerkingsactie om een ​​taak te vinden

    Open je todos controller opnieuw en kijk eens naar het is Bewerk actie. Het zou er ongeveer zo uit moeten zien:

    this.edit = function (req, resp, params) this.respond (params: params); ;

    Laten we de laadmethode gebruiken die we zojuist hebben gemaakt:

    this.edit = function (req, resp, params) var self = this; geddy.model.Todo.load (params.id, function (err, todo) self.respond (params: params, todo: todo);); ;

    Het enige wat we hier doen is het laden van de taak en het verzenden naar de sjabloon die moet worden weergegeven. Laten we de sjabloon eens bekijken.

    Bewerk edit.html.ejs

    Doe open /app/views/todos/edit.html.ejs. Nogmaals, we zullen de params boilerplate niet nodig hebben, dus laten we het verwijderen. Maak jouw edit.html.ejs er uitzien als dit:

    <%= partial('_form', params: params, todo: todo); %>

    Dit zou er ongeveer hetzelfde uit moeten zien te zien als de add.html.ejs bestand dat we zojuist hebben bewerkt. U zult merken dat we een Te doen bezwaar tot de gedeeltelijke evenals de params deze keer. Het leuke is, omdat we de gedeeltelijke tekst al hebben geschreven, dit is alles wat we moeten doen om ervoor te zorgen dat de bewerkpagina correct wordt weergegeven.

    Start de server opnieuw op, maak een nieuwe aan Te doen en klik op de link om te zien hoe dit werkt. Laten we nu die updateknop werken!

    Bewerk de opslagmethode in de modeladapter

    Open opnieuw de model-adapter en vind de opslaan methode. we gaan er een beetje aan toevoegen, zodat we kunnen besparen over het bestaande Te doens. Laat het er zo uitzien:

    this.save = functie (todo, opts, callback) if (typeof callback! = 'function') callback = function () ;  var todoErrors = null; for (var i in geddy.todos) // als het er al is, sla het op als (geddy.todos [i] .id == todo.id) geddy.todos [i] = todo; todoErrors = geddy.model.To.maken (todo) .errors; return callback (todoErrors, todo);  todo.saved = true; geddy.todos.push (todo); return callback (null, todo); 

    Dit lus over alle todo's geddy.todos en als het ID kaart is er al, het vervangt dat Te doen met het nieuwe Te doen aanleg. We doen hier wat dingen om ervoor te zorgen dat onze validaties zowel werken aan update als aan het maken - om dit te doen moeten we de fouten eigenschap van een nieuwe modelinstantie en geef die terug in de callback. Als het validaties heeft doorstaan, is het gewoon ongedefinieerd en negeert onze code het. Als het niet is geslaagd, todoErrors zal een reeks validatiefouten zijn.

    Nu dat op zijn plaats is, laten we aan onze controller werken bijwerken actie.


    Bewerk de updateactie om een ​​taak te vinden, de status te wijzigen en op te slaan

    Ga je gang en open de controller opnieuw en vind de 'update'-actie, het zou er ongeveer zo uit moeten zien:

    this.update = function (req, resp, params) // Sla de resource op en toon dan de itempagina this.redirect (controller: this.name, id: params.id); ;

    Je zult het willen bewerken om het er zo uit te laten zien:

    this.update = function (req, resp, params) var self = this; geddy.model.adapter.Todo.load (params.id, function (err, todo) todo.status = params.status; todo.title = params.title; todo.save (function (err, data) if ( err) params.errors = err; self.transfer ('edit'); else self.redirect (controller: self.name););); ;

    Wat we hier doen, is het laden van het gevraagde Te doen, een aantal van zijn eigenschappen bewerken en de Te doen nog een keer. De code die we zojuist in de model-adapter hebben geschreven zou de rest moeten behandelen. Als we een foutmelding krijgen, betekent dit dat de nieuwe eigenschappen niet zijn gevalideerd, dus we zullen het verzoek terugsturen naar de Bewerk actie. Als we geen foutmelding hebben ontvangen, leiden we het verzoek gewoon door naar het inhoudsopgave actie.

    Ga je gang en probeer het uit. Start de server opnieuw op, maak een nieuwe aan Te doen, klik op de bewerkingslink, verander de status in gedaan, en zie dat het wordt bijgewerkt in de inhoudsopgave. Als u wilt controleren of uw validaties werken, probeert u de titel tot iets korter dan 5 tekens.

    Laten we nu die knop "Verwijderen" gebruiken.


    Een Todo verwijderen

    Inmiddels hebben we een werkende takenlijst-applicatie, maar als je het een tijdje gebruikt, zal het moeilijk worden om de Te doen item dat u zoekt op die indexpagina. Laten we ervoor zorgen dat de knop "Verwijderen" werkt, zodat we onze lijst mooi en kort kunnen houden.

    Maak een verwijderingsmethode in de model-adapter

    Laten we onze model-adapter opnieuw openen, deze keer willen we een toevoegen verwijderen methode daar. Voeg dit toe direct na de opslaan methode:

    this.remove = function (id, callback) if (typeof callback! = 'function') callback = function () ;  voor (var i in geddy.todos) if (geddy.todos [i] .id == id) geddy.todos.splice (i, 1); return callback (nul);  return callback (message: "To Do not found"); 

    Deze is vrij eenvoudig, het zou veel op de laadmethode moeten lijken. Het loopt door alle Te doenzonde geddy.todos om de te vinden ID kaart waar we naar op zoek zijn. Vervolgens wordt dat item uit de array gesplitst en wordt de callback aangeroepen. Als het de array niet vindt, roept het de callback met een fout aan.

    Laten we dit nu in onze controller gebruiken.

    Bewerk de verwijderactie

    Open uw controller opnieuw en voer de verwijderen actie. Het zou er ongeveer zo uit moeten zien:

    this.remove = function (req, resp, params) this.respond (params: params); ;

    Bewerk het om het er zo uit te zien:

    this.remove = function (req, resp, params) var self = this; geddy.model.adapter.Todo.remove (params.id, function (err) if (err) params.errors = err; self.transfer ('edit'); else self.redirect (controller: self .naam);  ); 

    We passeren de ID kaart die we kregen van de params in de vorm post in de verwijderen methode die we zojuist hebben gemaakt. Als we een foutmelding krijgen, gaan we terug naar de Bewerk actie (we nemen aan dat het formulier de verkeerde info heeft geplaatst). Als we geen foutmelding hebben ontvangen, stuur het verzoek dan gewoon naar de inhoudsopgave actie.

    Dat is het! Werden gedaan.

    U kunt de verwijderfunctie testen door uw server opnieuw te starten en een nieuwe te maken Te doen item, klik op de link en klik vervolgens op de knop "Verwijderen". Als je het goed hebt gedaan, zou je terug moeten zijn op de indexpagina met dat item verwijderd.


    De volgende stappen

    In de volgende tutorial gebruiken we de ontzagwekkende mongodb-wrapper-module van http: //i.tv om ons te behouden Te doenzit in MongoDB. Met Geddy zal dit gemakkelijk zijn; alles wat we moeten veranderen is de model-adapter.

    Als u vragen heeft, kunt u hier een opmerking achterlaten of een probleem met github openen.