0
votes

This problem has been bugging me for awhile and I can not seem to get around it. So I stripped it down to the most basic level. 1. created a new XPage and bound it to an exiting form 2. created a panel called 'displayPanel' 3. inside the panel create a comboBox and give it a few values and a default value of any valid value. 4. Set an onChange event that does a partial refresh of displayPanel 5. Add a computed field that simply displays the value of the comboBox. 6 added a button that does a partial refresh of displayPanel onClick.

Open the XPage and the computed field is blank, make a change and the computed field displays.

open the XPage again and click the refresh button and the computed field displays

Now this is a very simple example but what I need to do is actually more complex but the value of the comboBox (Does not need to be a comboBox) is not available until a refresh is performed. This is only an issue on a new document when it first gets it's defaults.

I have added this:

view.postScript("XSP.partialRefreshGet('#{id:displayPanel}')")

to every one of the page events but it does not appear to do an actual page refresh like clicking the button or making a change.

I'm at a loss as to how to make this work. If I could get this simple example to work the rest of what I need is easy.

Thanks

1
How do you get the value of the combo box in the computed field?Per Henrik Lausten
document1.getValue("myComboBox")Bill F
Try setting the default value combobox in the beforepageload event. datasource.replaceItemValue("fieldnamne","Value"Fredrik Norling

1 Answers

5
votes

Fredrik is on the right track -- you should set the value manually during an event -- but I would add two caveats:

  • Call setValue instead of replaceItemValue (e.g. document1.setValue("myComboBox", "Default Value");). The comparative advantages might not be applicable in this specific case, but you should be in the habit of always calling setValue instead of replaceItemValue (and getValue instead of getItemValue) so that, when you've encountered a scenario where it makes a real difference, you just get that benefit for free... the rest of the time, the methods are equivalent, so you might as well just use the one that requires less typing. :)
  • You'll probably need to do this in afterPageLoad: the data source may not be ready yet during beforePageLoad; depending on why you're referencing the default value elsewhere in your page, beforeRenderResponse might be too late; and afterRenderResponse would definitely be too late...

...which leads me to why the defaultValue attribute does not behave the way we might expect it to, especially for those of us with experience developing Notes client apps.

The XPages engine splits the processing of every HTTP request into several "phases". Depending on the type of request (initial page load, partial refresh event, etc.) and other factors, the lifecycle will consist of as many as 6 phases and as few as 2.

This tutorial page provides an excellent description of these phases, but in the context of this question, the following are specifically of interest:

  • Apply Request Values

    When an event runs against a page that has already been loaded (e.g. a user clicks a button, or selects a value from a combobox that has an onChange event, etc.), the HTTP request sent to the server to trigger the event includes POST data that represents the value of all editable components. This phase temporarily stores these values in the submittedValue property of any affected components, but the data source doesn't "know" what the new values are yet.

  • Process Validations

    This phase runs any applicable validators and, if any fail, skips straight to the last phase (which means it never runs the code of the event that was triggered).

  • Update Model Values

    If no validations fail (or none are defined), this phase takes the submitted values and actually stores them in the corresponding data source. Until this point, the data source is completely unaware that there has been any user interaction. This is intended to avoid prematurely polluting any back end data with user input that might not even be valid. Remember, not every data source is a "document"; it might be relational data that is changed via an UPDATE the instant setValue is called... which is basically what this phase does: it takes the submittedValue and calls setValue on the corresponding data source (and then sets submittedValue to null). This separation allows components to simply be visual representations of the state of the back end data -- visual representations that the user interacts with; our code should always be interacting directly with the back end data via the abstraction layer of a data source.

  • Render Response

    Once all of the other phases have run (or been skipped), this phase sends a response to the consumer. In other words, it sends HTML (or JSON, or XML, or PDF, etc.) back to the browser. But the most salient point in the context of this question is that the prior phases are always skipped on initial page load. When the user first accesses a page, they haven't had a chance to enter any data yet, so there are no request values to be applied. Since no data has been posted, there's nothing to validate. And -- most pertinent of all -- there's nothing to be pushed to the data model. So a representation of the component tree (or a subset of it, in the case of a partial refresh event) always needs to be sent to the user, but until the user interacts with that representation, the data source remains in whatever state it was when the most recent response was rendered... which is why, if you want a specific property of the data source to have a specific value before the user interacts with it, your code needs to set that property's value manually.

In summary, components are visual. Data sources are abstract. Perhaps this behavior would be more self-explanatory if the component property had simply been called defaultClientValue instead of defaultValue, because that's essentially what it is: a default suggestion to the user for what data to send back to the data source. But until they do, the data source has not received that value. So if, on initial page load, you need a data source property to have a value that it wouldn't already have in its default state, you should manually call setValue to give it the desired value.

P.S. Ironically, if you'd called partialRefreshPost instead of partialRefreshGet, you likely would have achieved the result you were looking for, because the former sends all the form data, while the latter just asks the existing component state to be rendered again. But then you're forcing all of the form data to be sent just to update one data source property, so it's better to simply do what's described above.