ALV OO – Trabalhando com botões e eventos
Sem dúvida, uma das atividades mais realizadas por um programador ABAP é a emissão de relatórios.
Como não estamos na era dos dinossauros, “não conhecemos” o relatório “write” e vamos direto aos “ALV’s”.
Este post tem como objetivo exemplificar algumas funcionalidades de um ALV OO (Orientado a Objetos), são elas:
- Exclusão de botões do toolbar de um ALV OO
- Inclusão de botões no toolbar do ALV
- Utilização de eventos
- Como desabilitar um botão (ele aparece, mas não é possível clicar)
- Geração de um ALV sem necessidade de criação de um container
Veja também:
- Como encontrar ícones
- Utilização de interfaces
- A modelagem utilizada, foi construída de forma a permitir a utilização dos pontos acima citados.
Especificação
Na tela de seleção, o usuário poderá determinar uma ou mais cias aéreas. Deve ser gerado um relatório ALV com os dados desta(s) cia(s), a serem extraídos da tabela SCARR.
Neste relatório, ao selecionar uma determinada cia, deve haver um botão que gere um segundo ALV, desta vez exibindo as informações dos vôos (conexões) referentes à seleção realizada (dados a serem extraídos da tabela SPFLI).
E então, vamos ao que interessa!?
No programa principal, devemos criar a tela de seleção e um include, onde codificaremos nossas classes.
Tela de seleção:
Declara-se o select-options para possibilitar a determinação do range das cias aéreas:
include: z_classes.
tables: scarr,
spfli.
data: wa_scarr type scarr.
data: r_companies type ref to lcl_companies.
selection-screen begin of block filter with frame title text-001.
select-options s_carrid for wa_scarr-carrid.
selection-screen end of block filter.
Vamos criar também, duas telas (as quais nomeei de “0100” e “0200”, que serão utilizadas na exibição dos ALV’s.
No include criado, segue declaração da interface:
interface: zif_data_reader.
types: ty_scarr type range of scarr-carrid.
methods: read_data
importing
im_scarr type ty_scarr,
generate_alv
importing
im_structure type char30.
endinterface. "zif_data_reader
Observe que temos os métodos read_data e generate_alv, que foram declarados na interface, porque serão utilizados em ambos os relatórios.
Agora vamos à definição da classe lcl_companies, que será responsável pela leitura dos dados das cias aéreas.
*----------------------------------------------------------------------*
* CLASS lcl_companies DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_companies definition.
public section.
interfaces zif_data_reader.
methods: constructor,
set_toolbar
for event toolbar of cl_gui_alv_grid
importing e_object,
get_ucomm
for event user_command of cl_gui_alv_grid
importing e_ucomm,
refresh_table
for event after_user_command
of cl_gui_alv_grid.
private section.
data: r_grid type ref to cl_gui_alv_grid,
r_connections type ref to lcl_connections.
data: gt_scarr type table of scarr,
gt_exc_button type ui_functions.
endclass. "lcl_companies DEFINITION
Observe que a classe tem como atributos privados:
- Objeto r_grid (para criação do grid do alv)
- Objeto r_connections para referência e criação dos objetos referentes aos vôos (conexões)
- Tabela gt_scarr, contendo as informações da(s) cia(s) aérea;
- Tabela de botões a serem excluídos
Através da keyword “INTERFACES”, declaramos a utilização da interface zif_data_reader, com isso, na classe lcl_companies devemos implementar todos os métodos contemplados na interface.
Seguem Implementações:
- Constructor: será responsável pela criação do grid do alv, registrar os eventos a serem utilizados e determinar os botões a serem excluídos.
method: constructor.
create object r_grid
exporting
i_parent = cl_gui_container=>screen0
exceptions
error_cntl_create = 1
error_cntl_init = 2
error_cntl_link = 3
error_dp_create = 4
others = 5.
if sy-subrc ne 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
endif.
*--------------------------------------------------------------------*
* Registra os eventos a serem utilizados
*--------------------------------------------------------------------*
set handler get_ucomm for r_grid.
set handler set_toolbar for r_grid.
set handler refresh_table for r_grid.
*--------------------------------------------------------------------*
* Informa os botões a serem excluídos
*--------------------------------------------------------------------*
append cl_gui_alv_grid=>mc_fc_graph to gt_exc_button.
endmethod. "constructor
Nota: quando informamos “cl_gui_container=>screen0” no parâmetro “i_parent” não temos a necessidade de declarar um container (recomenda-se gerar o ALV sem container apenas em casos de real necessidade, pois esteticamente o programa será prejudicado, visto que o ALV ocupará a tela inteira).
Para entender melhor, vá até a transação SE24, na classe CL_GUI_ALV_GRID.
Na aba “métodos” você encontrará o “constructor”, clique em “Parâmetros” e verá que o parâmetro “i_parent” é do tipo “CL_GUI_CONTAINER”.
Clicando em CL_GUI_CONTAINER, na aba atributos, você encontrará “Screen0”.
Ou seja, o atributo “Screen0” utiliza a tela como nosso container.
Entendendo a exclusão de botões
Novamente na transação SE24, consulte o método “SET_TABLE_FOR_FIRST_DISPLAY”.
Ao clicar em “parâmetros” você encontrará o “IT_TOOLBAR_EXCLUDING”, de tipo “UI_FUNCTIONS”.
Vamos agora para a aba de atributos da classe, onde encontraremos os botões.
Desta forma, basta inserirmos os botões a serem excluídos na tabela declarada, e informá-la no momento em que chamarmos o método “SET_TABLE_FOR_FIRST_DISPLAY” (neste exercício, excluí apenas o botão utilizado para geração de gráficos).
O próximo passo é selecionarmos os dados da tabela SCARR conforme dados da tela de seleção.
- zif_data_reader~read_data
Observe que quando implementamos um método de uma interface, este é denominado: nome_da_interface~nome_ do_ método.
method: zif_data_reader~read_data.
clear gt_scarr.
refresh gt_scarr.
select *
from scarr
into table gt_scarr
where carrid in im_scarr[].
if sy-subrc ne 0.
message: text-002 type 'E'.
else.
call method me->zif_data_reader~generate_alv
exporting
im_structure = 'SCARR'.
endif.
endmethod. "zif_data_reader~read_data
Neste método, selecionamos os dados conforme critérios informados na tela de seleção.
Quando sy-subrc não é “0” (valores não encontrados), exibimos uma mensagem de erro, caso contrário, chamamos o método “zif_data_reader~generate_alv”, vide implementação abaixo:
method: zif_data_reader~generate_alv.
call method r_grid->set_table_for_first_display
exporting
i_structure_name = im_structure
it_toolbar_excluding = gt_exc_button
changing
it_outtab = gt_scarr
exceptions
invalid_parameter_combination = 1
program_error = 2
too_many_lines = 3
others = 4.
if sy-subrc NE 0.
message id sy-msgid type sy-msgty number sy-msgno
with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
endif.
call screen 100.
endmethod. "zif_data_reader~generate_alv
Com os dados selecionados e exibidos na tela, precisamos incluir o botão para visualizarmos as conexões das cias aéreas.
- set_toolbar – será chamado cada vez que o evento “TOOLBAR” (registrado no constructor!) for disparado pela classe CL_GUI_ALV_GRID.
Consultando os parâmetros deste evento, na transação SE24, podemos verificar que ele exporta o parâmetro “E_OBJECT”, de tipo “CL_ALV_EVENT_TOOLBAR_SET”.
Nos atributos desta classe, encontraremos o “MT_TOOLBAR”, de tipo “TTB_BUTTON”, finalmente tipo “STB_BUTTON”, onde estão as informações que precisamos.
Veja a implementação do método “set_toolbar”:
method: set_toolbar.
data: wa_toolbar type stb_button.
case sy-dynnr.
when '1000'.
clear wa_toolbar.
wa_toolbar-butn_type = 3. "Separador
append wa_toolbar to e_object->mt_toolbar.
clear wa_toolbar.
wa_toolbar-function = '100_CONN'.
wa_toolbar-icon = icon_object_folder.
wa_toolbar-quickinfo = 'Visualiza Conexões'.
wa_toolbar-butn_type = 0. "Botão Normal
wa_toolbar-text = 'Visualizar Conexões'.
append wa_toolbar to e_object->mt_toolbar.
when '0100'.
clear wa_toolbar.
wa_toolbar-butn_type = 3. "Separador
append wa_toolbar to e_object->mt_toolbar.
clear wa_toolbar.
wa_toolbar-function = '100_CONN'.
wa_toolbar-icon = icon_object_folder.
wa_toolbar-quickinfo = 'Visualiza Conexões'.
wa_toolbar-butn_type = 0. "Botão normal
wa_toolbar-text = 'Visualizar Conexões'.
if sy-ucomm = 'BACK'.
wa_toolbar-disabled = ' '.
else.
wa_toolbar-disabled = 'X'.
endif.
append wa_toolbar to e_object->mt_toolbar.
when others.
endcase.
endmethod. "set_toolbar
Observe que temos duas tratativa diferentes para o botão: quando estamos na tela 1000 (chamando a tela 0100, que contém os dados das cias aéreas), quando deve estar habilitado, e quando estamos na tela 0100 (chamando a tela 0200, que conterá os dados das conexões), quando o botão deve estar desabilitado.
Para melhor compreensão, faça este exercício, coloque um BREAK-POINT no método e debugue! Assim você entenderá os momentos em que ele é chamado e sua tratativa!
Dica: lembre-se de utilizar text-symbols. Na implementação acima, escrevi as informações diretamente no código para facilitar o entendimento!
Nota: informamos o ícone: icon_object_folder, este nome é encontrado na tabela “ICON”, porém, algumas vezes precisamos do parâmetro “internal” quando trabalhamos com ícones. Este pode ser encontrado no programa “SHOWICON”.
- get_ucomm – é através deste que nosso botão funcionará:
method: get_ucomm.
data: lv_row type lvc_s_row,
wa_scarr like line of gt_scarr.
data: r_cias type range of scarr-carrid,
wa_cias like line of r_cias.
if e_ucomm eq '100_CONN'.
call method r_grid->get_current_cell
importing
es_row_id = lv_row.
*--------------------------------------------------------------------*
if lv_row is initial.
message: text-003 type 'I'.
*--------------------------------------------------------------------*
else.
read table gt_scarr into wa_scarr index lv_row-index.
clear wa_cias.
wa_cias-sign = 'I'.
wa_cias-option = 'EQ'.
wa_cias-low = wa_scarr-carrid.
* wa_cias-high = ' ' .
append wa_cias to r_cias.
create object r_connections
exporting
im_grid = r_grid.
call method r_connections->zif_data_reader~read_data
exporting
im_scarr = r_cias.
*--------------------------------------------------------------------*
endif.
endif.
endmethod. "get_ucomm
Lembrando que este método será chamado cada vez que o evento “user_command” for disparado (conforme sua declaração), devemos verificar se o “sy-ucomm” é “100_CONN”, ou seja, se nosso botão foi “clicado”.
Com isso, chamamos o método get_current_cel para verificarmos qual foi a linha selecionada.
Verificamos em nossa tabela qual é cia aérea referente a linha selecionada, para então criarmos o objeto referente à classe das conexões e ler seus dados.
Observe que utilizamos o range r_cias para informar a cia aérea. O range é como se fosse o select-options, por isso precisamos informar:
- sign = “I” (Include)
- Option = “EQ” (Equal, porque informaremos uma única cia aérea.
- Low = wa_scarr-carrid (cia aérea da linha selecionada).
- High = Não precisamos informar, pois utilizamos “option = EQ”.
Para a classe lcl_companies, ainda precisamos implementar o método:
- refresh_table:
method refresh_table.
check sy-dynnr = 100 and
sy-ucomm = 'BACK'.
call method me->zif_data_reader~generate_alv
exporting
im_structure = 'SCARR'.
endmethod. "refresh_table
Este método será chamado após o user_command.
É necessário para atualizar as informações do grid quando voltamos da tela 200 para a tela 100. Sem ele, os dados das conexões permanecem na tela.
Para melhor compreensão, debugue!
Finalmente, vamos à declaração da classe “lcl_connections”.
*----------------------------------------------------------------------*
* CLASS lcl_connections DEFINITION
*----------------------------------------------------------------------*
*
*----------------------------------------------------------------------*
class lcl_connections definition.
public section.
interfaces: zif_data_reader.
methods: constructor
importing
im_grid type ref to cl_gui_alv_grid.
private section.
data: gt_spfli type standard table of spfli,
gt_exc_button type ui_functions,
gv_carrid type scarr-carrid.
data: r_grid_conn type ref to cl_gui_alv_grid.
endclass. "lcl_connections DEFINITION
Como podem ver, esta é uma classe bem simples. Verifiquem sua implementação:
class lcl_connections implementation.
method: constructor.
r_grid_conn = im_grid.
endmethod. "constructor
method: zif_data_reader~read_data.
clear gt_spfli.
refresh gt_spfli.
select *
from spfli
into table gt_spfli
where carrid in im_scarr.
if sy-subrc = 0.
call method me->zif_data_reader~generate_alv
exporting
im_structure = 'SPFLI'.
else.
message: text-004 type 'I'.
endif.
endmethod. "zif_data_reader~read_data
method: zif_data_reader~generate_alv.
data: wa_exclude like line of gt_exc_button.
call method r_grid_conn->set_table_for_first_display
exporting
i_structure_name = im_structure
changing
it_outtab = gt_spfli
exceptions
invalid_parameter_combination = 1
program_error = 2
too_many_lines = 3
others = 4.
call screen 200.
endmethod. "zif_data_reader~generate_alv
endclass.
Observe que o constructor é responsável apenas por “importar” o grid gerado na classe lcl_companies.
Vejam abaixo o resultado do nosso exercício:
Tela 0100, com os as informações das cias aéreas e botão habilitado.
Observe que não há o botão responsável pela geração de gráficos.
Tela 0200, com informações das conexões e botão desabilitado.
E então, gostaram? baixe o código (Z_ALV_OO ), estude e nos envie suas dúvidas e sugestões!
Veja neste post como utilizar o arquivo.
Olá, baixei o código mas qnd uso o SAP LINK para importar, não vem o Z_CLASSES. Poderia conferir? Obrigado
Olá Edson!
O Arquivo foi alterado.
Bons estudos!
Oi Claudia. Gostei do seu exercício. Muito bem elaborado e bem explicado.
Eu tiraria a declaração do Tables… não faz sentido.
Outra coisa: cuidado com a informação de que report com Write é “dinossáurico”… Ok, é ultrapassado, mas muita coisa em um sistema SAP ainda depende dele. Sem esse tipo de report, como gerar Livros Fiscais?
Abraço
Vagnão
Olá Vagnão!
Obrigada pelos elogios e pelas dicas. Realmente “bobeei” declarando o “tables”. Eu deveria ter criado uma estrutura e declarado o Select-Options para esta estrutura, de forma a não precisar utilizar o “tables”.
Quanto aos relatórios write, trabalhei com uma transação que imprimia relatórios de balancete. Meu Deus….quanto trabalho! :/
Obrigada,
Claudia.
Alguém ainda vai me dizer… livros fiscais? SPED?
ok!
Parabéns, Excelênte Post.
Obrigada Mauro!
Parabéns, tenho certeza que ainda vou procurar esse post várias vezes para consulta!
Obrigada Miguel!
Espero que seja realmente útil!
Bons estudos!
Estou implementando um ALV orientado a objeto. Gostaria de capturar o evento de um clique na linha, porém não consigo capturar esse evento ( já tentei pelo LEFT_CLICK_DESIGN, LEFT_CLICK_RUN) só que não dispara. Desde já obrigado
Bom dia Humberto!
Para registrar um único click em uma célula de um resultado do ALV, geralmente utilizamoso hotspot (no field cat e o evento “HOTSPOT_CLICK”), ou então, utilizamos o duplo clique na linha ou célula do ALV, através do evento “DOUBLE_CLICK”.
Dê uma olhada nestes eventos, e qualquer dúvida, entre em contato novamente!
Boa sorte!
Claudia.