How to add an ContextMenu to a Diagram Element in Primefaces - jsf

Is it possible to add an Context Menu to an Element in an Diagram?
I was able to add the Context Menu to the whole diagram in the xhtml file, but the context menu should only be shown if the user do a right click on the elment.
I try to solve it programmatically but the context menu is not show.
Here is my code:
DefaultDiagramModel model = new DefaultDiagramModel();
model.setMaxConnections(-1);
Element element = new Element();
element.setData("someData");
element.setId("testId");
ContextMenu contextMenu = new ContextMenu();
DynamicMenuModel menuModel = new DynamicMenuModel();
DefaultMenuItem menuItem = new DefaultMenuItem("show Info");
menuModel.addElement(menuItem);
contextMenu.setModel(menuModel);
contextMenu.setFor(element.getId());
model.addElement(element);
I use Primefaces 7

I ran into the same needs. Instead of adding the ContextMenu in the Bean. I was able to get it working using <f:facet> in the xhtml file like the following:
Create an new Class to use as the Data
public class NodeElement implements Serializable {
private Long id;
private String title;
private String type;
//... other variables
// getter and setters and other methods
}
Example creating an Element
Element element = new Element(new NodeElement(1L, "Element Title", "type1"), "30em", "16em");
in diagram.xhtml
...
<h:form id="patent-form" enctype="multipart/form-data">
<p:growl id="msgs" showDetail="true" skipDetailIfEqualsSummary="true" />
<p:diagram id="sample-graph" value="#{diagramBasicView.model}" styleClass="ui-widget-content" var="el">
<f:facet name="element">
<h:panelGroup layout="block" rendered="#{el.type == 'type1'}" id="type1-node" styleClass="type1-node" ps:data-id="#{el.id}">
<h:panelGroup laybout="block" styleClass="graph-node-content" ps:data-id="#{el.id}">
<h:outputText ps:title="Node Title: #{el.title}" ps:data-id="#{el.id}" value="#{el.title.length() gt 45 ? el.title.substring(0, 45) : el.title}" styleClass="graph-node-title"/>
<!-- ... OTHER data (make sure to use h:outputText or other JSF tags) -->
</h:panelGroup>
</h:panelGroup>
...
</h:panelGroup>
<f:facet>
</p:diagram>
<!-- from https://stackoverflow.com/questions/35812682/jsf-primefaces-eventing-in-diagram-elements -->
<p:remoteCommand name="elementClicked" actionListener="#{diagramBasicView.onSelection()}"/>
<p:contextMenu for="type1-node" style="width: 220px; height: 150px;">
<p:menuitem value="Show Info"
update="sample-graph"
id="show-info-menu-item"/>
<p:menuitem value="Second Menu"
update="sample-graph"
id="second-menu-item"/>
</p:contextMenu>
</h:form>
<!-- use to get the id from the element as data attribute -->
<script>
$('.ui-diagram > .ui-diagram-element').contextmenu(function(info){
// console.log($(info.target).data('id'));
elementClicked([ {
name : 'elementId',
value : $(info.target).data('id')
} ]);
});
</script>
...

Related

How to make selected h:selectOneRadio of h:dataTable remain selected on postback?

