Instalar o aplicativo de exemplo
Objetivos de aprendizagem
- Instalar o aplicativo de exemplo Visualizador de registros.
- Entender a estrutura do aplicativo Visualizador de registros.
- Visualizar um registro no aplicativo Visualizador de registros.
Instalar o aplicativo Visualizador de registros

- Se não tiver o Git instalado, instale-o primeiro.
- Clone o repositório Git do Visualizador de registros. Se você não sabe clonar um repositório, consulte Como clonar um repositório na Ajuda do GitHub.
- O Visualizador de registros é executado em um contêiner do Docker. Instale a versão estável do Docker Engine (Community). O Docker solicita a criação de uma conta para baixar o software.
- Inicie o Docker.
O Visualizador de registros é um aplicativo conectado, ou seja, um aplicativo que se comunica com o Salesforce por meio de APIs. O aplicativo não é executado na Salesforce Platform, mas está conectado por meio de APIs. Para definir como o aplicativo Visualizador de registros é autenticado no Salesforce, acesse Configuração e crie um aplicativo conectado.
- No Trailhead Playground, em Configuração, insira App e selecione Gerente de aplicativo.
- Clique em Novo aplicativo conectado e insira estas configurações:
- Nome do aplicativo conectado: RecordViewer
- Email de contato: Seu endereço de email
- Selecione Habilitar as configurações do OAuth e insira estas configurações:
- URL de retorno de chamada: https://localhost:8443/oauth-redirect
- Escopos OAuth selecionados: Access and manage your data (api) (Acessar e gerenciar seus dados (api))
- Clique em Salvar.
- Na página Novo aplicativo conectado, clique em Continuar.
- Copie e salve a Chave do cliente (também conhecida como ID do cliente OAuth). Ela será necessária depois.
- Em Configuração, insira CORS e selecione CORS.
- Clique em Novo.
- Para Padrão de URL de origem, insira https://localhost:8443
- Clique em Salvar.
Para criar o aplicativo Visualizador de registros e iniciar o servidor, use uma interface de linha de comando como o Terminal do MacOS ou o Prompt de comando do Windows.
cd /replace/with/path/to/RecordViewer
Em seguida, execute estes comandos do Docker.
docker-compose build && docker-compose up -d
Para autorizar o Visualizador de registros a fazer solicitações ao Salesforce, insira a URL de login do Salesforce, https://login.salesforce.com e a chave do cliente que foi salva do aplicativo conectado e clique em Login.
Para que o aplicativo Visualizador de registros possa acessar sua organização do Salesforce, verifique se seu nome de usuário está correto e clique em Permitir.
Usar o aplicativo Visualizador de registros
Use o Visualizador de registros para visualizar, editar, criar e excluir registros do Salesforce. O Visualizador de registros usa a API de interface de usuário para fazer essas operações nos registros do Salesforce. O aplicativo também oferece uma maneira prática de examinar a resposta JSON da API de interface de usuário.
- Em um navegador, abra a organização do Salesforce que usou para criar o aplicativo conectado Visualizador de registros.
- Clique em Contas e depois em Nova.
- Em Nome da conta, insira Contêineres universais
- Em Telefone, insira 206 555 1212 e clique em Salvar.
- Para ver a página de detalhes do novo registro, clique em Detalhes.

