📜 ⬆️ ⬇️

Writing a Simple SailsJS Blog: Visual Practice for Beginners (Part 1)

Synopsis


This article is intended for beginners in NodeJS development, and introduces the newbie to developing on this platform using the SailsJS framework. The article will look at the process of developing a simple blog, with accompanying explanatory materials, the purpose of which is to describe the initial skills of working with this framework - which certainly is an excellent basis for any projects on NodeJS. For a better assimilation of the material, it is desirable to have a basic understanding of the Javascript programming language, and its server implementation of NodeJS , as well as at least a primary understanding of the MVC scheme, which is the basis of Sails. For a better understanding of the framework, you can read the documentation on the official website of Sails, as well as see the castes describing the work with Sails in sufficient detail. When writing an article, I tried to write the material as much as possible in a simpler and clearer way; for experienced users, this article will not tell anything new, and some techniques may seem ineffective.

Preparing the working environment


To begin with, we will install the SailsJS framework itself, initially it is assumed that you already have the NodeJS package installed and have access to the Internet. In my case, my OS is Fedora 20, for your part it could be Mac OS X, Ubuntu and other OS, in the example we will use the beta version, to install SailsJS globally enter the command
sudo npm install -g sails@beta 

After that we need to create a new project - in Sails this is done simply and clearly.
 sails new sails-blog --linker cd sails-blog/ 

I will explain - parameter
 new 
headlines that we want to create a new project, then we enter the name of the project, the parameter
 --linker 
makes it so that in our project files for the frontend will be automatically connected: js, css, images and so on, as well as automatically compiled files CoffeeScript and LESS which is also very convenient - but more on that later. After that we go to the directory with the generated project.

We connect Bootstrap - we get acquainted with the organization of the frontend


In Sails the party responsible for placing the frontend is very conveniently organized; the server translates all the files that are located in the / assets folder which is in the project root. The files from the assets folder have approximately the same access, I will explain it on the fingers: suppose you want to place some image.png image, you place it in the assets / images / directory - in this case, when the server is running, this file will be available at Host / images /image.png . This is basic information, and now we ’ll install bootstrap and download the source archive to LESS (I love this style language).

Unpack the less folder from the archive into assets / styles / - this should be the location of the assets / styles / less folder, then rename the less folder to bootstrap (for convenience), then after unpacking the main part of the bootstrap, we need to connect the glyphs, to do this, copy the fonts folder from archive to root assets (approx. / assets / fonts). Now open the file /assets/styles/importer.less in your favorite text editor Sublime Text : this file is by default connected to the main template and is constantly monitored by Grunt - automatically compiled into importer.css
Respectively, then we will distribute bootstrap by adding a line to this file.
 @import 'bootstrap/bootstrap'; 

To connect the glyphs in the same importer.less, you need to declare a variable that will indicate the path to the folder with the glyphs, because our glyphs are located in the fonts folder - we add the following line to the file
 @icon-font-path: '/fonts/'; 

In order to finally install bootstrap, we just need to upload the jquery.js file and boostrap.js to the assets / js / dependencies / folder.
With this we will finish the initial acquaintance with the organization of the frontend and statics in Sails and proceed directly to the development of the Blog itself.
')

We create API Post - first acquaintance with models, controllers


To begin with, we will create an API complex - consisting of a model and a controller, which we will call post for obvious reasons, to create an API complex, enter the following command:

 sails generate api post 

The generated files will be located in the folders with the same name in the api / directory, Sails by default creates a CRUD API ready for exploitation, you can see more in the video describing Sails.
Now we’ll open the Post model we created earlier and start writing code, we need to specify the attribute name, its type, and validator in the model. Now I will give the contents of our model.

api / models / Post.js

 module.exports = { attributes: { title: { type: 'string', maxLength: 120, required: true }, description: { type: 'string', required: true }, content: { type: 'string', required: true } } }; 

The component of our model is built very similar to JSON, which makes it very understandable and convenient, as you could understand inside the atributes construction we list the attributes of the model, in our case we need 3 attributes - a title, a short description and content. All 3 types have a string, the header has 2 validators: maxLength: maximum string length, required: whether this attribute is mandatory when creating a new record (mandatory in our case), then we set the parameters for the remaining 2 attributes, for sails there are dozens of types and validators for all occasions (even the Hex color validator), see the full list here .

