May 23, 2007
The component represents the fundamental unit of work in PeopleSoft.
Pages are the most visible object to the user, but they are just views
into a component. The component is where all the action is.
PeopleSoft keeps track of whether a component is "changed." This occurs
when the user or any PeopleCode changes the value of one of the fields
on one of the physical tables in the component. You may have seen cases
in which your code in an event such as SavePostChange never runs, even
though the user has clicked the Save button. This happens because the
component's state was never set to "changed" and so the event never fires.
One way to force the Save events to fire is to fake a change by
writing code to move a value into a field and then change it back.
This kludge is no longer necessary, though, because there is a
built-in function—SetComponentChanged()—that tells the component processor that some data
has changed (even if it hasn't).
I'm currently in the process of developing some self-service pages and
I've run into a little problem along these lines. A page contains a Submit button.
Code in RowInit sets the initial values of many fields, so now the
component is considered to have been changed.
The user can make changes to the data on the page and click Submit, at which
time workflow sends it off to the HR Department for approval. Now, if there
weren't actually any user-entered changes, I don't want to send it off and I want to
let the user know that nothing was changed. How to do this?
Unfortunately, there is no function that is the opposite of SetComponentChanged().
I came up with several solutions, none of which are all that good. One way would
be to assemble the page using only derived record fields and then shuttle
the data back and forth to the real tables. That's unappealing. DoSaveNow() could
be used to save the component after the various initial values are set, but that is
wasteful and raises other difficulties—for example, the code in the
various Save events would need to know whether this is the user's save or
the initial DoSaveNow(). Also, DoSave() and DoSaveNow() are restricted
to the FieldChanged and SaveEdit events, whereas I want to do this in
RowInit. A kludge would be possible, but... It would be nice to be able
to start the Submit button out in a disabled state and then enable it
after any field change. But that means turning off Deferred Processing
for every field—another unappealing prospect. Another way would be to maintain a
component-level boolean "dirty" flag.
This would be set in the FieldChanged event for every field (after being
initialized at the end of RowInit). That's a
possibility, although there are a lot of fields involved.
I tried a real kludge and it works. In RowInit, I concatenate
all of the starting values of the fields using a loop to go through all of
the fields (using GetRecord().FieldCount) and save this in a component-level
string. (That could be changed to a hash value using the Hash() function
instead.) Then when the Submit button is clicked, the same concatenation is
done again and the values are compared. It works, but it's one of those things
where you sort of feel compelled to explain and apologize in a comment. I'll
probably go back to setting a flag in all of the FieldChanged events...
Until next time...