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
24 Injetar objetos nos componentes, stores e routes.
Para o correto funcionamento de uma aplicação SSR
, alguns serviços precisam ser instanciados de maneira isolada, de forma, que um usuário não consiga acessar a uma instancia destinada a outro usuário, em resumo, todos os serviços devem está preferencialmente isoladas dentro do escopo do usuário.
O primeiro passo para conseguir este objetivo, é criar um utilitário que irá injetar estes serviços nos components
, store
e routes
.
QPANC.App/src/boot/inject.js
import Vue from 'vue' const mixins = [] const inject = function (bootCb) { return async function (ctx) { const { app, router, store } = ctx let boot if (typeof bootCb === 'function') { const response = bootCb(ctx) boot = response.then ? await response : response } else { boot = bootCb } for (const name in boot) { const key = `$${name}` if (mixins.indexOf(name) === -1) { mixins.push(name) Vue.mixin({ beforeCreate () { const options = this.$options if (options[name]) { this[key] = options[name] } else if (options.parent) { this[key] = options.parent[key] } } }) } app[name] = boot[name] store[key] = boot[name] router[key] = boot[name] } } } export default inject
Agora, um exemplo de utilização.:
Digamos que tenhamos um serviço que faz o print da mensagem Hello World
no console.:
class DummieService { sayHello ({ name }) { console.log(`${name}: Hello Wolrd`) } }
E agora queremos injetar uma instancia do serviço acima em todos os componentes
, stores
e routes
, poderemos faze-lo em um arquivo de boot.
import inject from './inject' import DummieService from 'services/dummie' export default inject(({ Vue }) => { const dummie = new DummieService() return { dummie: dummie } })
feito isto, poderemos acessar this.$dummie
nos componentes
e stores
, assim como router.$dummie
nos navigation guards, segue alguns exemplos.:
sample/page/dummie.js
export default { data () { const initialMsg = this.$dummie.sayHello({ name: 'me' }) return { msg: initialMsg } }, methods: { changeName ({ name }) { this.msg = this.$dummie.sayHello({ name }) } } }
sample/store/dummie.js
export default { namespaed: true, state () { return { name: 'me', msg: '' } }, mutations: { name (state, value) { state.name = value }, msg (state, value) { state.msg = value } }, actions: { setMessage ({ commit }, name) { const msg = this.$dummie.sayHello({ name }) commit('msg', msg) } }, getters: { getMessage (state) { return this.$dummie.sayHello({ name: state.name }) } } }
sample/router/dummie.js
export default function (context) { return { path: '/dummie', beforeEnter (to, from, next) { const { $dummie } = context.router const msg = $dummie.sayHello({ name: to.params.name }) if (msg.length > 50) { next('/hello-long-name') } else { next('/hello-short-name') } } } }
25 Customizando os componentes do Quasar
Para esta tarefa, estarei usando a extensão @toby-mosque/utils
, porém não irei mostrar um código equivalente sem o uso da extensão, pois envolve um transparent wrapper
bem intricado, onde um pequeno deslize, pode levar a um bug difícil de rastrear ou a um comportamento indesejado.
Para esta demostração, estaremos personalizando apenas o QInput
, mas podemos customizar qual quer componente, inclusive aqueles que são instalados através de extensões.
O primeiro passo, é criar um boot, aqui chamaremos ele de brand
quasar new boot brand
QPANC.App/quasar.config.js
module.exports = function (ctx) { return { boots: [ 'brand' ] } }
QPANC.App/src/boot/inject.js
import { factory } from '@toby.mosque/utils' import inject from './inject' import { QInput } from 'quasar' // "async" is optional export default inject(({ Vue }) => { const brand = {} brand.input = Vue.observable({ /* style: { 'font-size': '12px' }, class: { 'custom-input': true }, */ props: { outlined: true } }) factory.reBrand('q-input', QInput, brand.input) return { brand } })
O objeto brand
será injetado nos componentes
e stores
, então poderemos acessa-lo futuramente.
A propriedade input
é um Vue.observable
, estão qual quer alteração nele, será refletido para os componentes que fazem uso dele. input
é apenas um nome, poderia ser qual quer coisa no lugar.
O factory.reBrand
, é o responsável por injetar o brand.input
em todos os q-input
. Ele fará uso apenas das propriedades style
, class
e props
, onde estas propriedades serão injetadas no style
, class
e props
do respectivo componente, sendo que nenhum deles é obrigatório.
Caso execute a aplicação agora, verá que todos os inputs estarão com a propriedade :outlined="true"
Agora, vamos adaptar o exemplo acima, para usar o Dark Mode
, onde os inputs deverão ser filled
no modo dark
e outlined
no light
QPANC.App/src/boot/inject.js
import { factory } from '@toby.mosque/utils' import inject from './inject' import { QInput, Dark } from 'quasar' // "async" is optional export default inject(({ Vue }) => { const brand = {} brand.input = Vue.observable({ /* style: { 'font-size': '12px' }, class: { 'custom-input': true }, */ props: { filled: Dark.isActive outlined: !Dark.isActive } }) factory.reBrand('q-input', QInput, brand.input) return { brand } })
O problema aqui, é que o Dark.isActive
está sendo usado apenas como o valor inicial para o brand.input
, porém quando o Dark.isActive
é alterado, ele não é propagado para o brand.input
.
Então, precisaremos de um watch
no App.vue
.
QPANC.App/src/App.vue
export default { name: 'App', watch: { '$q.dark.isActive' () { this.$brand.input.props.filled = this.$q.dark.isActive this.$brand.input.props.outlined = !this.$q.dark.isActive } } }
E por fim, algumas prints.:
25 Persistência em Cookies
Como se trata de uma aplicação SSR
, é natural que alguns dados serão persistidos no lado do cliente, coisas como o Token JWT
, o tema e o idioma preferido.
Porém, alguns destes dados precisam ser acessados no lado do servidor, e como não podem ser recuperados de outra forma, teremos de usar Cookies.
O primeiro passo, será ativar o plugin responsável por ler os Cookies.
QPANC.App/quasar.config.js
module.exports = function (ctx) { return { framework: plugins: [ 'Cookies' ] } } }
Agora, iremos adicionar um plugin para o vuex, no caso o vuex-persistedstate
yarn add vuex-persistedstate
Agora, adicione o boot persist
, e não deixe e adicionar ele ao quasar.config.js
> boots
, é vital que ele seja adicionado antes do boot do axios
e do i18n
.
quasar new boot persist
QPANC.App/quasar.config.js
module.exports = function (ctx) { return { boots: [ 'persist', 'i18n', 'axios' ] } }
Antes de codificamos o boot persist
, precisamos criar um pequeno serviço, que será responsável por detectar o idioma recomendado para o usuário.
QPANC.App/src/services/locales.js
const locales = ['en-us', 'pt-br'] const regions = { en: 'en-us', pt: 'pt-br' } const fallback = regions.en const detectLocale = function () { if (process.env.CLIENT) { const locale = navigator.language.toLowerCase() if (locales.includes(locale)) { return locale } const region = locale.split('-')[0] if (region in regions) { return regions[region] } return regions.en } else { return fallback } } export { locales, regions, fallback, detectLocale }
Agora, vamos ao boot:
QPANC.App/src/boot/persist.js
import { Cookies, Quasar } from 'quasar' import createPersistedState from 'vuex-persistedstate' const persistState = function ({ name, store, storage }) { createPersistedState({ key: name, paths: [name], filter ({ type }) { return type.startsWith(name) }, storage })(store) } export default function ({ store, ssrContext }) { const cookies = process.env.SERVER ? Cookies.parseSSR(ssrContext) : Cookies const cookieStorage = { getItem (key) { return JSON.stringify(cookies.get(key)) }, setItem (key, value) { cookies.set(key, value, { path: '/' }) }, removeItem (key) { cookies.remove(key, { path: '/' }) } } persistState({ name: 'app', store, storage: cookieStorage }) if (process.env.CLIENT) { // persistState({ name: 'local', store, storage: window.localStorage }) store.commit('app/localeOs', detectLocale()) } }
Um pequeno detalhamento sobre o que está sendo feito:
persistState({ name: 'app', store, storage: cookieStorage })
Estamos instruindo o vuex-persistedstate
à persistir todo o modulo app
em um Cookie
if (process.env.CLIENT) { // persistState({ name: 'local', store, storage: window.localStorage }) }
Caso o comentário seja removido, estamos instruindo o vuex-persistedstate
à persistir todo o modulo local
no localStorage
if (process.env.CLIENT) { store.commit('app/localeOs', detectLocale()) }
Estamos atualizando o localeOs
para que ele seja igual ao locale
disponível mais próximo ao que é utilizado pelo browser, este vai ser o idioma da aplicação, caso o usuário também não especifique o localeUser
.
Porém vale lembrar, que o código no cliente é executado após a execução no servidor, então, na primeira requisição, o servidor irá sempre utilizar a linguagem padrão, no caso, o inglês (isto é configurável em quasar.config.js > framework > lang
).
Desta forma, na primeira requisição, o app será carregado usando a linguagem padrão, e irá alternar para a linguagem informada pelo browser após a conclusão do carregamento da pagina.
Agora, vamos criar o nosso modulo app
QPANC.App/src/store/app.js
import { factory } from '@toby.mosque/utils' class AppStoreModel { constructor ({ token = '', localeOs = '', localeUser = '' } = {}) { this.token = token this.localeOs = localeOs this.localeUser = localeUser } } const options = { model: AppStoreModel } export default factory.store({ options, getters: { locale (state) { return state.localeUser || state.localeOs } } }) export { options, AppStoreModel }
Como se trata de um modulo global, não esqueça de registra-lo no QPANC.App/src/store/index.js
QPANC.App/src/store/index.js
import Vue from 'vue' import Vuex from 'vuex' import app from './app' Vue.use(Vuex) export default function (context) { const Store = new Vuex.Store({ modules: { app }, strict: process.env.DEV }) return Store }
Top comments (0)