cancel
Showing results for 
Search instead for 
Did you mean: 

MVC: Subcontroller and event handling

Former Member
0 Kudos

Hello together,

I have the following problem:

I have two controllers, "main" and "sub", both inherting from CL_BSP_CONTROLLER2.

"main" creates a view in ON_REQUEST and I can query events form the main view on DO_HANDLE_EVENT.

The main controller instantiates a subcontroller in it's ON_INIT method.

When including the subcontroller from within the main view using bsp:call, everything works fine - the subcontroller also has all required attribes as passed from main's DO_INIT.

The include looks like this:

<bsp:call url="./sub/sub.do" comp_id="sub" />

and gets rendered fine. FYI: previously I had the sam but with <bsp:parameter /> and w/o instantiation in main's ON_INIT.

However, the subview has a control (dropdown), but I am not able to query it in DO_HANDLE_EVENT:

- When handling in main controller, DO_HANDLE_EVENT is fired but cannot obtain a valid reference to the HTMLB control

- When handling in sub controller, DO_HANDLE_EVENT is not fired, although having dispatch_input( ) in sub's ON_REQUEST.

So I am in a dilemma: where the event is handled I get no reference to the HTMLB component (because being included in the sub view?), on the other side I cannot run DO_HANDLE_EVENT from the subcontroller.

Is a subcontroller's DO_HANDLE_EVENT possible to run at all? Would be nice in order to handle input controls of the according view. Should this not work like in a parent controller (except for navigation calls)?

FYI: checked enough in debugger to know that I do not have typos etc. when querying the control's values

Thanks in advance and regards,

Timo

P.S.: I seem to get a little bit closer: the BSP controller codes comes to a section where it checks if a controller is top-level and quits:


if is_toplevel( ) is initial.
   return.
endif.

How are subcontrollers told that they are subcontrollers? Do they derive from another class indirectly?

Accepted Solutions (1)

Accepted Solutions (1)

thomas_jung
Developer Advocate
Developer Advocate
0 Kudos

dispatch_input( ) shouldn't be called from the Sub Controller - Only the High Level Controller. If it is called from anywhere but the high level controller it will do nothing.

This call to displatch_input should make sure the DO_HANDLE_EVENT method of all sub controllers gets called properly.

Have you tried debugging into the DISPATCH_INPUT of your high level controller?

I would look at a couple of lines in there specifically:

* Call all registered controllers, even if no form_fields for them are requested
  loop at m_subcontrollers assigning <controller> where hide = ''.
*    CHECK <controller>-fields[] IS NOT INITIAL.
    <controller>-instance->fill_values(
                 form_fields = <controller>-fields
                 global_messages = component_messages ).
  endloop.

and

   read table m_subcontrollers index event_comp_index assigning <controller>.
      component_event = <controller>-instance->handle_event(
                           event = event_value
                           htmlb_event = request_event
                           htmlb_event_ex = event_data
                           global_messages = component_messages ).

Former Member
0 Kudos

Hello Thomas, hello Shiva,

Okay, in debugging I see that the subctontroller is successfully registered to the parent; it iterates over m_subcontrollers and gets the right one.

I found out that it even gets the correct component and value, but calls the wrong do_handle_method; as Thomas pointed out, it executes the local one, not <controller>-instance->handle_event(). It forks when testing the variable event_comp_index against. I have to see what the reason might be (it has a dedicated htmlb:form w/o further params).

Thank you so far, this was very enlightening to know how that stuff is internally processed.

THX and regards,

Timo

Former Member
0 Kudos

Hello,

Okay, I now found out that the real problem is when trying to get the controls value!

I Have tried this technique in order to get the value in DO_ANDLE_EVENT:


method DO_HANDLE_EVENT .

  data: view     type ref to If_BSP_Page,
        eventObj type ref to If_HTMLB_Data,
        dropDown type ref to Cl_HTMLB_DropDownListBox.


* Get the event object from request.
  eventObj = Cl_HTMLB_Manager=>get_event_ex( request ).

  if ( eventObj is not initial ).
*   Query the event ID (e.g. the onClick attrib of buttons)
    case eventObj->event_server_name.

[...]
      when 'periodChange'.
        dropDown ?= cl_htmlb_manager=>get_data(
          request = runtime->server->request
          name    = 'dropdownListBox'
          id      = 'periodSelect'
        ).
        if ( not dropDown->selection is initial ).
          data: periodId type i.
          move dropDown->selection to periodId.
          me->model->period_current_set( id_period = periodId ).
        endIf.

    endCase.
  endIf.

endmethod.

So, when getting the event from the request, everything is empty (selection). In debugger however, I see that this method gets passed all required data as event, htmlb_event and htmlb_event_ex, so I tried to get the values from these iinstances: nada!

The problem is that I have to do a typecast or so, because I see in the debugger, that there is a field called "selection" containing the correct value. In coding, I can only get the server_event flag because the other stuff is dynamically assigned at runtime. I tried to cast over to IF_HTMLB_EVENT and that but it could not be compiled.

So, can I use the passed event infos like this?:


method DO_HANDLE_EVENT .
data:  dropDown type ref to Cl_HTMLB_DropDownListBox.


* Get the event object from request.
  dropDown ?= htmlb_event_ex.

  if ( htmlb_event is not initial ).
*   Query the event ID (e.g. the onClick attrib of buttons)
    case htmlb_event->server_event.

      when 'periodChange'.
*        dropDown-selection.
*        if ( not htmlb_event->data->selection is initial ).
*          data: periodId type ZTM_Integer.
*          move dropDown->selection to periodId.
*          me->model->period_current_set( id_period = periodId ).
*        endIf.

    endCase.
  endIf.
endmethod.

Thanks,

Timo