In normal circumstances like this:
<h:form>
<h:selectOneRadio value="#{bean.gender}">
<f:selectItem itemValue="Male" itemLabel="Male" />
<f:selectItem itemValue="Female" itemLabel="Female" />
<f:selectItem itemValue="Other" itemLabel="Other" />
</h:selectOneRadio>
<h:commandButton value="Submit" action="#{bean.action}" />
</h:form>
Selecting one radio button disselects the other & the radio button will be remain selected on the postback. (when the same view is rendered)
However, when we're dealing with an iterating component like <h:dataTable>, the selection is lost.
Consider the snippet:
<h:form id="hashMapFormId">
<b>HASH MAP:</b>
<h:dataTable value="#{playersBean.dataHashMap.entrySet()}" var="t" border="1">
<h:column>
<f:facet name="header">Select</f:facet>
<h:selectOneRadio id="radiosId" onclick="deselectRadios(this.id);"
value="#{playersBean.select}">
<f:selectItem itemValue="null"/>
</h:selectOneRadio>
</h:column>
</h:dataTable>
<h:commandButton value="Show Hash Map Selection"
action="#{playersBean.showSelectedPlayer()}" />
</h:form>
With disselecting the other radio buttons when one radio button is selected being implemented by simple JavaScript-
function deselectRadios(id) {
var f = document.getElementById("hashMapFormId");
for (var i = 0; i < f.length; i++)
{
var e = f.elements[i];
var eid = e.id;
if (eid.indexOf("radiosId") !== -1) {
if (eid.indexOf(id) === -1) {
e.checked = false;
} else {
e.checked = true;
}
}
}
}
Fire the GET request:
Select a radio button:
Now press the submit button, response:
You see that the radio button gets dis selected on postback. How to solve this shortcoming?
I know it very well that this is due to this component attribute itemValue being null:
<f:selectItem itemValue="null"/>
This trick is a leftover from JSF 1.x / 2.0/2.1 when it wasn't possible to use a <h:selectOneRadio> for single row selection in a <h:dataTable>. This trick originated in my 10 year old blog article Using Datatables - Select row by radio button.
The root problem is, HTML radio buttons are grouped based on their name attribute, so the webbrowser knows which others to unselect when one is selected. But JSF generates by design a different one for each <h:dataTable> item, with the row index inlined and therefore they can't be grouped and hence the JavaScript based workaround.
Since JSF 2.2, with the new passthrough elements and attributes feature, it's however possible to force the name attribute to the value of your choice and capture the selected item via a helper <h:inputHidden>. This is fleshed out in another blog article of me, from previous year: Custom layout with h:selectOneRadio in JSF 2.2. The article uses <ui:repeat> as an example, this can be rewritten to <h:dataTable> as below.
<h:form>
<h:dataTable value="#{bean.items}" var="item">
<h:column>
<input type="radio" jsf:id="item" a:name="#{hiddenItem.clientId}"
value="#{item.id}" a:checked="#{item.id eq bean.selectedItemId ? 'checked' : null}" />
</h:column>
<h:column>#{item.id}</h:column>
<h:column>#{item.name}</h:column>
</h:dataTable>
<h:inputHidden id="selectedItem" binding="#{hiddenItem}" value="#{bean.selectedItemId}"
rendered="#{facesContext.currentPhaseId.ordinal ne 6}" />
<h:commandButton id="submit" value="Submit" action="#{bean.submit}" />
</h:form>
#Named
#ViewScoped
public class Bean implements Serializable {
private List<Item> items;
private Long selectedItemId;
// ...
public void submit() {
System.out.println("Selected item ID: " + selectedItemId);
}
// ...
}
And yes, the selected radio button remains selected on postback this way. You can also pass whole entities, this only requires a converter on the <h:inputHidden>.

Controlling component from backing bean

