QPANC são as iniciais de Quasar PostgreSQL ASP NET Core.
- Source
- Introdução
- Parte I - ASP.NET - Inicializando os Projetos
- Parte 2 - PostgreSQL
- Parte 3 - ASP.NET - Registrando Serviços e Lendo Variáveis de Ambiente
- Parte 4 - ASP.NET - Entity Framework e ASP.NET Core Identity
- Parte 5 - ASP.NET - Documentação Interativa com Swagger
- Parte 6 - ASP.NET - Regionalização
- Parte 7 - ASP.NET - Autenticação e Autorização
- Parte 8 - ASP.NET - CORS
- Parte 9 - Quasar - Criação e Configuração do Projeto
- Parte 10 - Quasar - Configurações e Customizações
- Parte 11 - Quasar - Componentes - Diferença entre SPA e SSR
- Parte 12 - Quasar - Serviços
- Parte 13 - Quasar - Regionalização e Stores
- Parte 14 - Quasar - Consumindo a API
- Parte 15 - Quasar - Login
- Parte 16 - Quasar - Áreas Protegidas
- Parte 17 - Quasar - Registro
- Parte 18 - Docker - Maquina Virtual Linux
- Parte 19 - Docker - Registro e Build
- Parte 20 - Docker - Traefik e Publicação
- Demo Online
18 Customização de Temas e Modos (Dark e Light)
A customização do tema e do modo dark
é feita de forma semelhante, porém a classe do tema deve ser definida pelo desenvolvedor, enquanto a classe do modo dark
é injetada pelo framework
, porém o recomendado é que ambas sejam inseridas no body
.
Para ativar o modo dark
, você deve alterar o quasar.config.js > framework > config > dark
Quasar.App/quasar.config.js
module.exports = function (ctx) {
return {
framework: {
config: {
dark: 'auto'
}
}
}
}
abaixo segue um exemplo de customização para o modo dark
.:
Quasar.App/src/css/app.sass
// app global css in Sass form
body.body--dark
color: $grey-1
--q-color-primary: #26a69a
--q-color-secondary: #027BE3
--q-color-accent: #9C27B0
--q-color-positive: #21BA45
--q-color-negative: #C10015
--q-color-info: #31CCEC
--q-color-warning: #F2C037
.text-main
color: $grey-10
.text-content
color: $grey-9
.text-content-2
color: $grey-8
.bg-main
background: $grey-10
.bg-content
background: $grey-9
.bg-content-2
background: $grey-8
body.body--light
color: $grey-10
--q-color-primary: #027BE3
--q-color-secondary: #26A69A
--q-color-accent: #9C27B0
--q-color-positive: #21BA45
--q-color-negative: #C10015
--q-color-info: #31CCEC
--q-color-warning: #F2C037
.text-main
color: white
.text-content
color: $grey-2
.text-content-2
color: $grey-4
.bg-main
background: white
.bg-content
background: $grey-2
.bg-content-2
background: $grey-4
No caso acima, criamos as cores personalizadas main
, content
e content-2
para os modos dark
e light
, assim como definimos as cores base (primary
, secondary
, accent
, positive
, negative
, info
, warning
)
Note que as cores definidas para o modo dark
, não foram bem selecionas, por tanto pode haver um problema de contraste no modo dark
caso haja a necessidade de implementar múltiplos temas ou esquemas de cores, você poderá definir classes base com a seguinte estrutura: body.${nome_tema}.body--dark
e body.${nome_tema}.body--light
, e um nível abaixo, definir as customizações.
19 Internacionalização - I18n
A internacionalização no Quasar é bem simples, basicamente são arquivos JSON
, então cabe a você desenvolvedor definir como estes textos serão organizados.
Mas dado a estrutura deste projeto, não será possível a flexibilização quanto a estrutura dos textos usados nas validações, ou seja, os erros e os nomes dos rótulos dos campos, aqui chamados de fields
.
Então, iremos criar dois arquivos dentro da pasta Quasar.App/src/i18n
.
Quasar.App/src/i18n/en-us/index.js
export default {
validations: {
compare: '{field} and {other} do not match',
email: 'The {field} field is not a valid e-mail address',
required: 'The {field} field is required'
},
fields: {
confirmPassword: 'Confirm your Password',
confirmUserName: 'Confirm your Email',
firstName: 'First Name',
lastName: 'Last Name',
password: 'Password',
userName: 'Email'
}
}
Quasar.App/src/i18n/pt-br/index.js
export default {
validations: {
compare: '{field} e {other} não são iguais',
email: 'O campo {field} não possui um email válido',
required: 'O campo {field} é requerido'
},
fields: {
confirmPassword: 'Confirme à Senha',
confirmUserName: 'Confirme o Email',
firstName: 'Nome',
lastName: 'Sobrenome',
password: 'Senha',
userName: 'Email'
}
}
Apenas para ficar claro, os nomes entre {}
são placeholders
, por exemplo, {field}
em The {field} field is required
será substituído pelo nome do campo que está sendo validado.
Agora precisamos registrar estes textos localizados.
Quasar.App/src/i18n/index.js
import enUS from './en-us'
import ptBR from './pt-br'
export default {
'en-us': enUS,
'pt-br': ptBR
}
20 Validações
Iremos utilizar o sistema de validações que o Quasar oferece, porém iremos encapsular isto dentro de um serviço, para que posamos aplicar os textos localizados.
então, crie o seguinte serviço:
Quasar.App/src/services/validations.js
const validations = {}
validations.required = ({ self, field }) => {
return function required (val) {
return !!val || self.$t('validations.required', {
field: self.$t(`fields.${field}`)
})
}
}
const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
validations.email = ({ self, field }) => {
return function required (val) {
return emailRegex.test(val) || self.$t('validations.email', {
field: self.$t(`fields.${field}`)
})
}
}
validations.server = ({ self, field }) => {
return function server (val) {
return self.validationArgs[field].server
}
}
export default function validation (self, context) {
const _validations = {}
for (const field in context) {
const _rules = []
const rules = context[field]
const names = Object.keys(validations)
for (const rule of names) {
if (rules.includes(rule)) {
_rules.push(validations[rule]({ self, field }))
}
}
_validations[field] = _rules
}
_validations.resetServer = function reset () {
for (const field in context) {
if (field in self.validationArgs && 'server' in self.validationArgs[field]) {
self.validationArgs[field].server = true
}
}
}
return _validations
}
no serviço acima, as linas que merecem atenção são:
function required (val) {
return !!val || self.$t('validations.required', {
field: self.$t(`fields.${field}`)
})
}
function email (val) {
return emailRegex.test(val) || self.$t('validations.email', {
field: self.$t(`fields.${field}`)
})
}
Em ambos os casos, temos uma função que retorna um boolean
ou uma string
, caso retorne true
, é por que passou no teste, se retornar uma string
, é por que o teste falhou, e a string
é a mensagem de erro. o self.$t
serve para recuperamos esta mensagem já localizada.
return function server (val) {
return self.validationArgs[field].server
}
Já esta validação dependente de um dado proveniente do componente, neste caso, o retorno da API.
Desta forma, se quisemos validar o userName
e o password
, podemos montar as rules da seguinte forma.:
import validations from 'services/validations'
export default {
data () {
const self = this
const validation = validations(self, {
userName: ['required', 'email', 'server'],
password: ['required', 'server']
})
return {
userName: '',
password: '',
validation,
validationArgs: {
userName: {
server: true
},
password: {
server: true
}
}
}
}
}
<q-nput label="$t('fields.userName')" v-model="userName" :rules="validation.userName" @blur="validationArgs.userName.server = true" />
<q-nput label="$t('fields.password')" type="password" v-model="password" :rules="validation.password" @blur="validationArgs.password.server = true" />
Através do objeto validationArgs
, conseguimos passar alguma informação complementar o serviço que constrói as validações, por exemplo, validationArgs.userName.server
será utilizado pela validação server
.
Lembrando que userName
e password
devem está presentes no arquivo de localização (i18n), assim como as regras required
, email
e server
.
Assim como as regras utilizadas (no caso acima, required
, email
e server
) devem está devidamente implementadas no serviço validations
.
Nos próximos capítulos, nós veremos estas validações em ação.
E para fechamos estes assunto, precisamos criar um alias para a pasta services
, o primeiro passo é adicionar o seguinte bloco de código no quasar.config.js
> build
> extendWebpack
QPANC.App/quasar.config.js
const path = require('path')
module.exports = function (ctx) {
return {
build: {
extendWebpack (cfg, { isServer, isClient }) {
cfg.resolve.alias = {
...cfg.resolve.alias,
services: path.resolve(__dirname, './src/services'),
}
}
}
}
}
e para permitir que o intellisense funcione no VSCode, precisamos adicionar o seguinte item jsconfig.json
> compilerOptions
> paths
QPANC.App/jsconfig.json
{
"compilerOptions": {
"paths": {
"services/*": [
"src/services/*"
]
}
}
}
20 Validações
Iremos utilizar o sistema de validações que o Quasar
oferece, porém iremos encapsular isto dentro de um serviço, para que posamos aplicar os textos localizados.
então, crie o seguinte serviço:
Quasar.App/src/services/validations.js
const validations = {}
validations.required = ({ self, field }) => {
return function required (val) {
return !!val || self.$t('validations.required', {
field: self.$t(`fields.${field}`)
})
}
}
const emailRegex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
validations.email = ({ self, field }) => {
return function required (val) {
return emailRegex.test(val) || self.$t('validations.email', {
field: self.$t(`fields.${field}`)
})
}
}
validations.server = ({ self, field }) => {
return function server (val) {
return self.validationArgs[field].server
}
}
export default function validation (self, context) {
const _validations = {}
for (const field in context) {
const _rules = []
const rules = context[field]
const names = Object.keys(validations)
for (const rule of names) {
if (rules.includes(rule)) {
_rules.push(validations[rule]({ self, field }))
}
}
_validations[field] = _rules
}
_validations.resetServer = function reset () {
for (const field in context) {
if (field in self.validationArgs && 'server' in self.validationArgs[field]) {
self.validationArgs[field].server = true
}
}
}
return _validations
}
no serviço acima, as linhas que merecem atenção são:
function required (val) {
return !!val || self.$t('validations.required', {
field: self.$t(`fields.${field}`)
})
}
function email (val) {
return emailRegex.test(val) || self.$t('validations.email', {
field: self.$t(`fields.${field}`)
})
}
Em ambos os casos, temos uma função que retorna um boolean
ou uma string
, caso retorne true
, é por que passou no teste, se retornar uma string
, é por que o teste falhou, e a string
é a mensagem de erro. o self.$t
serve para recuperamos esta mensagem já localizada.
return function server (val) {
return self.validationArgs[field].server
}
Já esta validação dependente de um dado proveniente do componente, neste caso, o retorno da API.
Desta forma, se quisemos validar o userName
e o password
, podemos montar as rules da seguinte forma.:
import validations from 'services/validations'
export default {
data () {
const self = this
const validation = validations(self, {
userName: ['required', 'email', 'server'],
password: ['required', 'server']
})
return {
userName: '',
password: '',
validation,
validationArgs: {
userName: {
server: true
},
password: {
server: true
}
}
}
}
}
<q-nput label="$t('fields.userName')" v-model="userName" :rules="validation.userName" @blur="validationArgs.userName.server = true" />
<q-nput label="$t('fields.password')" type="password" v-model="password" :rules="validation.password" @blur="validationArgs.password.server = true" />
Através do objeto validationArgs
, conseguimos passar alguma informação complementar ao serviço que constrói as validações, por exemplo, validationArgs.userName.server
será utilizado pela validação server
.
Lembrando que userName
e password
devem está presentes no arquivo de localização (i18n
), assim como as regras required
, email
e server
.
Assim como as regras utilizadas (no caso acima, required
, email
e server
) devem está devidamente implementadas no serviço validations
.
Nos próximos capítulos, nós veremos mais sobre estas validações.
E para fechamos estes assunto, precisamos criar um alias para a pasta services
, o primeiro passo é adicionar o seguinte bloco de código no quasar.config.js > build > extendWebpack
QPANC.App/quasar.config.js
const path = require('path')
module.exports = function (ctx) {
return {
build: {
extendWebpack (cfg, { isServer, isClient }) {
cfg.resolve.alias = {
...cfg.resolve.alias,
services: path.resolve(__dirname, './src/services'),
}
}
}
}
}
e para permitir que o intellisense funcione no VSCode, precisamos adicionar o seguinte item jsconfig.json
> compilerOptions
> paths
QPANC.App/jsconfig.json
{
"compilerOptions": {
"paths": {
"services/*": [
"src/services/*"
]
}
}
}
21 Instalando extensões
Como a aplicação está rodando em uma VM
, e ao instalar uma extensão será adicionado um pacote pelo npm
ou yarn
. você terá que adotar uma das seguintes rotinas.:
21.1 - quasar ext add extensao_id
Você poderá adicionar a extensão usando o quasar ext add extensao_id
, porém será preciso excluir a pasta node_modules
e os arquivos de lock (yarn.lock
e/ou package-lock.json
). exemplo:
quasar ext invoke qautomate
21.2 - quasar ext invoke extensao_id
Para usar o invoke, é preciso instalar o pacote utilizado pela extensão antes, por exemplo, para a extensão qautomate
:
QPANC.App/package.json
{
"devDependencies": {
"quasar-app-extension-qautomate": "^1.0.0-alpha.5"
}
}
então execute a aplicação para que o pacote seja instalado a partir da VM
gerenciada pelo Docker
.
Após parar o container, podemos usar o invoke:
quasar ext invoke qautomate
21.3 - Considerações
Eu sempre opto por fazer o add
, então apago o node_modules
e o yarn.lock
, por mais que seja mais demorado, é mais pratico, menos propenso a erros e me dá uma desculpa para ir tomar um café.
Top comments (0)