thomas_jung
Developer Advocate
Developer Advocate
0 Kudos

In a DO_HANDLE_EVENT you never need to retrieve the IF_HTMLB_EVENT object (in fact it doesn't work as you found out). You already have the generic IF_HTMLB_EVENT in the form of htmlb_event_ex.

Yes you must type cast the event to the more specific object. I find that the best way to figure out what to cast into is to use attributes that are available in the IF_HTMLB_EVENT interface. Have a look at the following example:

if  htmlb_event_ex is not initial.
and htmlb_event_ex->event_name = htmlb_events=>DROPDOWNLISTBOX
and htmlb_event_ex->event_type = htmlb_events=>DROPDOWNLISTBOX_SELECT.
  dropDown ?= htmlb_event_ex.
  ...Event Coding
endif.

Take advantage of the constants in classes HTMLB_EVENTS, XHTMLB_EVENTS, and PHTMLB_EVENTS to determine which object type to cast into.

Former Member
0 Kudos

Hello Thomas,

Yes, it is working - thanks! But it would not be me if I had further problems I am quering a table view event also and it is pretty fine (e.g. getting the selected row index etc.). But having just a reference to a table event and not the table event, I at least have to get a reference to data (i.e. contents), e.g. the key of the current row. Using the index and comparing it to my known internal table is dangerous because the table may be filtered or sorted differently.

So, is there a way to get a reference from the event to the control itself sintead or additionally to the event; i.e. I must have these references:

- CL_HTMLB_EVENT_TABLEVIEW (okay, passed by htmlb_event)

- CL_HTMLB_TABLEVIEW (to query at least the prim key value)

Thank you!

Timo

thomas_jung
Developer Advocate
Developer Advocate
0 Kudos

You should have a look at SBSPEXT_TABLE. It answers many questions about events and TableViews.

If you want to get the CL_HTMLB_TABLEVIEW you can use the following:

data: tv          type ref to cl_htmlb_tableview.
    tv ?= cl_htmlb_manager=>get_data( request = request
                                      name    = 'tableView'
                                      id      = 'tvX' ).

Former Member
0 Kudos

Okay, figured out that I can use the event's methods like get_cell_value.

Problem is that this method retrieves values from the request. However, this table is read-only and has no fields containing cell values as <input /> or at all data having the id that the tableview and get_cell_value concatenate (like table_table_2_1).

Mh...

Thanks, Timo

Former Member
0 Kudos

Rehi,


data: tv          type ref to cl_htmlb_tableview.
    tv ?= cl_htmlb_manager=>get_data( request = request
                                      name    = 'tableView'
                                      id      = 'tvX' ).

is not working for me in subcontrollers, as we figured out at the beginning of this thread (I did not get any value form a dropdown box). Well, I should have acces by using the event:


          cellVal = tabEv->get_cell_value(
            row_index = tabEv->selectedRowIndex
            column_index = 1
          ).

But empty (see my post above that I submitted about the same time you wrote yours ).

Thanks and regards,

Timo

thomas_jung
Developer Advocate
Developer Advocate
0 Kudos

Ah, yes... and now to the actual reason why your get_data call didn't work in the subcontroller - you didn't specify the correct ID.

I didn't point that out earlier because using the event_ex is actually a better approach (and I got side tracked ).

However if you do need to call a GET_DATA or read a form field you have to know how SAP builds your element ids inside of sub-controllers.

SAP automatically prefixes any ids with the controller id. Keep in mind that the browser doesn't know anything about nested controllers/views. Therefore if you name an element the same id in the main controller and the subcontroller you would get inconsistent results. To protect you from this SAP takes the subcontroller ID and puts it on the element ids to assure that they will be unique. Therefore anything that interacts direct with the response object must compensate for this when specifying the ID. So in my example the id would't necessarily be tvX but subcontroller->id + tvX.

Former Member
0 Kudos

Rehi Thomas,

I tried again with the controller prefix, but the mecha is still the same: get_cell_value looksup for input fields - but there are none in the tableview being read-only.

Also, the other tech where I now use the event directly is also working 1:1 when debugging. The funny thing is that I used the same stuff in non-MVC BSP apps more than a year ago where it is working.

Do you know if there is no way that that get_cell_value queries against the internal table?

The get_cell_value does this:

request->get_form_field( name ).

- of course this will only work for editable tabs.

Thanks,

Timo

thomas_jung
Developer Advocate
Developer Advocate
0 Kudos

Not sure. I tend to use Model Binding for the internal table itself on the tableView. This matches up my data for my on input so I am not having to query cells from the input.

I tend use the selectedindex options for my input, but I'm not sure about the filtering/sorting effect on that. I tried to find an example, but when I use that the selection I dont' use the filtering/sorting. I would have to play with an example.

Former Member
0 Kudos

Hi Thomas,

I've made it! It is quite simple:

a) data binding helps me nothing because I do not have to edit data but to find out what row is selected.

b) I cannot map the selected index to the internal table because the table view is sortable and filterable

So, this is what I have to do:

a) add the keyColumn="COL_NAME" property in <htmlb:tableView />

b) in the event (CL_HTMLB_EVENT_TABLEVIEW), I simply can query the SELECTEDROWKEY attribute that now contains the value of my internal table prim. key (an employee number)

c) using that key, I can read my known internal table to get all row data

I found this tip here:

posted by Durairaj Athavan Raja.

- so, thanks to you both!!!

Thanks and regards,

Timo

(who hopes not to bother anylonger now that all basics are clear )

Answers (1)

Answers (1)

shiva_suvarna
Participant
0 Kudos

Hi timo

Check the M_subcontrollers variable in dispatch_input() method. if it is initial then the subcontroller was not assigned to the parent controller.

I think u need to specify the Application should be stateful. try by setting the stateful flag in Application properties