I want to 'update' a component with a new component in the bean.
XHTML
<composite:interface>
<composite:attribute name="rootKey" required="true" />
<composite:attribute name="id" required="true" />
</composite:interface>
<composite:implementation>
<rich:panel id="#{cc.attrs.id}" binding="#{myBean.customPanel}"/>
<a4j:jsFunction name="createPanels"
action="#{myBean.createPanels}"
render="#{cc.attrs.id}">
<a4j:param name="rootId" assignTo="#{myBean.rootId}"/>
<a4j:param name="rootKey" assignTo="#{myBean.rootKey}"/>
</a4j:jsFunction>
<script type="text/javascript">
/* <![CDATA[ */
jQuery(document).ready(function() {
createPanels('#{cc.attrs.id}','#{cc.attrs.rootKey}');
});
/*]]> */
</script>
</composite:implementation>
</ui:composition>
Bean
private UIPanel rootPanel;
public void setCustomPanel(UIPanel panel) {
rootPanel = panel;
}
public UIPanel getCustomPanel() {
return rootPanel;
}
public void createPanels() {
//try #1 : Adding new panels as children
rootPanel.getChildren().add((UIPanel)createPanels(rootId,rootKey));
//try #2 : A new Panel component
rootPanel = (UIPanel)createPanels(rootId,rootKey);
...
rootPanel.setId(rootId); // this ID is the same as the 'placeholder' panelId
FacesContext.getCurrentInstance().getPartialViewContext().getRenderIds().add(rootPanel.getId()); // the rerender should also already be done in the XHTML js function.
}
With the debugger i see the rootPanel changes into the new panel component, but not in the view.
What do i miss?
What i try in short: Generating dynamically components as children for the panel component in the xhtml view. The generation needs the 'rootKey' param for generating the right set.
Using:
JSF Mojarra 2.1.19
Richfaces
The solution for me was to rerender te parent, not the actual component.
Also adding the new element children as children to the bound component.
XHTML : Added a h:panelgroup around the place holder.
<h:panelGroup id="#{cc.attrs.id}_parent">
<rich:panel id="#{cc.attrs.id}" binding="#{myBean.panel}"/>
</h:panelGroup>
<a4j:jsFunction name="createPanels"
action="#{myBean.createPanels}"
render="#{cc.attrs.id}_parent">
<a4j:param name="rootId" assignTo="#{myBean.rootId}"/>
<a4j:param name="rootKey" assignTo="#{myBean.rootKey}"/>
</a4j:jsFunction>
BEAN : try #1 was the right one.
public void createPanels() {
UIPanel newPanel = (UIPanel)createPanels(rootId,RootKey);
if(newPanel.getChildCount() >= 1){
rootPanel.getChildren().addAll(newPanel.getChildren());
rootPanel.setStyleClass(newPanel.getStyleClass());
rootPanel.setHeader(newPanel.getHeader());
}
}

Inserting multiple datas in 1 entity in a page

i have a CRUD generated create form:
<div class="create-form">
<h:form>
<h:inputText id="name" value="#{pointController.selected.name}" title="#{bundle.CreatePointTitle_name}" required="true" />
<h:inputText id="term" value="#{pointController.selected.term}" title="#{bundle.CreatePointTitle_term}" required="true" />
<p:commandButton styleClass="btn" action="#{pointController.create}" value="#{bundle.CreatePointSaveLink}" />
</h:form>
</div>
<button>add new form</button>
i have a button that if clicked it will create another form same as above using javascript. (2 inputText, name and term)
my goal is, with 2 or more forms, depending how many forms the user wants, with 1 commandButton that is clicked it will insert everything in the database.
example:
first form: name = first form test, term = first form testterm
2nd form: name = 2nd form test, term= 2nd form testterm
after clicking the command button
2 rows will be inserted in the same table in the database.
but i'm not sure what would be the structure for page for this.
You can't send data from many forms in a single request using JSF components, you should serialize all the data and send it manually. It would be better to have a List<Item> and every time you click in the button it will create a new item on the list and update an UIContainer that will display the items of the list.
This would be a start example of the above:
#ManagedBean
#ViewScoped
public class ItemBean {
private List<Item> lstItem;
public ItemBean() {
lstItem = new ArrayList<Item>();
addItem();
}
//getters and setter...
public void addItem() {
lstItem.add(new Item());
}
public void saveData() {
//you can inject the service as an EJB or however you think would be better...
ItemService itemService = new ItemService();
itemService.save(lstItem);
}
}
JSF code (<h:body> content only):
<h:form id="frmItems">
<h:panelGrid id="pnlItems">
<ui:repeat value="#{itemBean.lstItem}" var="item">
Enter item name
<h:inputText value="#{item.name}" />
<br />
Enter item description
<h:inputText value="#{item.description}" />
<br />
<br />
</ui:repeat>
</h:panelGrid>
<p:commandButton value="Add new item" action="#{itemBean.addItem}"
update="pnlItems" />
<p:commandButton value="Save data" action="#{itemBean.saveData}" />
</h:form>

how to bind dynamic list of composite components to panelGroup