So, we have made our first model which will be responsible for our records in a DB - and manipulations with them. Now we can proceed to the main actions with the controller - api / controllers / PostController.js .

Manipulations with controllers also occur in a convenient JSON view, for a start we will list that we want the blog to be able to - and accordingly we will divide the tasks into elements of the controller. Our blog should be able to display a list of posts with a short description in descending order (new posts at the beginning, old ones at the end), be able to split the list of posts into pages (pagination) and the page where we can see the full content of a separate post with comments (disqus). Thus, for myself, I divided these features into 3 attributes of the controller, and 3 main functions of record manipulation, index: displaying the last 10 posts. viewing: displaying the full content of a particular post

pagination - splitting the list, and viewing the list on a specific slice of the list. Let's start writing the code with the recording functionality - add, update, delete. Inside module.exports - we will write code.

Utilities

Creature

 create: function (req, res) { var params = { description : req.param('description'), content : req.param('content'), title : req.param('title'), } Post.create(params).exec(function (err, post) { res.redirect('/post/watch/' + post.id); if (err) return res.send(500); }); } 


In this code, as you understand, we describe the creation of a new record in the database, as I said in the Sails CRUD API is built in by default, this means that you can pass parameters to each subcontroller via url using GET or POST, and Sails will be able to process them.
Post.create - means 1) that we are going to work with the Post model and the create method is responsible for creating a new record to which we need to transfer a list in which we specify the record attribute, and the value of this attribute, in our case, the record should be generated from passing arguments to it in which we use CRUD, in the params list I indicate the parameters passed in the value, if you don’t understand how this is done, I’ll explain it on my fingers so that in our case we create a record - we send a POST request (for example via Postman) with parameters for title, description, content - at url / p ost / create parameters accepted on this url can be called using req.param ('parameter'), which we did. 2) In the exec method, we use an anonymous function that accepts err as arguments - errors that occurred during the creation process, and post - data from the post we just created, which we further process in such a way that if an error occurs, it produces page 500, with successful creation (when we get the post data), we redirect to a page with a full description (we will consider this controller below) by passing the post ID to the url.

The next auxiliary subcontroller will be a subcontroller for updating data, which is very convenient if you need to edit information.

Update

 update: function (req, res) { var Id = req.param('id'); var elem = { description : req.param('description'), content : req.param('content'), title : req.param('title') }; Post.update(Id, elem).exec(function (err) { if (err) return res.send(500); res.redirect('/'); }); } 

In this case, the update method is very similar to the create method - the difference is that with the first argument we specify the id of the record - which, like last time, we get from the passed parameter. The essence of this code as I think you have also caught. The last utility we will delete the record.

Deletion

 delete: function (req, res) { var Id = req.param('id'); Post.destroy(Id).exec(function (err) { if (err) return res.send(500); res.redirect('/post'); }); } 

To delete the record, we only need to specify the id.

The main part + work with views (views)

Now we will consider writing the main “front” part of our application, which parts I described above, according to tradition, we will start from the index page, many may say that as an index I could simply assign 1 page of the pagination slice, but I think it will be better for newbies to chew time. and so the index.

 index: function (req, res) { Post.find() .sort('id DESC') .limit(5) .exec(function (err, posts) { if (err) return res.send(500); res.view({ posts: posts }); }); } 


Now I’ll start explaining the code - the find method is responsible for finding records in the model, we do not call anything as arguments to it - it means that all records match, then we sort the records in descending order - in this case, I use id only as an example If you are going to use such databases as MySQL, Mongo, etc., then you should replace id with createdAt for obvious reasons, and the last item on our list will be the product of the primary section of the list of posts with a limit of 5 entries. After all the procedures with the model are completed, it returns us the list of posts in the right order and number: so that we can continue to use it in our submission. As you remember from previous manipulations, we use an anonymous function in the exec method to perform final data processing. So now let's move on to the key part - the display method, the view method is responsible for this, in which we give a list of what will be available to us when creating the view, in our case this is an object list, for access I create an attribute list item called posts and the value - returned to us by anonymous function posts attribute.