- Em um navegador, abra https://localhost:8443.
- Em Itens recentes, clique em Contêineres universais.
O aplicativo Visualizador de registros mostra a página de detalhes do registro.
Falamos sobre o poder da API de interface de usuário, ou seja, suas respostas respeitam as alterações de metadados feitas na organização do Salesforce. Então, vamos remover um campo do layout da Conta na organização do Salesforce e ver o que acontece no Visualizador de registros.
Contêineres universais tem um número de telefone, mas a página de detalhes do registro também tem um campo chamado Fax. Ninguém mais usa fax, então vamos remover isso do layout de página.
- No Trailhead Playground, em Configuração, insira Objeto e selecione Gerenciador de objetos.
- Clique em .
- No editor de layout de página, clique para remover o campo Fax e clique em Salvar.
Agora, retorne ao aplicativo Visualizador de registros e clique em Exibir registro para recarregar o registro Contêineres universais. Tcharam! O campo Fax desapareceu e nenhuma linha de código foi alterada.
Nesse caso, o campo foi removido do layout. Em uma situação do mundo real, o administrador não precisa avisar quando muda um layout, seu aplicativo apenas responde!
Estrutura do aplicativo Visualizador de registros
Para trabalhar com a API de interface de usuário, é possível usar qualquer estrutura ou linguagem da Web que possa fazer solicitações HTTP. Polymer, React, Angular, Vue, iOS, Android – use a tecnologia da sua preferência.
O aplicativo Visualizador de registros é um aplicativo de página única criado usando React, Redux e Redux-Saga. Examinaremos rapidamente as noções básicas dessas tecnologias quando analisarmos a estrutura de arquivos do aplicativo. Se você não estiver familiarizado com o React e o Redux, não há problema. Apenas tente perceber as ideias gerais.
Seu objetivo é aprender como acessar os recursos da API de interface de usuário e como lidar com as respostas. Cada estrutura e linguagem tem maneiras ligeiramente diferentes de concluir essas tarefas, mas os conceitos principais são os mesmos e a API se comporta da mesma forma.
O aplicativo Visualizador de registros foi construído usando:
- Pug – gera páginas HTML a partir de modelos do lado do servidor.
- React – uma biblioteca JavaScript para criar componentes personalizados e combiná-los para compor interfaces de usuário
- Redux – mantém o estado de um aplicativo React em um depósito, que é um objeto JavaScript único e imutável. Os componentes React enviam objetos JavaScript chamados de ações, que são interceptados por redutores. Redutores são funções JavaScript que usam ações como entradas para atualizar o estado do Redux.
- Redux-Saga – como os redutores, sagas são funções JavaScript que são executadas quando interceptam uma ação. As sagas gerenciam solicitações assíncronas para a API de interface de usuário. No final de uma operação assíncrona, as sagas enviam uma ação, que pode ser interceptada por um redutor que atualiza o depósito. As sagas evitam a confusão entre retornos de chamada.
- Node.js – um tempo de execução no lado do servidor para aplicativos JavaScript
Vejamos onde esses conceitos abstratos estão no mundo real. Inicie seu IDE favorito e abra a pasta RecordViewer que foi clonada do GitHub.
Primeiro, vejamos a pasta visualizações, que contém os modelos de HTML Pug. O aplicativo tem três visualizações: a página de login, onde foram inseridas as informações de login do Salesforce; a página de visualização de registros, que é o contêiner principal do aplicativo; e uma página de erro, que você, esperamos, ainda não viu. O quarto modelo Pug é um redirecionamento de autenticação OAuth.
<!-- /views/recordView.pug --> doctype html html block head title Record Viewer // Metadata and style sheet links removed to keep sample short script(type='text/javascript', src='https://code.jquery.com/jquery-2.0.3.min.js') script(type='text/javascript', src='root.js') body .slds-grid.slds-wrap nav.slds-col.slds-size--1-of-1 .slds-page-header p.slds-page-header__title.slds-truncate.slds-align-middle a(href='/recordView') Record Viewer p.slds-page-header__info powered by User Interface API #root script. $( document ).ready(function() { let rootNode = document.getElementById("root"); renderRoot("#{targetUrl}", "#{accessToken}", "#{recordId}", rootNode); });
/* Excerpt from /client-src/root.js */ // Root renderer for record viewer. global.renderRoot = function(instanceUrl, accessToken, recordId, rootNode) { const sagaMiddleware = createSagaMiddleware() const store = createStore(rootReducer, {login:{instanceUrl, accessToken}, record:{recordId}}, applyMiddleware(sagaMiddleware)); sagaMiddleware.run(rootSaga); ReactDOM.render( <Provider store={store}> <div> <RecordViewerWrapper /> </div> </Provider>, rootNode); }
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */ // Presentational Component that uses state to decide how to // construct the RecordViewer. const mapStateToProps = (state) => { if (state.record.record) { return { screen: 'RECORD', record: state.record.record, headerRecordId: state.header.recordId, mode: state.record.mode, context: state.context, prevMode: state.record.prevMode, creds: state.login, picklists : state.picklists, depGraph: state.depGraph, rawjson: state.rawjson, error: state.error } } else if (state.record.recordId) { return { screen: 'FETCH_RECORD', recordId: state.record.recordId, mode: 'View', context: state.context, creds: state.login, rawjson: state.rawjson, error: state.error } } // Additional mappings removed to shorten sample
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */ const mapDispatchToProps = (dispatch) => { return { onFetchRecord: (creds, id, context) => { dispatch(actions.fetchRecord(creds, id, context)) }, onRecordClick: (creds, id, context) => { dispatch(actions.fetchRecord(creds, id, context)) }, onViewRecordClick: (creds, id, context) => { dispatch(actions.fetchRecord(creds, id, context)) }, onFormFactorSelect: (formFactor, recordId) => { dispatch(actions.updateFormFactor(formFactor, recordId)) }, // Additional mappings removed to shorten sample
/* Excerpt from /client-src/containers/RecordViewerWrapper.js */ const RecordViewerWrapper = connect( mapStateToProps, mapDispatchToProps ) (RecordViewer) export default RecordViewerWrapper
As ações, redutores e sagas ficam (tranquilamente) nas pastas ações, redutores e sagas.
/* Excerpt from /client-src/actions/index.js */ export const fetchEntities = (creds) => { return { type: 'FETCH_ENTITIES', creds } } export const receiveEntities = (entities) => { return { type: 'RECEIVE_ENTITIES', entities, receivedAt: Date.now() } }
/* /client-src/sagas/rootSaga.js */ import { takeEvery } from 'redux-saga' import recentItemsFetcher from './recentItemsFetcher' import recordFetcher from './recordFetcher' import recordDeleter from './recordDeleter' import recordUpdater from './recordUpdater' import entitiesFetcher from './entitiesFetcher' import recordCreator from './recordCreator' import cloneDefaultsFetcher from './cloneDefaultsFetcher' import createDefaultsFetcher from './createDefaultsFetcher' import picklistsFetcher from './picklistsFetcher' import depGraphValueUpdater from './depGraphValueUpdater' export default function* rootSaga() { yield takeEvery('FETCH_RECORD', recordFetcher) yield takeEvery('FETCH_RECENT_ITEMS', recentItemsFetcher) yield takeEvery('DELETE_RECORD', recordDeleter) yield takeEvery('SAVE_RECORD', recordUpdater) yield takeEvery('FETCH_ENTITIES', entitiesFetcher) yield takeEvery('CREATE_RECORD', recordCreator) yield takeEvery('FETCH_CREATE_DEFAULTS', createDefaultsFetcher); yield takeEvery('FETCH_CLONE_DEFAULTS', cloneDefaultsFetcher); yield takeEvery('FETCH_PICKLISTS', picklistsFetcher); yield takeEvery('UPDATE_DEP_GRAPH_FIELD_VALUE', depGraphValueUpdater) }
/* /client-src/sagas/entitiesFetcher.js */ import { call, put } from 'redux-saga/effects' import { receiveEntities } from '../actions' export default function* entitiesFetcher(action) { let mruUrl = action.creds.instanceUrl + '/services/data/v48.0/ui-api/object-info' let req = { method: 'GET', headers: { 'Authorization' : 'Bearer ' + action.creds.accessToken, 'X-Chatter-Entity-Encoding': false} } try { const response = yield call(fetch, mruUrl, req) const responseJson = yield response.json() yield put(receiveEntities(responseJson)) } catch(err) { console.error('Describe sobjects error: ' + JSON.stringify(err)) } }
/* /client-src/reducers/entities.js */ const entities = (state = {sobjects: []}, action) => { switch (action.type) { case 'RECEIVE_ENTITIES': return { sobjects: action.entities.objects, receivedAt: action.receivedAt } default: return state } } export default entities
O redutor atualiza uma parte do estado Redux. Depois que a alteração é feita, os componentes React que assinam esse bit de estado são atualizados automaticamente.

No React, é possível compor componentes para criar a interface de usuário, o que significa que os componentes pai podem ser compostos de componentes filho aninhados. Ao desenvolver um aplicativo, divida a funcionalidade em componentes simples e combine-os para formar componentes mais complexos.
/* Excerpt from /client-src/components/RecordSection.js */ // Component that displays a Record section. const RecordSection = ({section, error, editValues, picklists, onFieldValueUpdate, allowEdit, index, objectInfo, onEditDepGraph, uiMode, recordView}) => { return ( <tbody> { section.useHeading && <tr> <td colSpan="4" key={'sectionHeading' + index} className="slds-text-heading--small slds-p-left--medium slds-p-top--small slds-p-bottom--medium"> {section.heading} </td> </tr> } {section.rows.map((row, i) => <RecordRow key={'sectionRow' + index + ',' + i} allowEdit={allowEdit} uiMode={uiMode} picklists={picklists} onFieldValueUpdate={onFieldValueUpdate} error={error} editValues={editValues} row={row} sectionIndex={index} rowIndex={i} objectInfo={objectInfo} recordView={recordView} onEditDepGraph={onEditDepGraph}/> )} </tbody> ); }
Por fim, mas certamente não menos importante, a pasta auxiliares contém algumas funções auxiliares de JavaScript. Essas funções transformam as respostas da API de interface de usuário em modelos de dados que alimentam a interface de usuário do Visualizador de registros. O código recordLayout.js cria o modelo de dados interno para layouts, enquanto o código depGraphHelper.js cria o editor de lista de opções dependentes. Veremos esses arquivos em mais detalhe depois.