What was the issue?

When using ALVs, in this case using CL_SALV_TABLE, the event handler has to be defined as a method in a class that points to an event in the interface IF_SALV_EVENTS_ACTIONS_TABLE. This is all wonderful in keeping with the object oriented approach to programming.

However, when you try to use or manipulate the data from the displayed ALV grid, the class gets in the way. If you look at the SALV_DEMO_TABLE_EVENTS program, it gets around this issue by having the event handler method calling a Form which is back in the program. While this works, you are still declaring the data table as a global, and not really using the object oriented methodology. Here are two ways to get that data and use object oriented methodology without falling back to a Form in the report.

Method 1 – Reference to the table in the class

The very first thing I want to do is acknowledge that this post borrows heavily from this blog post on the website www.kerum.pl. I looked around the site, and unfortunately I can’t acknowledge the name of the person other than “Kris”. Well thank you Kris! From Kris’ example, I have added some newer ABAP constructs and converted the older style FORM to an object oriented METHOD. The key piece that makes this work is declaring the reference to table in the class…

ref_alv_table TYPE REF TO tt_usr,

and then sending the reference of table used in the ALV to that attribute with this statement.

Method 2 – Single class

Now that I have moved the display_alv code into a class… why not move it into the same class as the event handler? This works wonderfully! No more passing references and dereferencing them. The table and the alv object are declared as private attributes. That way any method in the class can access them and you don’t have to pass any references around. I think it is both easier to understand the context of each variable and also reduces the overall code.

Another goodie –

See how I have used CL_ABAP_BROWSER to quickly display a popup window with a couple of lines of data. While I could send it a full HTML table with style sheets and headers and tables, etc… Here I just pass the data separated by a line break </br>.

If anyone has a better and simple way of quickly displaying data, please do share. In the past, I have created screens and added tables using CL_DD_DOCUMENT. This seems to be much simpler, and if you have HTML knowledge, you will probably be able to do a lot more visually.

Assigned tags

Related Blog Posts

Related Questions

Be careful with declaring a local internal table while playing with control framework. It always relies on an ALV internal table which must be persistent (global scope, or anonymous data object (i.e. CREATE DATA) with at least one living reference).

In your case, a local internal table works because the display is done full screen, i.e. the display is done inside the method, and the method never ends, so local internal table always exists. Even when a button is clicked, the method is still active (in the call stack), so the internal table still exists. You can see this by debugging it.

That would not work if you’d display the ALV after leaving the method where you build.

You could solve using CREATE DATA for creating the internal table. It would still exist after you leave the method, as long as there is at least one reference to it.

Very true! I do not know what the impact of containers or split screens or even using one of the other ways of creating an ALV.

I am not sure I understand how using CREATE DATA for the internal table is going to help. Even in that case wouldn’t you have to create the data reference in a global scope? In that case, what is the advantage over just declaring a global variable?

I wasn’t clear about the containers; in fact, it was more an example: screens with something else than just a full screen ALV grid, are handled using several methods, so probably the procedure where the ALV internal table is defined is not the same procedure where the main screen is displayed. It’s better to forget my example.

If you create an “anonymous data object” in a procedure, it will persist after the procedure has ended if you have stored the data reference in a data object declared outside the procedure.

I didn’t say that CREATE DATA was better than a global variable, especially for dynpros.

I just saw that local alv internal table, and this made be react, because this is something which often leads to a short dump, at scrolling for instance (FREED STACK).

I see that you have moved the alv table from local scope to an attribute. As long as the instance of LCL_ROUTINES exists, the alv table exists. So it’s much better now.

Did not think about that! I guess it makes sense that it actually has to contents as opposed to the reference. In case of the reference being global it would no longer be usable outside the procedure. If we start thinking in terms of pointers, this makes so much more sense.

this is a common design discussion while trying to assign responsibilities to objects. In this case, I see two competing architectures:

Layered Architecture, with the Model-View-Controller as the grandfather of patterns. The solution (Method 1) looks like:

It could be improved to better match the pattern participants, e.g. CL_SALV_TABLE is the view, the LCL_ROUTINES can hold the model data (data selection), and the LCL_EVENT_HANDLER could implement the controller. With this, you will gain the needed flexibility if you have many views.

I used to create a public interface to get the model data, check this example.

Nowadays I use the iterator pattern to traverse data in the model object without making it global, e.g. the model object can generate a new iterator for selected items.

Smart UI (Method 2) often seen as an Anti-Pattern because all the business logic in the user interface. But even Eric Evans (cf. chapter 4, Domain-Driven Design) considers it a legitimate pattern in the contexts of simple applications. You just have to know there is no graceful path to richer behavior.