Holistic View

 watch: function (req, res) { var Id = req.param('id'); Post.findOne(Id).exec(function (err, post) { if (!post) return res.send(404); if (err) return res.send(500); res.view({ post: post }); }); } 

Here, as an argument to the findOne method , we pass the identifier argument - which is also a request; in response, it gives us data from a separate post, to which we give access from the view method.

Next, we consider the pagination controller and the blueprints path settings and proceed directly to drawing up the presentation.

 page: function (req, res) { var page = req.param('page'); Post.find() .sort('id DESC') .paginate({ page : page, limit: 5 }) .exec(function (err, posts) { if (err) return res.send(500); res.view({ posts: posts }); }); } 


Here we do almost the same as in the index controller, with the difference that here we added the paginate method, which takes as an argument JSON in which we must specify the limit of records per page, and the page itself we want to display. To make the page slice more dynamic - we create a page - variable with a request that will set the page - for more convenience, we will pass this argument as a get request - without any extra query elements: directly, in the path configuration . To do this, open the file config / routes.js and start editing. Add the following to module.exports.routes :

  'get /post/:page': { controller: 'post', //  action: 'page' //  }, 

What is being done here? In principle, everything is extremely simple: we assign the path — the type of request first, the url, and the passed attribute — : page — which we used in our controller ( req.param ('page') ) and simplified its transmission to the controller (I think this is better - / post / page? page = 2 ). At the same time with pagination, let us define a simplified control scheme for our utility functions:

  'post /post/create': { controller: 'post', action: 'create' }, 'get /post/delete/:id': { controller: 'post', action: 'delete' }, 'post /post/update': { controller: 'post', action: 'update' } 


So, we made the basic manipulations with the Post controller and now for the final entry into force we need only write a presentation - which will be the face of the application. If anyone found it difficult to compile a code, this is the full version of the Post controller with comments.

Representation

Views in Sails are built automatically by the controllers, we created the Post controller - then the folder with the views of this controller will be located at views / post / * , and the views have names of sub-controllers that have a views method, Sails supports many template engines including Jade, Handlebars, HAML and others but by default it has an embedded EJS so our views will be built on it. Create a post folder in views , and add the index.ejs file and page.ejs with the following content:

views / post / index.ejs and views / post / page.ejs

 <div class="container text-center"> <h2 class="text-center">MY BLOG APP</h2> <div class="row"> <div class="col-md-1"></div> <div class="col-md-10"> <% _.each(posts, function (post) { %> <div class="panel panel-default"> <div class="panel-body"> <h3 class="text-center"><%= post.title %></h3><hr> <article> <%= post.description %> </article> </div> <div class="panel-footer"> <a href="/post/watch/<%= post.id %>" class="btn btn-info">LEARN MORE</a> </div> </div> <% }) %> </div> <div class="col-md-1"> </div> <ul class="pagination"> <li><a href="/post">1</a></li> <li><a href="/post/2">2</a></li> <li><a href="/post/3">3</a></li> </ul> </div> 


_.each () as the first parameter is an array of values, the next callback that gives us data from a single element of the array (something like ng-repeat from angular), then we construct data from which the repetition of values ​​should be built, I think those familiar with EJS understand that the values ​​of variables we conclude in <% =%> because it is text, or in <%%> we conclude functions (if it is very easy to explain). So I think the main flow of information about EJS is clear to you at least at an intuitive level - if not, then the documentation will help. And the last view is a single mapping of a specific entry - views / post / watch.ejs

views / post / watch.ejs

 <div class="container"> <div class="panel panel-default"> <div class="panel-body text-center"> <h3><%= post.title %></h3><hr> <article> <%= post.content %> </article> </div> </div> </div> 


This is where the main part of the functionality of our blog is created - it can create, edit and delete posts, it includes pagination, and viewing of individual records, so long as we do not have a form in the admin area that could create posts visually - but first we can test using Postman by first running the test server with the command

 sails lift 


I hope the material from the first part was interesting and useful, the second part will describe the creation of sessions, authorization, and writing a simple admin, if you do not want to wait, or misunderstood how the code should be written, you can see the full project code on github with full comments describing everything that is done there.

2 Part Articles

Useful links and materials used in writing:

Source: https://habr.com/ru/post/224195/


All Articles