CL_GUI_ALV_GRID editable field not refresh - abap

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

Related

SALV event at grid initialization?

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( ).

Use output without conversion exit with SALV class

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

Updated records are not displayed in subscreen ALV

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.

How to trigger the ALV DATA_CHANGE event manually?

I have an instance of CL_GUI_ALV_GRID referenced by variable mo_alv_grid.
I have a button column in this grid, which after some logic, updates the table mt_alv_grid (backing mo_alv_grid).
I need to be able to trigger the event DATA_CHANGED at this point.
I have tried many methods of CL_GUI_ALV_GRID, like CHECK_DATA_CHANGED and REFRESH_TABLE_DISPLAY
and even CL_GUI_CFW=>FLUSH and CL_GUI_CFW=>SET_NEW_OK_CODE( 'ENTER' ). but none of this has worked.
Is there a way to trigger the DATA_CHANGED event, or should I be doing things completely differently ?
I don't know if this solves your problem, but in order to update the ALV internal table in the PAI, you could use the following method:
DATA lv_entries_are_consisted TYPE abap_bool.
mo_grid->check_changed_data(
IMPORTING
e_valid = lv_entries_are_consisted
).
well, it's possible.
1) don't change values in internal table by program
2) create a change protocol of type LVC_T_MODI with a new values for lines needed
then call
CALL METHOD lo_grid->change_data_from_inside
EXPORTING
it_style_cells = lt_cells.
where lo_grid is instance of cl_gui_alv_grid and lt_cells table type LVC_T_MODI. please note, that you will need to set field VAL_DATA of layout structure (LVC_S_LAYO) to 'X' when calling ALV grid for the first time to make this work.
after this, class will automatically change internal table for you and call DATA_CHANGE event

How to get the selected entry in WebDynpro ABAP table?

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.