I am creating a SALV grid in a simplistic manner avoiding screens and containers:
TRY.
cl_salv_table=>factory(
IMPORTING
r_salv_table = lo_alv
CHANGING
t_table = itab ).
CATCH cx_salv_msg INTO exc.
ENDTRY.
...
lo_alv->set_screen_status( pfstatus = 'SALV_STATUS'
report = sy-repid
set_functions = lo_alv->c_functions_all ).
...
lo_alv->display( ).
I want to show the standard MESSAGE only once upon grid display, making it showed in the bottom status bar.
I tried PAGE events, but they are not fired on start, only after pressing standard toolbar button
SET HANDLER cl_event_handler=>on_top_of_page FOR lo_events.
SET HANDLER cl_event_handler=>on_end_of_page FOR lo_events.
I also tried putting MESSAGE after the display( ) method, but this way it is showed after I exit the grid with back button, and this is not what I want, I want it showed exactly under the grid at the moment of first load.
Is there an event that is triggered on grid initialization or after rendering/loading data?
If not, is there any workaround to make this work ?
Put the message before calling lo_alv->display( ). This complete program seems to behave the way you want. The status message "Hello World" appears below the ALV grid with the data:
REPORT Z_TEST_SALV_MESSAGE.
SELECT * FROM sflight INTO TABLE #DATA(gt_sflight).
cl_salv_table=>factory(
IMPORTING
r_salv_table = DATA(lo_alv)
CHANGING
t_table = gt_sflight ).
MESSAGE 'Hello World' TYPE 'S'.
lo_alv->display( ).
Related
I have an ALV grid with an editable field, if I check the data entered and display errors, the ALV updates, if I try to change the input data, other times the ALV does not update anymore.
The code in the PAI is:
ls_layout-cwidth_opt = abap_true.
CREATE OBJECT go_alv
EXPORTING
i_parent = cl_gui_custom_container=>screen0
EXCEPTIONS
error_cntl_create = 1
error_cntl_init = 2
error_cntl_link = 3
error_dp_create = 4
OTHERS = 5.
IF sy-subrc EQ 0.
* Adapting field catalog
CALL FUNCTION 'LVC_FIELDCATALOG_MERGE'
EXPORTING
i_structure_name = 'ZAMOUNT'
CHANGING
ct_fieldcat = lt_fieldcat.
IF sy-subrc EQ 0.
* Field catalog specifics
LOOP AT lt_fieldcat ASSIGNING <ls_fieldcat>.
IF <ls_fieldcat>-fieldname = 'DMBTR'.
<ls_fieldcat>-edit = abap_true.
ENDIF.
ENDLOOP.
ENDIF.
* Show data usig ALV class
go_alv->set_table_for_first_display(
EXPORTING
is_layout = ls_layout
CHANGING
it_outtab = gt_out
it_fieldcatalog = lt_fieldcat ).
go_alv->set_ready_for_input( EXPORTING
i_ready_for_input = 1 ).
CALL METHOD go_alv->register_edit_event
EXPORTING
i_event_id = cl_gui_alv_grid=>mc_evt_enter.
CALL METHOD go_alv->register_edit_event
EXPORTING
i_event_id = cl_gui_alv_grid=>mc_evt_modified.
ENDIF.
The code in the PBO is:
IF go_alv IS NOT INITIAL.
CALL METHOD go_alv->check_changed_data( ).
PERFORM check_amounts TABLES gt_out
CHANGING gv_sum_amounts
gv_tot_amount.
CALL METHOD go_alv->refresh_table_display
EXPORTING
is_stable = VALUE #( row = abap_true
col = abap_true )
i_soft_refresh = 'X'.
cl_gui_cfw=>flush( ).
ENDIF.
In the perform check_amounts I populate a field of the ALV with the errors, if the user modifies the input field and the error is removed in the internal table but it is not shown in the ALV.
I also tried to implement the data_change_finished method by calling the refresh inside but I have not solved the anomaly.
Can you help me?
Thanks
I'm not quite sure if I understand your problem but here are some potential issues I see:
You have your validity checks split up. On the one hand there is the data_changed event. On the other hand you have the check_amounts perform.
If the data_changed event finds an input error, it'll show you the red outline on your grid. In your internal table ("gt_out") this field will stay the same as before (it'll not update the table with the invalid input !).
This might be why you think there should be a message in your grid but there isn't.
Did you define your own local version for the data_changed event? Than the handlers are missing. When you tried doing the refresh in the data_changed_finished event you definitly need the handler for that.
Also you said that you only put the refresh in the method for data_changed_finished. You need to put the check_amount perform there too then. Otherwise you called the refresh to early.
In the method for data_changed_finished you have the automatically updated table ("gt_out") from the data_changed method. You should be able to update it here and send it to your grid with a refresh.
I don't like that you call the check_changed_data method in your PBO. Because of the register_edit_event calls the data_changed event already triggers when you hit enter or jump to the next field. You usually only call this method when you received a seperat user command, that can be fired before the event can be triggered to be sure that you have correct data.
Not 100% sure about this but for your register_edit_event calls, the event id cl_gui_alv_grid=>mc_evt_modified should include the event id cl_gui_alv_grid=>mc_evt_enter already.
I'd try to define a local class to handle the data_changed and data_changed_finished events and put the check_amount and refresh in the method for the data_changed_finished event.
Maybe some of this helps you? If you have any questions about any of this, let me know and I can go into more detail.
regards
in SE16N in Technical Settings there is a Checkbox the OUTPUT WITHOUT CONVERSION EXIT.
In my programs I use the class CL_SALV_TABLE for creating the ALVs. Is it possible to put a checkbox in the program similar to SE16N and when the user enable it my ALV will display the data like the SE16N?
Thanks
Elias
This is what I wanted as I am using SALV extensively.
** Display the data Without Conversion
DATA: l_tabledescr_ref TYPE REF TO cl_abap_tabledescr,
l_descr_ref TYPE REF TO cl_abap_structdescr,
wa_table TYPE abap_compdescr.
IF p_woconv = 'X'.
TRY.
columns = oref_table->get_columns( ).
l_tabledescr_ref ?= cl_abap_typedescr=>describe_by_data( <fs_itab> ).
l_descr_ref ?= l_tabledescr_ref->get_table_line_type( ).
LOOP AT l_descr_ref->components INTO wa_table.
DATA(edit_mask) = columns->get_column( wa_table-name )->get_edit_mask( ).
IF edit_mask(2) = '=='.
columns->get_column( wa_table-name )->set_edit_mask( ' ' ).
ENDIF.
IF wa_table-type_kind = 'C' OR wa_table-type_kind = 'N'.
columns->get_column( wa_table-name )->set_leading_zero( ).
ENDIF.
ENDLOOP.
CATCH cx_salv_not_found.
ENDTRY.
ENDIF.
If someone has a better solution with SALV, I will be much obliged to share with us.
Thanks all for your answers.
Elias
Everything is possible in SAP, However I'm not sure if your request is possible using the class CL_SALV_TABLE. I recommend you to use the Class CL_GUI_ALV_GRID which is really dynamic and can be implemented to fit your scenario.
There is a lot of tutorials online but I'll try to make small summary
Create a Parent Container CL_GUI_CUSTOMCONTAINER
Create the ALV Grid and set the Parent.
Fetch Field catalogs (can also be dynamic to fit any table)
Create the Output table and pass the operations
Display ALV
Sure the Displayed table can be set according to the user's choice (Checkbox)
Please write down if you need more help
I am trying to display updated records in ALV but old records are being displayd.
Here is the code written in the screen exit of work order.
TRY.
cl_salv_table=>factory(
EXPORTING
r_container = lo_cust_container
IMPORTING
r_salv_table = lo_alv_table
CHANGING
t_table = gt_wflog ).
**// Functions
DATA(lo_alv_functions) = lo_alv_table->get_functions( ).
lo_alv_functions->set_all( abap_true ).
**// Display Settings
DATA(lo_alv_display) = lo_alv_table->get_display_settings( ).
lo_alv_display->set_striped_pattern( abap_true ).
**// Layout Settings
DATA: ls_layout_key TYPE salv_s_layout_key.
DATA(lo_alv_layout) = lo_alv_table->get_layout( ).
ls_layout_key-report = sy-repid.
lo_alv_layout->set_key( ls_layout_key ).
lo_alv_layout->set_save_restriction( cl_salv_layout=>restrict_user_independant ).
lo_alv_columns->set_optimize( abap_true ).
lo_alv_table->set_data( CHANGING t_table = gt_wflog[] ).
lo_alv_table->display( ).
CATCH cx_salv_msg cx_salv_error INTO DATA(lx_salv_msg).
MESSAGE lx_salv_msg->get_text( ) TYPE 'I'.
ENDTRY.
I tried to used method refresh lo_alv_table->resfresh( ). with option soft or full refresh but nothing happened. First time call data is ok when subscreen is called again and there is change in data then updated records are not displayed. I can see updated records in the table during debug.
More than likely you have CX_SALV_NO_NEW_DATA_ALLOWED exception which is caught by TRY clause during the second call of your instantiation. That's why display() method is not executed.
There is a note in SET_DATA method documentation:
You are not able to call these methods in an event handler. If you
do you will get an error.
...
Exceptions
CX_SALV_NO_NEW_DATA_ALLOWED
You have called SET_DATA in an event handler.
In your context screen exit is the same as event handler as it is called by the same event.
Solution is confirmed by OP: "It works perfectly"
Added to declarations in the top include.
DATA go_alv_table TYPE REF TO cl_salv_table.
Added in the code
IF go_alv_table IS NOT BOUND.
cl_salv_table=>factory( )
...
ENDIF.
Added after set_data method call
go_alv_table->refresh( refresh_mode = if_salv_c_refresh=>soft ).
That's a well-known issue with controls. If you instantiate any GUI control (in your case, it's the ALV grid) inside a container in which there was already a control which has not been freed up (in your case, the ALV grid first instantiated using cl_salv_table=>factory), then the old control still shows up, the new one is not shown.
Two solutions :
Either you keep instantiating the control, but then you must free the previous control. For this, you must call control->FREE( ) followed by the statement FREE control. This method is available for all controls (even the container itself can be freed, all its inner controls are then freed up).
Or you change the logic by instantiating the control only once, and you refresh its contents.
Special case: some controls may be wrapped by some wrapper classes which don't give access to the control (SALV classes for instance), so the easy way is to free the container to which the control is attached.
Just an addition to #suncatcher's answer.
Firstly checks whether a reference variable contains a valid reference: 'IF go_alv_grid IS BOUND'.
Example:
SELECT * FROM zemployees BYPASSING BUFFER INTO TABLE it_zemployees.
IF go_alv_grid IS BOUND.
go_alv_grid->refresh( ).
ELSE.
cl_salv_table=>factory(
EXPORTING
r_container = NEW cl_gui_custom_container( 'CONTAINER_NAME' )
container_name = 'CONTAINER_NAME'
IMPORTING
r_salv_table = go_alv_grid
CHANGING
t_table = it_zemployees
).
"Style the table
go_alv_grid->get_functions( )->set_all( ).
go_alv_grid->get_columns( )->set_optimize( ).
go_alv_grid->get_display_settings( )->set_striped_pattern( abap_true ).
go_alv_grid->display( ).
ENDIF.
I have a program which displays a splitter-container with two columns:
Left side displays a control tree (this is OK)
Right side should displays a custom TCode (not sure how to do this bit)
On double-click of a tree node, it calls a transaction which opens a screen, but it opens in a new window.
How to open the screen in the right-side window?
Minimal reproducible example:
Create the transaction code ZCALL_ZPRG1 via SE93, so that it calls the program ZPRG1
Create the program ZPRG1 (minimalist screen):
REPORT zprg1.
PARAMETERS test AS CHECKBOX.
Create the program ZPRG2 (splitter and tree):
REPORT zprg2.
CLASS lcl_app DEFINITION.
PUBLIC SECTION.
METHODS:
pbo,
on_node_double_click FOR EVENT node_double_click OF cl_gui_simple_tree.
DATA:
tree TYPE REF TO cl_gui_simple_tree,
itab TYPE TABLE OF mtreesnode,
splitter TYPE REF TO cl_gui_splitter_container.
ENDCLASS.
CLASS lcl_app IMPLEMENTATION.
METHOD pbo.
CHECK tree IS NOT BOUND.
CREATE OBJECT splitter
EXPORTING
parent = cl_gui_container=>screen0
rows = 1
columns = 2.
itab = VALUE #(
( node_key = '1'
text = 'Double-click me' ) ).
CREATE OBJECT tree
EXPORTING
parent = splitter->get_container( row = 1 column = 1 )
node_selection_mode = cl_gui_simple_tree=>node_sel_mode_single.
tree->add_nodes(
table_structure_name = 'MTREESNODE'
node_table = itab ).
SET HANDLER on_node_double_click FOR tree.
DATA events TYPE cntl_simple_events.
CALL METHOD tree->set_registered_events
EXPORTING
events = VALUE #( ( eventid = tree->eventid_node_double_click ) )
EXCEPTIONS
OTHERS = 4.
ENDMETHOD.
METHOD on_node_double_click.
CALL TRANSACTION 'ZCALL_ZPRG1'.
ENDMETHOD.
ENDCLASS.
DATA: app TYPE REF TO lcl_app.
PARAMETERS dummy.
INITIALIZATION.
CREATE OBJECT app.
AT SELECTION-SCREEN OUTPUT.
app->pbo( ).
AT SELECTION-SCREEN ON EXIT-COMMAND.
app->splitter->free( ).
Run ZPRG2
Double-click the tree node
In the SAP GUI, it is possible to mix screens of dynpro technology and GUI controls of the SAP Control Framework in these ways:
Screen and Docking Containers ; these containers may be displayed at any of the 4 sides of the screen
In these containers can be placed GUI controls (including splitter containers)
Screen containing a so-called Custom Container area in which can be placed GUI controls (including splitter containers)
Popups can be displayed over any dynpro screen or GUI control; those popups may also contain any (other) dynpro screen or GUI control; they are completely independent from the screen below. Popups may also be displayed over popups.
It is not possible to embed a dynpro screen inside a GUI control (including containers) in any manner.
So, in your case, the only solution is to use a docking container on the left of your dynpro screen. But only the program which displays the dynpro screen is allowed to "add" the docking container (i.e. CALL TRANSACTION or SUBMIT cannot be used).
Consequently, ZPRG2 must handle both your GUI control and a dynpro screen. You have two options to handle the dynpro screen:
Either ZPRG2 handles it (everything is done by ZPRG2, you don't need ZPRG1)
Or you only define a subscreen area inside the dynpro screen of ZPRG2, and you include a subscreen from ZPRG1 which handles all the initializations and interactions of the subscreen.
The second solution is recommended because it permits the Separation Of Concerns".
The code below is a minimal example to demonstrate how it could work, that will allow you to debug and understand better; there are so many things to explain that I prefer to let you ask questions in the comments (or new questions); be aware that many of the algorithm, statements and tricks below are not advised, I use them just because they are much shorter. You will end up with a much more complex program than this one.
Program ZPRG1 (right side, show/hide UI elements to react to double click at left side):
REPORT zprg1.
DATA display TYPE abap_bool VALUE abap_false.
SELECTION-SCREEN BEGIN OF SCREEN 0100 AS SUBSCREEN.
PARAMETERS test AS CHECKBOX.
SELECTION-SCREEN END OF SCREEN 0100.
AT SELECTION-SCREEN OUTPUT.
IF display = abap_false.
LOOP AT SCREEN.
screen-active = '0'.
MODIFY SCREEN.
ENDLOOP.
ENDIF.
FORM toggle_display.
IF display = abap_false.
display = abap_true.
ELSE.
display = abap_false.
ENDIF.
ENDFORM.
Program ZPRG2 (left side tree + handling of subscreen area at the right to include ZPRG1 screen):
REPORT zprg2.
DATA okcode TYPE syucomm. " global variable for dynpro 0200 "OKCODE" element
CLASS lcl_app DEFINITION.
PUBLIC SECTION.
METHODS:
pbo,
pai,
on_node_double_click FOR EVENT node_double_click OF cl_gui_simple_tree.
DATA:
tree TYPE REF TO cl_gui_simple_tree,
itab TYPE TABLE OF mtreesnode,
docking TYPE REF TO cl_gui_docking_container.
ENDCLASS.
CLASS lcl_app IMPLEMENTATION.
METHOD pbo.
SET PF-STATUS space. " <=== trick to activate buttons in the system toolbar
CHECK tree IS NOT BOUND.
CREATE OBJECT docking
EXPORTING
repid = sy-repid
dynnr = sy-dynnr
side = docking->dock_at_left
extension = 400 " pixels
EXCEPTIONS
OTHERS = 6.
itab = VALUE #(
( node_key = '1'
text = 'Double-click me' ) ).
CREATE OBJECT tree
EXPORTING
parent = docking
node_selection_mode = cl_gui_simple_tree=>node_sel_mode_single.
tree->add_nodes(
table_structure_name = 'MTREESNODE'
node_table = itab ).
SET HANDLER on_node_double_click FOR tree.
DATA events TYPE cntl_simple_events.
CALL METHOD tree->set_registered_events
EXPORTING
events = VALUE #( ( eventid = tree->eventid_node_double_click
appl_event = 'X' ) ) " <=== to trigger the PAI
EXCEPTIONS
OTHERS = 4.
ENDMETHOD.
METHOD on_node_double_click.
PERFORM toggle_display IN PROGRAM zprg1.
ENDMETHOD.
METHOD pai.
CASE okcode.
WHEN 'BACK' OR 'RW' OR '%EX'. " one of the exit buttons in system toolbar
docking->free( ). " free container and inner controls
SET SCREEN 0. " to return after the current CALL SCREEN
ENDCASE.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA: app TYPE REF TO lcl_app.
CREATE OBJECT app.
CALL SCREEN 200.
MODULE status_0200 OUTPUT.
app->pbo( ).
ENDMODULE.
MODULE user_command_0200 INPUT.
app->pai( ).
ENDMODULE.
Dynpro 0200 of program ZPRG2:
The layout contains a huge subscreen area named SUBAREA, and the OKCODE element is to be assigned the name OKCODE (which is mapped to the global variable of ZPRG2 having the same name)
The flow logic contains this code:
PROCESS BEFORE OUTPUT.
MODULE status_0200.
CALL SUBSCREEN subarea INCLUDING 'ZPRG1' '0100'.
PROCESS AFTER INPUT.
CALL SUBSCREEN subarea.
MODULE user_command_0200.
I have a webdynpro containing a table displaying numerous lines. After the user clicks the delete button I want to delete the selected line of the table.
My problem right now is, that I don't know how to implement this in the event-call.
How can I identify the selected line of the table?
If by "table" you mean an editable ALV, there's a preset function for this. Take a look at http://help.sap.com/saphelp_nw04s/helpdata/EN/5f/ec57c72a1349c8bfdda56d976e9399/frameset.htm and http://help.sap.com/saphelp_nw04s/helpdata/EN/5f/ec57c72a1349c8bfdda56d976e9399/frameset.htm For details on how to process the selection manually, see http://help.sap.com/saphelp_nw04s/helpdata/EN/5f/ec57c72a1349c8bfdda56d976e9399/frameset.htm.
I finally got the solution:
In the button event implement the following, to access the node and finally the id-value:
method ONACTIONZSS10_15_ONDELETE .
DATA ls_cust type wd_this->element_IT_Cust.
DATA lo_nd_cust TYPE REF TO if_wd_context_node.
DATA lo_el_cust TYPE REF TO if_wd_context_element.
" Get the selected element
lo_nd_cust = wd_context->get_child_node( name = 'IT_CUST' ).
lo_el_cust = lo_nd_cust->get_element( ).
" Get the attributes of the node-element
lo_el_cust->get_static_attributes(
IMPORTING
static_attributes = ls_cust ).
" Call the delete-function
CALL FUNCTION 'ZSS10_15_CUST_FM_DELETE'
EXPORTING
custid = ls_cust-ID
.
endmethod.