Leia e entenda a seção de fundamentos do guia Vue.js antes de continuar.
A Biblioteca de IU é criada com Vue.js. Ele fornece componentes reutilizáveis para criar interfaces de usuário consistentes e acessíveis para todas as aplicações PKP. Este capítulo descreve como passar dados para o componente raiz Vue.js e gerenciar o estado no navegador.
O Page component é o componente raiz em uma página que gerencia dados e passa props para componentes filhos. Sempre que um template de backend é usado, um componente Page
é montado automaticamente no DOM. Isso significa que os componentes globais podem ser usados sem qualquer configuração adicional.
{extends file="layouts/backend.tpl"}
{block name="page"}
<badge :is-success="true">Publicado</badge>
{/block}
A sintaxe do Smarty pode ser misturada com componentes da biblioteca de interface do usuário. O template abaixo mostra um selo quando uma subimissão é publicado.
{extends file="layouts/backend.tpl"}
{block name="page"}
{if $isPublished}
<badge :is-success="true">Publicado</badge>
{/if}
{/block}
Às vezes, o status da publicação muda antes que o template Smarty seja recarregado. Quando são realizadas ações no navegador que alteram o status da publicação, precisamos mostrar ou ocultar o <badge>
usando state.
Os dados que podem ser alterados após o carregamento da página são chamados de state. Por exemplo, quando um editor publica ou cancela a publicação de uma submissão, o template precisa ser atualizado para refletir o novo status da submissão.
State é outro nome para as propriedades
data
do componente da raiz Vue.js.
Inicialize o stste no servidor usando o método setState
para passar dados para o componente Page
.
class WorkflowHandler extends Handler {
public function distribution(array $args, Request $request) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setState([
'isPublished' => $publication->getData('status') === STATUS_PUBLISHED,
]);
return $templateMgr->display('/workflow.tpl');
}
}
O sate pode ser acessado em templates usando a sintaxe de template Vue.js. O exemplo abaixo mostrará ou ocultará o <badge>
quando o estado isPublished
for alterado.
{extends file="layouts/backend.tpl"}
{block name="page"}
<badge
v-if="isPublished"
:is-success="true"
>
Publicado
</badge>
{/block}
Use as ferramentas de desenvolvimento Vue.js para Firefox ou Chrome para alternar o estado e ver como o template muda.
O state só deve ser usado quando os dados são alterados, a interface do usuário deve atualizar para refletir essa alteração sem recarregar a página. Nem sempre é fácil determinar quais dados devem ser gerenciados pelo Vue.js como state e quais dados devem ser gerenciados pelo Smarty.
Para ajudar a fazer a distinção, considere uma rua da cidade. A qualquer momento, o número e a localização dos carros na rua mudarão. Mas os limites da rua e a direção da viagem não.
Neste exemplo, o número e a localização dos carros são state e devem ser passados para o template usando o método setState
. Os limites e a direção da viagem não são e podem ser passados para o modelo usando o método assign
.
O componente Page
às vezes gerencia o state que deve ser passado para um componente complexo. Uma única Page
pode gerenciar muitos componentes complexos, como Formulários e ListPanels que precisam atualizar o state após fazer solicitações à API.
A convenção descrita abaixo é uma alternativa leve para bibliotecas de gerenciamento de sate, como Vuex.
O state é passado para esses componentes como props
.
class WorkflowHandler extends Handler {
public function distribution(array $args, Request $request) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setState([
'formId' => 'exampleForm',
'fields' => [...],
]);
return $templateMgr->display('/workflow.tpl');
}
}
{extends file="layouts/backend.tpl"}
{block name="page"}
<pkp-form
:id="formId"
:fields="fields"
...
/>
{/block}
Isso leva a um problema quando o valor de um campo é alterado. O componente Form
não pode modificar esse valor porque o Vue.js impõe um fluxo de dados unidirecional.
Leia este guia sobre organização de componentes para entender como props e eventos são usados no Vue.js para gerenciar estado em vários componentes.
Nesses casos, os componentes Page
fazem uso de eventos para gerenciar o estado desses componentes. As props do componente são adicionadas a um objeto components
no estado.
class WorkflowHandler extends Handler {
public function distribution(array $args, Request $request) {
$templateMgr = TemplateManager::getManager($request);
$templateMgr->setState([
'components' => [
'exampleForm' => [
'fields' => [...],
],
],
]);
return $templateMgr->display('/workflow.tpl');
}
}
As props são vinculadas ao componente no template e o componente Page
escuta um evento set
.
{extends file="layouts/backend.tpl"}
{block name="page"}
<pkp-form
v-bind="components.exampleForm"
@set="set"
/>
{/block}
Quando o componente filho precisa atualizar suas props, ele dispara o evento set
com a seguinte assinatura.
const newData = {
fields: [...],
};
this.$emit('set', 'exampleForm', newData);
O componente Page
localizará as props do componente e as atualizará com o newData
. Para que isso funcione, o evento deve passar a chave do objeto, exampleForm
, como o segundo argumento na função $emit
.
O exemplo a seguir mostra como o método set
em um componente Page
atualiza os dados de um componente.
set(key, newData) {
if (!this.components[key]) {
return;
}
this.components[key] = {
...this.components[key],
...newData
};
}
Muitas vezes é necessário estender o componente Page
para fornecer funcionalidade adicional para uma página. O exemplo abaixo mostra o componente SettingsPage
, que adiciona ou remove um item do menu de navegação quando as notícias são habilitadas ou desabilitadas.
import Page from './Page.vue';
export default {
name: 'SettingsPage',
extends: Page,
data() {
return {
announcementsLabel: '',
announcementsUrl: ''
};
},
mounted() {
pkp.eventBus.$on('form-success', (formId, context) => {
if (formId === pkp.const.FORM_ANNOUNCEMENT_SETTINGS) {
let menu = {...this.menu};
if (!context.enableAnnouncements) {
delete menu.announcements;
} else {
menu.announcements = {
name: 'Announcements',
url: 'http://example.org/announcements'
};
}
this.menu = menu;
}
});
},
destroyed() {
pkp.eventBus.$off('form-success');
}
};
Após a criação de um novo componente de página, ele deve ser importado e registrado em js/load.js
.
...
import SettingsPage from '@/components/Container/SettingsPage.vue';
window.pkp = Object.assign(PkpLoad, {
controllers: {
...
SettingsPage
}
});
Finalmente, o PageHandler
deve atribuir a variável pageComponent
ao template e passar o state correto
$templateMgr = TemplateManager::getManager($request);
$templateMgr->assign([
'pageComponent' => 'SettingsPage',
]);
$templateMgr->setState([
'announcementLabel' => __('announcement.announcements'),
'announcementsUrl' => $request->getRouter()->url($request, null, 'management', 'settings', 'announcements'),
])
Consulte a Biblioteca de IU para obter uma lista de componentes de página disponíveis.
A Biblioteca de UI fornece uma demonstração, especificação técnica e orientação de uso para cada componente. Ele também fornece documentação importante sobre utilitários como o barramento de eventos, localização e muito mais.
O próximo capítulo descreve como criar e usar Formulários.