I think I need a beer or 10… we will have a lot to talk about differing philosophies 🙂 .You bring a lot to the table in your discussions of architecture – but, I think they have a place and a purpose. Just the fact that you mention two different architectures goes to show that there is no right or wrong – just benefits and detriments to different styles. And there is no saying that the a style which has a benefit in one set of circumstances will benefit in another – you have to pick and choose and maybe even mix it up based on your particular set of circumstances.

The classical MVC pattern was created not just to assign responsibilities to object, but also so the objects can then be handed to different people with different skill-sets and knowledge areas. I think that still has a very important place where you are building almost anything in software – but for standard ABAP reports. I would approach it a bit different.

I believe that in using the CL_SALV_* classes, the pattern that you are following the closest is that of “naked objects” because the user interface is automatically created. So in this case what we primarily dealing with in the entire custom code of the report is the Model.. and secondarily parts of the controller that are not pre-packaged. In that paradigm, I should have named my LCL_ROUTINES as LCL_MODEL and the LCL_EVENTHANDLER should have been named LCL_CONTROLLER. There is no view in the program, because the view is the CL_SALV_TABLE.

One place in your examples is the model LCL_MARA. This being a fairly standard object that will have to dealt with in every SAP system, I would take the time to make this a stand alone class ZCL_MARA with its own error handling and related bits and pieces such as getting the material description. This would truly be a separation of works. If this is done, then the report just becomes the controller and nothing else… at that point do we really need to go through the pain a finding a place to put the M, V and C separately in the report?

I did go over your examples, and I think unless you are making a program that will be stand alone and handle anything thrown at it without modifications, the iterator pattern is an overkill. If you do want to make your own version of SE16N with drill downs into value tables, it will be brilliant! Or, you could handle that with recursive calls too (Gasp!! 🙂 )

The other example with the public interfaces, again I not sure what you gain from the level of splitting up of tasks into separate local classes gets you. If there is something that I am missing please fill me in – does it make it easier to maintain, easier to extend, easier to copy to a new report for a different data structure? Here is how I managed to rewrite it… I think the behavior of both programs is identical.

Just to make sure I am not missing anything in your example, to get the table reference into an event handler, I had to go through the same gyrations as in my original blog post. Based on your split up of methods, where would I put the GET REFERENCE? My test had me putting the event handler method in the LCL_VIEW and taking the lr_data reference variable and declaring it as an attribute in the same class. Would you approach it the same way?

PS – I love the cl_demo_output, though I feel like with Horst Keller’s disclaimer in this post, I am going to stay away from using it in a production environment.

PPS – Though I disagree with some of your style, you are teaching me quite a bit and challenging me to think – Respect!

the question is always, which use case do you have? which concerns do you have?

We can discuss code structure patterns. but for each concrete case, the starting point will be requirement gathering/use cases, a specification that can be validated with tests. From this vantage point we evaluate a design and select an architecture. The solution has to be correct, but we can only call it optimal with reference to a given specification/context.

So the correct answer is always: it depends. Which use case do you have? Which concerns do you have?

Your proposal works, but it conflates the view and the model for convenience. This is legitimate if you want to avoid complexity. In this case, I would object with naming the class LCL_MODEL, as most of the logic is ALV code and it is not your intent to create a pure MODEL object.

My proposal reflects my concerns:

Modelling: I want to expressive code in the language of business objects (BO), i.e. in the ubiquitous language used in the domain model (note this does not mean plain english). I want to eliminate mappings between the objects in the specification and the code structure, so it really about a good domain model.In this case, LCL_MARA is the BO. Even with an existing ZCL_MARA BO, I think LCL_MARA would be needed as a facade to hide its complexity in a specific report. Helper objects were introduced because
– the BO should not be tied to selection screens, so an abstraction LCL_PARAM was invented/extracted to express this concern
– the BO should not be tied to the view, the LIF_DATA_SOURCE interface was extracted.

So I want to separate concerns to make my intent visible in the code structure.

Enable Unit Tests: Creating a design where objects only have one reason to change really requires discipline, but it enables unit testing in isolation. I can use mock objects for the data source (LCL_MARA) or the user parameters (LCL_PARAM).

Iterator Use Case

Suppose you have custom functions in the ALV that must process the user selected entries. The following iterator will return a reference to the each selected entry. So the PRIVATE data can be changed.

I always love to challenge the “it cannot be done” but sometimes, in fact, it cannot be done,

In the SAP GUI DYNPRO world the business logic was right there in the UI. For so long people have fought against this, with varying degrees of success, ignoring the fact the framework was designed to be all messed up together (Business Logic and UI logic) like an omelette,

You can break this up with some effort, and that is all good, but SAP never had a GUI way to do the MVC properly. Indeed Web DYNPRO did not really do MVC in a serious sense, UI5 does, however, as the UI is in a different language than the model…

In this case if the SAP framework wants a global variable, then you are best off supplying a global variable, and not banging your head against a brick wall….