JSF Access viewParams из компонента
У меня есть компонент для отображения записи. Этот компонент может появляться несколько раз на одной и той же странице и, следовательно, просто получает сущность, передает ее компоненту поддержки компонента, где происходит вся магия.
На внешний вид компонента могут влиять различные параметры.
Теперь требуется, чтобы независимо от того, есть ли у меня 1 или 10 из этих компонентов, "одно и то же" состояние просмотра должно быть восстановлено с использованием Deeplink.
Допустим, мой компонент выглядит так:
<composite:interface componentType="itemView" >
<composite:attribute name="item" required="true" shortDescription="The item to display."/>
<composite:attribute name="urlPrefix" required="true" />
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#{cc.init}" />
....
</composite:implementation>
у него есть соответствующая бина поддержки, я могу получить доступ. (опуская это, поскольку это не требуется)
Теперь эти компоненты встроены в страницу, которая выглядит следующим образом:
<f:metadata>
<f:viewParam name="itemType"
value="#{listController.itemType}"></f:viewParam>
</f:metadata>
<ui:define name="content">
<!-- multiple occurences, ui repeat over list of items -->
<my:itemView item="#{currentItem}" urlPrefix="#{listController.nextLetter}">
Как уже упоминалось, состояние каждого элемента может быть затронуто, а измененное состояние должно быть доступно по внутренней ссылке. Итак, моя идея состояла в том, чтобы использовать <h:link>
внутри компонента префикс каждого атрибута и использование includeViewParams="true"
, так что каждое (не дефолтное) состояние является частью URL.
<composite:interface componentType="itemView" >
<composite:attribute name="item" required="true" shortDescription="The item to display."/>
<composite:attribute name="urlPrefix" required="true" />
</composite:interface>
<composite:implementation>
<f:event type="preRenderComponent" listener="#{cc.init}" />
<h:link includeViewParams="true">
<f:param name="#{cc.attrs.urlPrefix}size" value="#{cc.largerSize}" />
larger
</h:link>
....
</composite:implementation>
Так же, как и для "более крупной" ссылки, существует до 10 ссылок, предоставляющих различные варианты.
Проблема: include-view-params включает в себя все view-params из одного и того же компонента и его родителя (itemType), но НЕ из других компонентов. Таким образом, при изменении стиля "одного" компонента будут сброшены параметры другого компонента.
Ожидаемый: URL уже выглядит ?itemType=something&asize=5&abackground=green
Щелкнув ссылку "больше" на компоненте b, я получу: ?itemType=something&asize=5&abackground=green&bsize=5
Что происходит: после нажатия на ссылку, URL выглядит так: ?itemType=something&bsize=5
поэтому атрибуты для "а" отбрасываются.
Я заметил, что если я продлю <f:metadata>
объект на странице листинга с чем-то вроде <f:viewParam name="asize" />
параметр aSize будет сохранен при щелчке чего-либо изнутри "b" - но, конечно, если мне нужно перечислить все параметры, такие как THAT, мне не нужны компоненты, потому что я не могу предсказать количество элементов.
Итак, я попытался добавить это прямо внутри компонента (например, событие f:): <f:viewParam name="#{cc.urlPrefix}size" />
- но это не сработало.
Любые идеи на этот счет (или альтернативные решения? Примечание: сохранение состояний представлений в базе данных и простое использование "единого" идентификатора для ссылки на это состояние в основном плохо, поскольку это приведет к большому количеству данных. Требуется состояние представлений). оставаться преходящим, но восстановимым:0))
1 ответ
Наконец я нашел решение. Это не самое лучшее, но это работает по крайней мере;)
Я создал метод в компонентном компоненте, который будет возвращать строку запроса, содержащую все значения, за исключением значения, переданного в качестве параметра.
Поэтому всякий раз, когда я хочу изменить параметр asize=5
я называю эту функцию с параметром size
и добавьте новый параметр (функция позаботится об исключении только этого параметра, когда префикс совпадает, поэтому стиль других компонентов не будет затронут.):
public String buildUrlWithout(String keyString) {
List<String> keys = ConversionHelper.Explode(keyString, ",");
// get all parameters from the url.
String prefix = this.getAttributes().get("urlPrefix").toString();
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, String> params = externalContext.getRequestParameterMap();
List<KeyValuePair<String, String>> result = new LinkedList<KeyValuePair<String, String>>();
outerfor: for (Map.Entry<String, String> kvp : params.entrySet()) {
for (String s : keys) {
if (kvp.getKey().equals(prefix + s)) {
continue outerfor;
}
}
// keep param
result.add(new KeyValuePair<String, String>(kvp.getKey(), kvp.getValue()));
}
String qStr = KeyValuePair.toQueryString(result);
return qStr;
}
KeyValuePair
это просто класс-помощник, который ведет себя как Map<S,T>
но предлагает несколько функций, таких как join()
, split()
и т.д.. (в этом случае toQueryString()
используется)
Наконец, из компонента я использую это так:
<h:outputLink
value="#{cc.buildUrlWithout('size')}&#{cc.attrs.urlPrefix}size={cc.size +1}">
larger
</h:outputLink>
<h:outputLink
value="#{cc.buildUrlWithout('size')}&#{cc.attrs.urlPrefix}size={cc.size -1}">
smaller
</h:outputLink>
<h:outputLink
value="#{cc.buildUrlWithout('background,border')}&#{cc.attrs.urlPrefix}background=red&#{cc.attrs.urlPrefix}border=green">
red background + green border
</h:outputLink>