HCM Processes & Forms: CRUD with FPM tables/lists – part 2 : Advanced Operations
This is part 2 of a 3 part series in discussing CRUD (Create, Read, Update, and Delete) operations with our FPM form tables in HCM P&F. If you stumbled here by accident, stay a while and join us. There will be free cake at the end!
So if you did follow through part 1 ( HCM Processes & Forms: CRUD with FPM tables/lists – part 1 : the Basics and Standard Events ) , you learned that SAP (via our standard HCM P&F feeder class) handles a lot of the “heavy lifting” for us. Unlike the “old” ISR days, we have standard “events” to add and delete rows in our tables (lists), and it will handle reordering our dataset indexes and keeping everything “in sync” (not to be confused with the pop music boy band) for us. However, all this “auto-magic” has a few drawbacks. Namely, we cannot really control any validations or defaults(for adding rows) when adding or deleting rows. The “magic” just happens, and you can only sit back and watch.
But what if we want more control? What do we do then? Are we stuck using the standard “add” and “delete” row events and just dealing with it? NO!!!! We can easily “roll our own” events and handle them ourselves.
User Events and Event Handling
If you have followed my blogs/threads/rants along the years, you will know there is no love lost between SAP’s implementation of “user events” in HCM P&F and me. However, as the old saying goes “when life gives you lemons, make lemonade!”…..I have learned how to turn SAP HCM P&F “user events” into my own. This is largely due to the fact that with the FPM framework, I can now “trap” my “user events” in my generic service and then handle them exactly as I want. You might have seen me discuss this in another blog ( HCM Processes & Forms: Smart FPM F4! (or “how to step up your drop-down list game”) ) ,but now my “generic services” look a bit “odd”. I tend to have one big “operation” defined with all of my form fields mapped to my user event field group, but then in my “do operations” method, I “catch” my FPM event, have a “case statement” (making sure that “CHECK” event is in there!) and react to everything like I want to in my service. I use a few other “tricks” to make sure I know when it is an custom “event” versus something I want to process in “check” and in the standard services (like SAP_PA), but we will not go into that here (*hint*…set a “flag” in your custom service that says “this was a user event” and then have “rules” set up where needed to not process SAP_PA or whatever when the “flag” is set). It is a LOT easier! OK, so back to it….
First off, we will create a “user event” in our configuration (let us call it “UE_ADD_ROW“) and assign our fieldgroup (with all form fields assigned to the fieldgroup) to our event. I am not going into detail of doing that because it is very basic HCM P&F configuration in the Design Time (transaction HRASR_DT). I just want to make sure we have our event set up because now it will matter for the next step.
After our “user event” is defined, we need to add it to our form. In our FLUID design tool, we simply add a toolbar element (as described in part 1 of this series), however this time, we will select our new custom “user event” and not the standard “add row” event:
We do exactly the same for “delete” row (ie. create a backend user event and then add a button in FLUID to the toolbar of the table/list).
In runtime, we now have:
Our events are in place, our form table has been modified to display buttons that “trigger” the events, so now all that is left is actually handling the events in our own code. One of the nice parts of handling this on our own is that we can also do fun things like:
- Assign default values (add row)
- Validation (for example, do we allow the user to delete the selected row?)
- Pre/Post processing (for example, say we want to recalculate some displayed total after a row has been deleted)
- Communicate with aliens (ok….not that one…just seeing if you are paying attention)
Create (Add Row)
As mentioned before, we should know now how to “trap” FPM events from my other blog ( ). The easiest thing to do is to “trap” the event, and then in our “do operations” method, we make a “case statement” that responds to each even accordling. For example, this might look like….
“do our add stuff
WHEN ‘CHECK’ OR ‘USER_EVENT_CHECK’.
“do regular processing
As shown, in our own generic services, we can handle the “add” row operation ourselves. It is probably the easiest operation to handle. Remember that a “row” in HCM P&F table-speak is simply the same index number across all our “column” fields. Therefore, the first step is to pick whichever column is the most important (will always have a value for instance) in your table and use that as our “key” column. We “add a row” simply by finding the “highest” index of our “key column” field and then adding one (index + 1) to this to make “room” for our new entry/row. We set the fieldname, fieldgroup, fieldindex and fieldvalue (default) for our new entry and simply append it to the existing dataset. We do this for each column in our table.
Reading a row is a bit different. Sure, we can default/initialize our rows, and we can surely loop through and “read” all of our rows. But what if we just want to read one particular row? What if the user selects a row in the table, and we want to display other details for the selection in a “form” component under/next to our table? We need to know which row the user selected in order to find our correct index value.
I will not give away all the secrets of this (I do want you to do some work on your own) , but I will give you a hint that again…it goes back to my other blog (HCM Processes & Forms: Taking control of your FPM form tables…by brute force! (haha)). You want to “trap” your FPM event.
Using similar code,
The “event” is quite useful to know, but we also have access to a LOT of other very useful information now as well. You can actually look at the “PARAM” table that we get back. It is laid in key/value pairs, and some will depend on what kind of component triggered the event (form vs. list). There are several Key/Value pairs that are very useful (you can know which component triggered your event, which field, what type of control it was, etc). I suggest you play around with it and figure it all out because it is just so darn fun! (ok…maybe not “fun” but interesting nonetheless). For our immediate needs, to find the “index” of the selected row, we actually look at:
For me personally, I put all of my “figure out the FPM event, what triggered it, and selected index” all into a central, “common” custom class I created (more on that in our part 3/“Expert” discussion later).
So now using this method, we know exactly which row (and index!) was selected. To read our row, it is simply a matter of looping through our dataset for all of our “column” fields with that same index value. EASY!
Update (modify row)
Again as mentioned in the previous, part 1 of this series, updating a row is fairly straightforward. It will really depend on the “display type” (ie. control) you have assigned to your column (such as a checkbox, input field, drop-down, etc). Because the form field is bound to the row, it automatically knows which index you are updating and handles this for us. If you want to control the UI settings of your column (read only or ready for input), you can do that by setting your configuration for the form field to have the UI settings determined by your generic service. Then, in your generic service, you simply assign the “ui attribute” as needed for you form field (Raja Sekhar Kuncham does a very nice job explaining this in his blog FPM Forms Scripting). However, be warned….this setting will affect the entire column….all or none….which might be fine in some cases. However, what if you want a particular cell of you column to be read-only and others open for input? That kind of “cell level” control takes a bit more work which we will also cover in the part 3/”Expert” discussion in this 3 part series.
Delete (remove row)
So now that we know which row (index) the user has selected, much like our “add row” code, we can just got through our dataset and do a little “dancing” to move our dataset values around. We do not actually have to “delete” a “row” and can instead, simply “move up” all the rows after the selected row and leave the last row “empty” (or delete that one….but be careful as we must always have at least one row…index 1…or the HCM P&F framework will error out on us).
I know this sounds confusing. We have our “selected row” index. We just loop through all columns/form fields with an index higher than our “selected index” (greater than the selected index), get that value, write the value back to the previous index field (n-1), continue to the end of our fields can simply clear out the very last one. In effect, we are just shifting our values “up” starting from the selected row for “delete” (we are not actually deleting anything…just reassigning values).
I will not burden you with all of that code. In fact, the easy way to go here….simply look at the “delete row” code from the standard “feeder” class (as mentioned in part 1) and just fit that to your needs. No need to reinvent the wheel.
Wrap Up and Next Steps…
Hopefully after trudging through this “edge of your seat”, wonderfully well written, thrill ride of a blog (one can dream, right? haha), you now understand how easy it is to (1) create your own “add”/”delete” row user event (2) “trap” the event in your own generic service (3) respond accordingly (low-tech “event handling”). I am just such an amazing communicator that this all been expressed so completely and easily that it is now second nature for you (I am still dreaming…haha). Once you do get the hang of this, you will likely hit upon your next grand idea…..”Now that I am doing this in my own generic service, what if I want to do the same in several other services? I don’t want to have to repeat this code each time. There must be an easier way!”….and to answer you, there is…which we will now cover in….
Oh…and by the way, if you made it this far into the blog looking for your sweet reward at the end, you should know that “the cake is a lie”.