Em termos gerais, Sails.js é um gerador de códigos JavaScript que entrega, em poucos minutos, um protótipo executável de uma Aplicação WEB do tipo backend robusta, segura e escalável. O resultado final é a capacidade de entregar páginas HTML5 + CSS + JavaScript dinâmicas ao Cliente Web.
Se esta é sua primeira vez aqui, recomendamos a leitura dos conteúdos abaixo antes de continuar:
npm i sails -gsails new primeiro-prototipo
Choose a template for your new Sails app:
1. Web App · Extensible project with auth, login, & password recovery
2. Empty · An empty Sails app, yours to configure
(type "?" for help, or <CTRL+C> to cancel)
? 1
cd primeiro-prototiposails lift você verá:
info: Starting app...
info: Initializing project hook... (`api/hooks/custom/`)
info: Initializing `apianalytics` hook... (requests to monitored routes will be logged!)
info: ·• Auto-migrating... (alter)
info: Hold tight, this could take a moment.
info: ✓ Auto-migration complete.
debug: Running v0 bootstrap script... (looks like this is the first time the bootstrap has run on this computer)
info:
info: .-..-.
info:
info: Sails <| .-..-.
info: v1.5.8 |\
info: /|.\
info: / || \
info: ,' |' \
info: .-'.-==|/_--'
info: `--'-------'
info: __---___--___---___--___---___--___
info: ____---___--___---___--___---___--___-__
info:
info: Server lifted in `C:\Users\rogeriorfs\git\passo-a-passo`
info: To shut down Sails, press <CTRL> + C at any time.
info: Read more at https://sailsjs.com/support.
debug: -------------------------------------------------------
debug: :: Mon Jul 17 2023 08:15:43 GMT-0300 (Horário Padrão de Brasília)
debug: Environment : development
debug: Port : 1337
debug: Local : http://localhost:1337
debug: -------------------------------------------------------
>http://localhost:1337.
O navegador deve apresentar algo do tipo:
sails generate page
lista-tarefas
Successfully generated:
•- views\pages\lista-tarefas.ejs
•- api\controllers\view-lista-tarefas.js
•- assets\styles\pages\lista-tarefas.less
•- assets\js\pages\lista-tarefas.page.js
A few reminders:
(1) These files were generated assuming your Sails app is using
Vue.js as its front-end framework. (If you're unsure,
head over to https://sailsjs.com/support)
(2) You'll need to manually add a route for this new page's
action in your `config/routes.js` file; e.g.
'GET /lista-tarefas': { action: 'view-lista-tarefas' },
(3) You'll need to manually import the new LESS stylesheet
from your `assets/styles/importer.less` file; e.g.
@import 'pages/lista-tarefas.less';
(4) Last but not least, since some of the above are backend changes,
don't forget to re-lift the server before testing!
config/routes.js a linha 'GET
/lista-tarefas': { action: 'view-lista-tarefas' },views/pages/dashboard/welcome.ejs e adicione
<a class="btn btn-info" href="/lista-tarefas">Minha Lista de Tarefas</a>
admin@example.com senha abc123
<- GET /login (6ms 200)
| view: pages/entrance/login
°
<- PUT /api/v1/entrance/login (226ms 200)
| The requesting user agent has been successfully logged in.
|
| Under the covers, this stores the id of the logged-in user in the session as the `userId` key. The next time this user agent sends a request, assuming it includes a cookie (like a web browser), Sails will automatically make this user id available as req.session.userId in the corresponding action. (Also note that, thanks to the included "custom" hook, when a relevant request is received from a logged-in user, that user's entire record from the database will be fetched and exposed as `req.me`.)
|
°
<- GET / (3ms 302)
| redirect
| Requesting user is logged in, so redirect to the internal welcome page.
°
<- GET /welcome (6ms 200)
| Display the welcome page for authenticated users.
| view: pages/dashboard/welcome
°
<- POST /api/v1/observe-my-session (2ms 200)
| The requesting socket is now subscribed to socket broadcasts about the logged-in user's session.
°
<- GET /lista-tarefas (6ms 200)
| view: pages/lista-tarefas
°
sails generate model tarefa
info: Created a new model ("Tarefa")!
api/models e você deverá encontrar um arquivo
api/models/Tarefa.js
com o seguinte conteúdo:
/**
* Tarefa.js
*
* @description :: A model definition represents a database table/collection.
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
*/
module.exports = {
attributes: {
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
// ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
// ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗
// ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
},
};
api/models/Tarefa.js e adicione os campos: prioridade, descricao e prazo, do
seguinte modo:
/**
* Tarefa.js
*
* @description :: A model definition represents a database table/collection.
* @docs :: https://sailsjs.com/docs/concepts/models-and-orm/models
*/
module.exports = {
attributes: {
// ╔═╗╦═╗╦╔╦╗╦╔╦╗╦╦ ╦╔═╗╔═╗
// ╠═╝╠╦╝║║║║║ ║ ║╚╗╔╝║╣ ╚═╗
// ╩ ╩╚═╩╩ ╩╩ ╩ ╩ ╚╝ ╚═╝╚═╝
prioridade: {type: 'number', required: true, description: 'Registra a prioridade de atendimento'},
descricao: {type: 'string', required: true, description: 'Registra o que deve ser feito'},
prazo: {type: 'string', columnType: 'date', required: false, description: 'Data limite, se existir'},
// ╔═╗╔╦╗╔╗ ╔═╗╔╦╗╔═╗
// ║╣ ║║║╠╩╗║╣ ║║╚═╗
// ╚═╝╩ ╩╚═╝╚═╝═╩╝╚═╝
// ╔═╗╔═╗╔═╗╔═╗╔═╗╦╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ╠═╣╚═╗╚═╗║ ║║ ║╠═╣ ║ ║║ ║║║║╚═╗
// ╩ ╩╚═╝╚═╝╚═╝╚═╝╩╩ ╩ ╩ ╩╚═╝╝╚╝╚═╝
},
};
sails lift e, se nenhuma mensagem de erro aparecer, passe para a etapa
seguinte.config/bootstrap.js e localize a linha // By convention, this is a good
place to set up fake data during development. (provavelmente a linha 61)
await
Tarefa.create({prioridade:1,descricao:'Teste de tarefa', prazo: new Date()}).tmp/localDiskDb/tarefa.db e
.tmp/bootstrap-version.json
sails lift.tmp/localDiskDb/tarefa.db e verifique se seus dados criados acima já
aparecem na coleção tarefa.db. Espera-se que o arquivo agora contenha algo do tipo:
{"$$indexCreated":{"fieldName":"id","unique":true,"sparse":false}}
{"prioridade":1,"descricao":"Teste de tarefa","prazo":"2023-07-12T14:27:20.992Z","createdAt":1689172040992,"updatedAt":1689172040992,"id":1,"_id":1}
api/controllers/view-lista-tarefas.js - criado na
segunda etapa - para que tenha o
seguinte código:
module.exports = {
friendlyName: 'View lista tarefas',
description: 'Display "Lista tarefas" page.',
exits: {
success: {
viewTemplatePath: 'pages/lista-tarefas'
}
},
fn: async function () {
let tarefas = await Tarefa.find({});
return {tarefas};
}
};
assets/js/pages/lista-tarefas.page.js para que ele
obtenha da sessão HTTP os objetos disponibilizados pelo Controller, conforme abaixo:
parasails.registerPage('lista-tarefas', {
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function() {
/* captura as variaveis / objetos da sessao */
/* e atribui a este objeto 'this'. */
/* Deste modo, o Vue2 pode acessar a lista */
/* de {tarefas} obtida no BD pelo Controller. */
_.extend(this, window.SAILS_LOCALS);
},
mounted: async function() {
//
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
//…
}
});
views/pages/lista-tarefas.ejs, conforme abaixo:
<div class="container" id="lista-tarefas" v-cloak>
<p>Listagem montada no frontend pelo Vue 2 (a partir do window.SAILS_LOCALS)</p>
<ol>
<li v-for='tarefa in tarefas'>{{tarefa.descricao}}</li>
</ol>
<p>Listagem montada no backend pela renderização do template EJS (passagem de parametros pelo <em>Enginer</em> Sails</p>
<ol>
<% tarefas.forEach(tarefa=>{ %>
<li><%=tarefa.descricao%></li>
<% }) %>
</ol>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
sails lift, faça login e e navegue para sua Lista de Tarefas (conforme orientado acima). você deverá visualizar:
sails generate page
edit-tarefasconfig/routes.js a
seguinte linha
'GET /edit-tarefas': { action: 'view-edit-tarefas' },
'GET /edit-tarefas/:id': { action: 'view-edit-tarefas' },
api\controllers\view-edit-tarefas.js para que tenha o seguinte
conteúdo:
module.exports = {
friendlyName: 'View edit tarefas',
description: 'Display "Edit tarefas" page.',
inputs: {
id: {type: 'string', required: true}
},
exits: {
success: {
viewTemplatePath: 'pages/edit-tarefas'
}
},
fn: async function ({id}) {
let instance = await Tarefa.findOne({id: id});
return {instance};
}
};
assets\js\pages\edit-tarefas.page.js para que tenha o seguinte
conteúdo:
parasails.registerPage('edit-tarefas', {
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
tarefa: {},
prazo: new Date(),
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function() {
/* captura as variaveis / objetos da sessao */
/* e atribui a este objeto 'this'. */
/* Deste modo, o Vue2 pode acessar a lista */
/* de {tarefas} obtida no BD pelo Controller, */
/* mas neste caso, vamos explicitar uma outra */
/* entidade tarefa simplesmente para */
/* faciltiar a compreensao do codigo. */
_.extend(this, window.SAILS_LOCALS);
this.tarefa = this.instance;
this.prazo = new Date(this.tarefa.prazo);
},
mounted: async function() {
//…
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
/* Este metodo nao funcionara enquanto nao */
/* for adicionada a entrada 'saveTarefa' no */
/* arquivo cloud.setup.js. Aprenda como fazer */
/* isso na próxima etapa */
save: async function(tarefa) {
let result = await Cloud.saveTarefas.with({tarefa});
alert(JSON.stringify(result));
window.location.reload()
}
}
});
views/pages/edit-tarefas.ejs para que tenha o seguinte conteúdo:
<div id="edit-tarefas" class="container" v-cloak="" >
<div class="display-3">Manipulação dos dados com Vue</div>
<input type="hidden" name="_csrf" id="_csrf" value="<%=_csrf%>">
<label for="prioridade">Prioridade</label>
<input type="number" class="form-control" id="prioridade" name="prioridade" v-model="tarefa.prioridade" required>
<label for="descricao">Descrição</label>
<input type="text" class="form-control" id="descricao" name="descricao" v-model="tarefa.descricao" required>
<label for="prazo">Prazo</label>
<input type="date" class="form-control" id="prazo" name="prazo" v-model="prazo">
<button class="btn btn-primary mt-2" @click="save(tarefa)">Enviar</button>
<div class="display-3">Visualização dos dados com EJS</div>
<label for="pri">Prioridade</label>
<span class="form-control" id="pri"><%=instance.prioridade%></span>
<label for="des">Descricao</label>
<span class="form-control" id="des"><%=instance.descricao%></span>
<label for="praz">Prazo</label>
<span class="form-control" id="praz" ><%=new Date(instance.prazo).toLocaleDateString()%></span>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
/views/pages/lista-tarefas.ejs para que tenha o seguinte conteúdo:
<div id="lista-tarefas" v-cloak>
<p>Listagem montada no frontend pelo Vue 2 (a partir do window.SAILS_LOCALS)</p>
<ol>
<li v-for='tarefa in tarefas'><a v-bind:href="'/edit-tarefas/'+tarefa.id" >{{tarefa.descricao}}</a></li>
</ol>
<p>Listagem montada no backend pela renderização do template EJS (passagem de parametros pelo <em>Enginer</em> Sails</p>
<ol>
<% tarefas.forEach(tarefa=>{ %>
<li><a href="/edit-tarefas/<%=tarefa.id%>"><%=tarefa.descricao%></a></li>
<% }) %>
</ol>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
sails lift, faça login e navegue até sua listagem de tarefas. Você provavelmente visualizará algo do tipo:
sails generate action save-tarefas
para criar uma requisição POST destinada a atualização dos dados.
O resultado esperado deve ser algo do tipo:
Successfully generated:
•- api/controllers/save-tarefas.js
A few reminders:
(1) For most projects, you'll need to manually configure an explicit route
in your `config/routes.js` file; e.g.
'POST /api/v1/save-tarefas': { action: 'save-tarefas' },
(2) If you are using the built-in JavaScript SDK ("Cloud") for AJAX requests
from client-side code, then after configuring a new route, you'll want to
regenerate the SDK setup file using:
sails run rebuild-cloud-sdk
(3) This new action was generated in the "actions2" format.
[?] https://sailsjs.com/docs/concepts/actions
(4) Last but not least, since adding an action or route is a backend change,
don't forget to re-lift the server before testing!
api/controllers/save-tarefas.js
e faça as seguintes modificações:
module.exports = {
friendlyName: 'Save tarefas',
description: '',
inputs: {
tarefa: {type: 'ref', required: true}
},
exits: {
success: {
description: 'Salvo/Atualizado com sucesso'
}
},
fn: async function ({tarefa}) {
/* Este codigo recebe como parametro um objeto tarefa */
/* e verifica se ele e novo ou nao para criar ou */
/* atualizar, conforme o caso, retornando o proprio */
/* objeto em seguida. */
if(tarefa.id&&tarefa.id!=='new'){
return await Tarefa.updateOne({id: tarefa.id}, {
prioridade: tarefa.prioridade,
descricao: tarefa.descricao,
prazo: tarefa.prazo
});
}else{
return await Tarefa.create({
prioridade: tarefa.prioridade,
descricao: tarefa.descricao,
prazo: tarefa.prazo
}).fetch();
}
}
};
sails run rebuild-cloud-sdk.
Espera-se algo do tipo:
info: Initializing project hook... (`api/hooks/custom/`)
info: Initializing `apianalytics` hook... (requests to monitored routes will be logged!)
info: ·• Auto-migrating... (alter)
info: Hold tight, this could take a moment.
info: ✓ Auto-migration complete.
info: --
info: Successfully rebuilt Cloud SDK for use in the browser.
info: (and CLOUD_SDK_METHODS.json for use in automated tests)
assets/js/cloud.setup.js se uma nova entrada, como
abaixo, foi adicionada ao arquivo:
'saveTarefas': {'verb': 'POST', 'url': '/api/v1/save-tarefas', 'args': ['tarefa']}
sails
lift, fazer o login, navegar até a lista de tarefas, selecionar uma tarefa para edição, modificar os dados e clicar no botão [Enviar].
.tmp/localDiskDb/tarefa.db (ou simplesmente volte para a 'listagem de tarefas') e verifique se os dados foram alterados.Em aproximadamente 45 minutos...
sails new nome-prototipo;cd nome-prototipo;sails lift e navega para
http://localhost:1337;
admin@example.com e abc123;sails generate page nome-da-pagina e
adiciona a rota no arquivo config/routes.js;
sails generate action nome-actionsails
generate model nome-modelo;
let modelo
= await NomeModelo.find(), tornando os dados disponíveis para a sessão http em seguida;
Cloud.js e descritas no arquivo cloud.setup.js
sails lift