I've gone ahead and implemented this, but instead of using the
Component.saveInnermostModel method, I coded up my own. The implementation
in Component only traverses IWrapModel implementations, not IChainedModel
implementations. My implementation handles both. Note that this will only
find components backed by the exact same IModel instance. Two distinct model
objects wrapping the exact same object will not match.
/**
* Searches the component tree and finds any components backed by the
same model as the model passed in, then
* adds those components to the given AjaxRequestTarget to be rendered
(after first verifying that they have
* outputMarkupId set to true).
*
* @param root the root MarkupContainer whose descendents will be
examined for matching backing models
* @param target the AjaxRequestTarget to add all the matching
components to
* @param model the model to match
*/
public static void addComponentsBackedBySameModel(MarkupContainer root,
final AjaxRequestTarget target, final IModel model) {
// Visit all children, looking for components backed by the same
model.
root.visitChildren(new IVisitor() {
public Object component(Component component) {
if (WicketUtils.isSameInnermostModel(component.getModel(),
model)) {
if (component.getOutputMarkupId()) {
// Refresh the component.
target.addComponent(component);
// No need to go deeper if we're already refreshing
the entire component!
return
IVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER;
}
}
return IVisitor.CONTINUE_TRAVERSAL;
}
});
// TODO: Return the list of components that were re-rendered?
// See also:
//
http://www.nabble.com/Best-Practices-for-accessing-repainting-sibling-cousin-components--tf3841514.html
}
/**
* Returns true if the given models ultimately share the same innermost
model;
* otherwise returns false.
*
* @param model1 the first model
* @param model2 the second model
* @return true if they share the same innermost model; otherwise
* false
*/
public static boolean isSameInnermostModel(IModel model1, IModel model2)
{
// There is a version of this on Component, but it is only aware of
IWrapModel.
// Our version knows how to traverse chained models as well as
warpped models.
if (model1 != null && model2 != null) {
return getInnermostModel(model1) == getInnermostModel(model2);
} else {
return false;
}
}
/**
* Returns the innermost model within the given model; similar to
Component.getInnermostModel,
* except that it follows both IWrapModel and IChainingModel, not just
the former.
*
* @param model the model
* @return the innermost model within that model
*/
public static IModel getInnermostModel(IModel model) {
// Pass through nulls.
if (model == null) {
return null;
}
// Try to figure out an inner model.
IModel innerModel = null;
if (model instanceof IWrapModel) {
innerModel =
getInnermostModel(((IWrapModel)model).getWrappedModel());
} else if (model instanceof IChainingModel) {
innerModel =
getInnermostModel(((IChainingModel)model).getChainedModel());
}
if (innerModel != null) {
// If we got an inner model, return it.
return innerModel;
} else {
// Otherwise the given model was the innermost, so return it.
return model;
}
}
/**
* Returns the root ancestor of this component by walking up the parent
tree until a component is found with no parent;
* note that this could return the same component passed in.
*
* @param component the component whose root ancestor should be
returned; must be non-null
* @return the root ancestor
*/
public static Component getRootAncestor(Component component) {
ExceptionUtils.throwIfNull(component, "component");
Component cur = component;
while (cur.getParent() != null) {
cur = cur.getParent();
}
return cur;
}
Jonathan Locke wrote:
>
>
> It shouldn't be hard to write the method you're talking about. To find
> all the components using the same model as a given component, just walk
> the component hierarchy using visitChildren() and add any component which
> returns true for sameInnermostModel(component).
>
> There is a more general case of this problem though where one area of a
> web page may need to be updated because some completely unrelated area
> changed. This I'm handling by hand right now, but I was asking a day or
> two ago if there was a way to add a component to every ajax request (Eelco
> answered that you can do this by implementing a request processor, I
> think). It seems to be pretty common in an AJAX request to want a global
> feedback component to update. Maybe we could have a poor-man's version of
> this where if you override some boolean method, your component will get
> auto-ajax-updated on every AJAX request. For many problems, this would be
> convenient because it's easier to just update the thing every time than to
> think about all the places it might need to be updated.
>
>
> dukejansen wrote:
>>
>> I have some state which backs two panels, Panel A and Panel B, that may
>> be included as part of other panels. Ultimately they are both on the same
>> page, and their backing state is shared via the model class that backs
>> both of them. Panel A has an Ajax event handler which modifies the
>> backing model state, after which I want to force Panel A and Panel B to
>> repaint.
>>
>> I've dealt with this in a few different ways so far, and they all bother
>> me:
>>
>> 1. Walk up the containership tree and back down again until I find the
>> panel with a known ID or which implements a specific marker interface,
>> finding it that way. (Or do a full DFS of the tree to be thorough.)
>>
>> 2. Assume how my Panel is included and how the other Panel are included,
>> and explicitly walk up and back down the containership tree. This is
>> fragile because if I decide to rework panel containership, the method
>> could fail.
>>
>> Is there some better way of doing this that I'm missing? A best practice
>> for reaching out to siblings and cousins?
>>
>> Or something more fundamental to trigger refreshes of all componets
>> backed by that model? Seems like a common use case. A component updates
>> some state as part of ajax event, then wants to use ajax to repaint any
>> other components backed by that state.
>>
>> Interested to hear how others have solved this problem.
>>
>> -Jason
>>
>
>
--
View this message in context:
http://www.nabble.com/Best-Practices-for-accessing-repainting-sibling-cousin-components--tf3841514.html#a10975521
Sent from the Wicket - User mailing list archive at Nabble.com.
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Wicket-user mailing list
Wicket-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/wicket-user