composite component xhtml:
<composite:interface componentType="metroComponent">
<composite:attribute name="uniqueId" />
</composite:interface>
<composite:implementation>
<rich:panel width="100%" header="#{msgs['pages.metroEth.header2']}">
<table class="resData">
<tr>
<td class="labelLeft"><h:outputLabel id="optionLabelId"
value="#{msgs['pages.ccparams.serviceOption']}" /></td>
<td><h:inputText id="optionTextId"
binding="#{cc.serviceOption}" size="15" /> <h:message
for="ccvodTextId" style="color:red" /></td>
</table>
</rich:panel>
</composite:implementation>
</ui:composition>
component type implementation is as follow:
#FacesComponent (value="metroComponent")
public class HtmlMetroCC extends UIComponentBase implements NamingContainer {
UIInput serviceOption;
public UIInput getServiceOption() {
return serviceOption;
}
public void setServiceOption(UIInput serviceOption) {
this.serviceOption = serviceOption;
}
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
}
there is also a backing bean which prepare panelGroup element with list of presented above composite components. i removed getters/setters for better reading
#ManagedBean (name="metroethernetBean")
#RequestScoped
public class MetroEthernetMBean implements IBean{
private MetroEthCCData metroCCData;
private HtmlPanelGroup metroCCPanel;
private List<HtmlMetroCC> metroCClist;
#PostConstruct
public void initBean(){
metroCClist = new ArrayList<HtmlMetroCC>();
metroCCPanel = new HtmlPanelGroup();
HtmlMetroCC initialMetroCC = new HtmlMetroCC();
metroCClist.add(initialMetroCC);
processMetroCCPanel();
}
private void processMetroCCPanel(){
metroCCPanel.getChildren().clear();
for (HtmlMetroCC comp: metroCClist){
metroCCPanel.getChildren().add(comp);
}
}
}
page fragment responsible for displaying panelGroup looks like this:
<h:panelGroup id="metroCCPanelGrouId" binding="#{metroethernetBean.metroCCPanel}" />
the question is, why my composite components are not presented on panel? it looks panel has no children added at all..
when I put it as follows on page:
<gui:metroCC />
then composite comp. is displayed properly on page. What i want to achive is a panel on page with composite components which can be added or removed dynamically by clickin add new or delete selected
There is a wrokaround, instead of binding dynamically created panel in menaged bean
<h:panelGroup id="metroCCPanelGrouId" binding="#{metroethernetBean.metroCCPanel}" />
use mBean to initialize list/add new/ remove and loop through it on page:
<h:panelGroup id="metroCCPanelGrouId">
<ui:repeat var="test" value="#{metroethernetBean.metroCClist}">
<gui:metroCC binding="#{test}" />
</ui:repeat>
</h:panelGroup>
BUT: still don't understand why doing it with my first aproach fails...... BaluC, where are you ;)

How to programmatically add dialogs with primefaces

I want to display some data through primefaces datalist component. To achieve this I have an arrayList like ArrayList<Person>.
The person class looks something like this
class Person{
private String name;
private String age;
private ArrayList<String> hobbies;
}
To display the data I'm using the following code:
<p:dataList value="{gameBean.persons}" var="person" itemType="disc">
Name: #{person.getName()}, Age: #{person.getAge()},
<h:link value="Hobbies" onclick="dlg1.show();" />
</p:dataList>
What I want do do now, is to create a link that opens a dialog when clicked:
<p:dialog header="Hobbies" widgetVar="dlg1" modal="true"height="100">
//iterate through hobbies list to print it
</p:dialog>
So far this is working because I've hard coded the dialog as mentioned above in the xhtml file.
This method is of course not working for a dynamic amount of persons as I can not hard code the dialogs and the links. My question is, how can I create this dialogs programmatically and assign the right widgetVar variable to the onClick method of the Links?
Any help is highly apprechiated,
cheers Nikolaus
You can try this:
<h:form id="form">
<p:dataList value="{gameBean.persons}" var="person" itemType="disc">
Name: #{person.getName()}, Age: #{person.getAge()},
<p:column>
<p:commandLink value="Hobbies" actionListener="#{gameBean.onPersonSelect(person)}"
oncomplete="dlg1.show();" update=":form:hobbiesDlg" />
</p:column>
</p:dataList>
<p:dialog header="Hobbies" id="hobbiesDlg" widgetVar="dlg1" modal="true"height="100">
//iterate through hobbies of gameBean.person to show here
</p:dialog>
</h:form>
#ManagedBean
#ViewScoped
public class GameBean {
private Person person;
public void onPersonSelect(Person person) {
this.person = person;
}
}

Resources