I'm learning ADF and have a question. I have an ADF TreeTable that I want to temporarly populate with static data. I'm using a bean for this, but the TreeTable is not being populated. Can someone check the code I have and tell me if I'm doing something wrong?
My treeTable
<af:treeTable rowBandingInterval="0" id="tt1" width="100%" value="#{viewScope.MyBean.treeData}">
<f:facet name="nodeStamp">
<af:column sortable="false" headerText="" id="c4">
<af:outputText value="#{row.col1}" id="ot2"/>
</af:column>
</f:facet>
<af:column sortable="false" headerText="Score" align="center" id="c5">
<af:outputText value="#{row.col2}" id="ot6"/>
</af:column>
<af:column sortable="false" headerText="Verified by " align="center" id="c3">
<af:outputText value="#{row.col3}" id="ot1"/>
</af:column>
<af:column sortable="false" headerText="On" align="center" id="c1">
<af:outputText value="#{row.col4}" id="ot5"/>
</af:column>
</af:treeTable>
RowMaker.class
public List row() {
List<rowModel> ls = new ArrayList<rowModel>();
for (int i = 0; i < 10; i++) {
rowModel tr = new rowModel("+","92%","Person X","14 Feb 2013");
ls.add(tr);
}
return ls;
}
RowModel.class
public class rowModel {
String col1, col2, col3, col4;
public rowModel(String col1, String col2, String col3, String col4) {
this.col1 = col1;
this.col2 = col2;
this.col3 = col3;
this.col4 = col4;
}
public String getCol1() {
return col1;
}
public String getCol2() {
return col2;
}
public String getCol3() {
return col3;
}
public String getCol4() {
return col4;
}
}
Managed Bean
<managed-bean id="__7">
<managed-bean-name id="__5">MyBean</managed-bean-name>
<managed-bean-class id="__8">com.im.popup.view.rowMaker</managed-bean-class>
<managed-bean-scope id="__6">view</managed-bean-scope>
</managed-bean>
The TreeTable would expect data of this form:
Manager1
|-Employee1
|-Employee2
Manager2
|-Employee3
|-Employee4
In your above example RowModel class doesn't have any children associated with it. I will take a very generic Employee example and show you how you can work with TreeTable:
class Employee{
private String firstName;
private String lastName;
private List<Employee> directs;
Employee(String fName, String lName){
firstName = fName;
lastName = lName;
directs = new ArrayList<Employee>();
}
//Getters and setters for above
public void addDirect(Employee emp){
directs.add(emp);
}
}
Lets look at creating the data for the TreeTable:
class TreeTablePageModel{
List<Employee> managers = new ArrayList<Employee>();
ChildPropertyTreeModel treeModel;
public TreeTablePageModel(){
Employee mgr1 = new Employee("First","Manager");
Employee mgr2 = new Employee("Second","Manager");
Employee mgr3 = new Employee("Third","Manager");
Employee emp = new Employee("First","Sub_1");
mgr1.addDirect(emp);
emp = new Employee("First","Sub_2");
mgr1.addDirect(emp);
emp = new Employee("Second","Sub_1");
mgr2.addDirect(emp);
emp = new Employee("Second","Sub_2");
mgr2.addDirect(emp);
emp = new Employee("Third","Sub_1");
mgr3.addDirect(emp);
emp = new Employee("Third","Sub_2");
mgr3.addDirect(emp);
treeModel = new ChildPropertyTreeModel(managers,"directs");
}
public ChildPropertyTreeModel getTreeModel(){
return treeModel;
}
}
In the code above I am wrapping the List of my Employee objects into a ChildPropertyTreeModel and also passing it the name of the attribute using which the ADF component can obtain the values of its children. And in the JSP I would access the data as:
<af:treeTable rowBandingInterval="0" id="tt1" width="100%" value="#{viewScope.pageData.treeModel}">
<!-- add columns here -->
</af:treeTable>
And my managed bean declaration would be:
<managed-bean id="__7">
<managed-bean-name id="__5">pageData</managed-bean-name>
<managed-bean-class id="__8">com.im.popup.view.TreeTablePageModel</managed-bean-class>
<managed-bean-scope id="__6">view</managed-bean-scope>
</managed-bean>
Related
I would like to display a list of Accounts or Account wrappers in my VF page. I have a controller that works for a LWC and would like to add methods for a VF page, as well, that does pretty much the same thing. I have a very simple VF page that I nearly copied from the developer guide, and Salesforce wants to create a method that just returns a String, but I want to return a list of accounts. For now, I am just trying to pass in some fixed parameters. Can anyone tell me what is wrong here?
Page:
<apex:page docType="html-5.0" controller="AccountTypeAheadSearchHelper">
<h1>Account Type-Ahead Search Demo VF</h1>
<apex:outputLink value="{!URLFOR($Action.Account.View,'0018c00002N0qccAAB')}">Open this Account</apex:outputLink>
<apex:form >
<apex:actionFunction name="onKeyUpHandler" action="{!onKeyUpHandlerVF}">
<apex:param name="currentValue" assignTo="{!accountSearchVF}" value="" />
</apex:actionFunction>
<div>
<apex:inputText label="Search for account" id="theAccountName" value="{!accountSearchVF}" onkeyup="onKeyUpHandler(document.getElementById({!$Component.theAccountName}).nodeValue);" />
</div>
<apex:commandButton action="{!saveVF}" value="Save" id="theSaveButtonVF" />
</apex:form>
<apex:pageBlock title="Matching Accounts" mode="view">
<apex:dataList value="{!matchingAccountsVF}" var="matchingAccount" >
<apex:outputText value="{!matchingAccount.Name}" />
</apex:dataList>
</apex:pageBlock>
</apex:page>
Controller:
public with sharing class AccountTypeAheadSearchHelper {
private String accountSearchVFValue = 'Enter an account name';
/**
* Given a searchString, returns an array of MatchingAccountsWrappers for the LWC to consume
*
* #param searchString a part of the name of the account to search for
*
* #return an array of MatchingAccountsWrappers
*/
#AuraEnabled
public static MatchingAccountsWrapper[] getMatchingAccounts(String searchString, Boolean showContacts) {
String searchSpec = '%' + searchString + '%';
List<Account> accountsFound;
if (showContacts) {
accountsFound = [
SELECT Id, Name,
(SELECT Id, Name FROM Contacts ORDER BY Name)
FROM Account
WHERE Name LIKE :searchSpec
ORDER BY Name];
} else {
accountsFound = [
SELECT Id, Name
FROM Account
WHERE Name LIKE :searchSpec
ORDER BY Name];
}
List<MatchingAccountsWrapper> matchingAccounts = new List<MatchingAccountsWrapper>();
for (Account ma : accountsFound) {
MatchingAccountsWrapper mar = new MatchingAccountsWrapper(ma.Id, ma.Name, showContacts ? ma.Contacts: null);
matchingAccounts.add(mar);
system.debug('### matching account.name = ' + ma.Name);
}
return matchingAccounts;
}
public List<Account> getMatchingAccountsVF(String searchString, Boolean showContacts) {
String searchSpec = '%' + searchString + '%';
List<Account> accountsFound;
if (showContacts) {
accountsFound = [
SELECT Id, Name,
(SELECT Id, Name FROM Contacts ORDER BY Name)
FROM Account
WHERE Name LIKE :searchSpec
ORDER BY Name];
} else {
accountsFound = [
SELECT Id, Name
FROM Account
WHERE Name LIKE :searchSpec
ORDER BY Name];
}
return accountsFound;
}
public PageReference saveVF() {
system.debug('### AccountTypeAheadSearchHelper:saveVF() method');
return null;
}
public String accountSearchVF {
get {
system.debug('### AccountTypeAheadSearchHelper:accountSearchVF() getter: accountSearchVFValue = ' + accountSearchVFValue);
return accountSearchVFValue;
}
set {
system.debug('### AccountTypeAheadSearchHelper:accountSearchVF() setter: value = ' + value);
accountSearchVFValue = value;
}
}
public void onKeyUpHandlerVF() {
system.debug('### AccountTypeAheadSearchHelper:onKeyUpHandlerVF(): BEGIN');
system.debug('### AccountTypeAheadSearchHelper:onKeyUpHandlerVF(): accountSearchVFValue = ' + accountSearchVFValue);
MatchingAccountsWrapper[] mars = getMatchingAccounts(accountSearchVFValue, false);
system.debug('### AccountTypeAheadSearchHelper:onKeyUpHandlerVF(): END');
}
private class MatchingAccountsWrapper {
public MatchingAccountsWrapper(String k, String n) {
key = k;
name = n;
}
public MatchingAccountsWrapper(String k, String n, List<Contact> c) {
key = k;
name = n;
relatedContacts = c;
}
public MatchingAccountsWrapper(Account a) {
key = a.Id;
name = a.Name;
}
#AuraEnabled
public string key {get; set;}
#AuraEnabled
public string name {get; set;}
#AuraEnabled
public string link {get {
return URL.getSalesforceBaseUrl().toExternalForm() + '/' + this.key;
} set;}
private List<Contact> relatedContacts {get; set;}
#AuraEnabled
public List<MatchingContactsWrapper> contacts {get {
if (relatedContacts != null) {
List<MatchingContactsWrapper> matchingContacts = new List<MatchingContactsWrapper>();
for (Contact matchingContact : relatedContacts) {
MatchingContactsWrapper mac = new MatchingContactsWrapper(matchingContact);
matchingContacts.add(mac);
}
return matchingContacts;
} else {
return null;
}
} set;}
}
private class MatchingContactsWrapper {
public MatchingContactsWrapper(Contact c) {
key = c.Id;
name = c.Name;
}
#AuraEnabled
public string key {get; set;}
#AuraEnabled
public string name {get; set;}
#AuraEnabled
public string link {get {
return URL.getSalesforceBaseUrl().toExternalForm() + '/' + this.key;
} set;}
}
}
I think I really should be able to use the same getMatchingAccounts method that I use for the LWC, have also tried getMatchingAccountsVF. But it tells me that it doesn't exist in the controller. When I let Salesforce create the method, I makes a Public String method. I don't understand why it is making a String method.
The form part is not even relevant here, just the pageblock with the datalist in it. I would like to call "{!matchingAccountsVF('st', false)}" as a starting point and then pass in actual parameters from the user, but this doesn't work.
I am basing this on the very simple example I see here: https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_compref_dataList.htm
You can reuse Apex written for Aura/LWC in VF but the main difference is "state". Aura and LWC are stateless, you pass everything you need to server (or query it fresh) explicitly and the methods have to be static (btw your #AuraEnabled could really use cacheable=true). In Visualforce state is passed silently for you in a hidden encoded variable. All variables (except the ones declared transient) will be passed from the page to apex and class' internal state will be reconstructed.
You need a kind of autocomplete, right? You have few ways to do it. (Plus there are lots of examples on the net) Depends if you want it "pure Visualforce way" with that syntax or you're more comfortable with doing everything in JS.
completely reuse your component (read up about "Lightning Out")
call the javascript similar to LWC (as a static function, lightweight, mobile-friendly) with "Remoting". It's bit old and I guess you could say it's early version of Aura - JavaScript function, handle the response in JS (not VF), rebuild your list of accounts in JS.
call the server side code "old school visualforce" - with full resubmit of the form (or onKeyUp, doesn't matter). The key thing will be that parameter will be passed as part of that whole viewstate thing. And then your getMatchingAccountsVF doesn't need parameters, it becomes pure getter. It'll just rely on normal class variable with the search term.
public class Stack73082136 {
// Service part (static, useable in Aura/LWC but eventually maybe also as a REST service)
// This method would typically throw exceptions, perhaps AuraHandledException
#RemoteAction #AuraEnabled(cacheable=true)
public static MatchingAccountsWrapper[] getMatchingAccounts(String searchString, Boolean showContacts) {
String searchSpec = '%' + searchString + '%';
List<Account> accountsFound;
if (showContacts) {
accountsFound = [
SELECT Id, Name,
(SELECT Id, Name FROM Contacts ORDER BY Name)
FROM Account
WHERE Name LIKE :searchSpec
ORDER BY Name];
} else {
accountsFound = [
SELECT Id, Name
FROM Account
WHERE Name LIKE :searchSpec
ORDER BY Name];
}
List<MatchingAccountsWrapper> matchingAccounts = new List<MatchingAccountsWrapper>();
for (Account ma : accountsFound) {
MatchingAccountsWrapper mar = new MatchingAccountsWrapper(ma.Id, ma.Name, showContacts ? ma.Contacts: null);
matchingAccounts.add(mar);
system.debug('### matching account.name = ' + ma.Name);
}
return matchingAccounts;
}
// Visualforce part (old school, stateful)
// This would typically not throw exceptions but ApexPages.addMessage() etc.
// (which means that non-VF context like a trigger, inbound email handler or code called from Flow would crash and burn at runtime; in these you can only do exceptions)
public String searchValue {get;set;}
public List<MatchingAccountsWrapper> getMatchingAccountsVF() {
return getMatchingAccounts(searchValue, false);
}
public void onKeyUpHandlerVF() {
system.debug('### AccountTypeAheadSearchHelper:onKeyUpHandlerVF(): BEGIN');
system.debug('### AccountTypeAheadSearchHelper:onKeyUpHandlerVF(): accountSearchVFValue = ' + searchValue);
// do nothing. this method is "stupid", it's only job is to be called, pass the parameter and then the getMatchingAccountsVF
// will be called by VF engine when it needs to rerender {!matchingAccountsVF} expression
}
private class MatchingAccountsWrapper {
public MatchingAccountsWrapper(String k, String n) {
key = k;
name = n;
}
public MatchingAccountsWrapper(String k, String n, List<Contact> c) {
key = k;
name = n;
relatedContacts = c;
}
public MatchingAccountsWrapper(Account a) {
key = a.Id;
name = a.Name;
}
#AuraEnabled
public string key {get; set;}
#AuraEnabled
public string name {get; set;}
#AuraEnabled
public string link {get {
return URL.getSalesforceBaseUrl().toExternalForm() + '/' + this.key;
} set;}
private List<Contact> relatedContacts {get; set;}
#AuraEnabled
public List<MatchingContactsWrapper> contacts {get {
if (relatedContacts != null) {
List<MatchingContactsWrapper> matchingContacts = new List<MatchingContactsWrapper>();
for (Contact matchingContact : relatedContacts) {
MatchingContactsWrapper mac = new MatchingContactsWrapper(matchingContact);
matchingContacts.add(mac);
}
return matchingContacts;
} else {
return null;
}
} set;}
}
private class MatchingContactsWrapper {
public MatchingContactsWrapper(Contact c) {
key = c.Id;
name = c.Name;
}
#AuraEnabled
public string key {get; set;}
#AuraEnabled
public string name {get; set;}
#AuraEnabled
public string link {get {
return URL.getSalesforceBaseUrl().toExternalForm() + '/' + this.key;
} set;}
}
}
<apex:page docType="html-5.0" controller="Stack73082136">
<h1>Account Type-Ahead Search Demo VF</h1>
<apex:form >
<apex:pageBlock title="1. Total old school">
<apex:actionRegion>
<apex:inputText value="{!searchValue}" label="type and click" />
<apex:commandButton action="{!onKeyUpHandlerVF}" rerender="out1" />
<apex:outputPanel id="out1">
<apex:dataList value="{!matchingAccountsVF}" var="acc" >
<apex:outputText value="{!acc.Name}" />
</apex:dataList>
</apex:outputPanel>
</apex:actionRegion>
</apex:pageBlock>
<apex:actionFunction action="{!onKeyUpHandlerVF}" name="onKeyUpJS" rerender="out2">
<apex:param name="param1" assignTo="{!searchValue}" value="" />
</apex:actionFunction>
<apex:pageBlock title="2. Old school (viewstate, not mobile friendly) but at least onkeyup">
<!-- this is bit rubbish, fires immediately. Realistically you probably want some delay, wait till user stops typing. -->
<apex:actionRegion>
<apex:inputText value="{!searchValue}" label="type and wait" onkeyup="onKeyUpJS(this.value);"/>
<apex:outputPanel id="out2">
<apex:dataList value="{!matchingAccountsVF}" var="acc" >
<apex:outputText value="{!acc.Name}" />
</apex:dataList>
</apex:outputPanel>
</apex:actionRegion>
</apex:pageBlock>
<script>
function callRemote(term){
// call static method in ClassName.methodName format
Visualforce.remoting.Manager.invokeAction(
'{!$RemoteAction.Stack73082136.getMatchingAccounts}',
term,
false, // no contacts pls
function(result, event){
if (event.status) {
//debugger;
let target = document.getElementById("out3");
while (target.firstChild) {
target.removeChild(target.firstChild);
}
result.forEach(item => target.append(item.key + ': ' + item.name + ';'));
} else if (event.type === 'exception') {
document.getElementById("responseErrors").innerHTML =
event.message + "<br/>\n<pre>" + event.where + "</pre>";
} else {
document.getElementById("responseErrors").innerHTML = event.message;
}
},
{escape: true}
);
}
</script>
<apex:pageBlock title="3. VF remoting, the grandfather of Aura. No viewstate, pure html and js">
<input type="text" id="text3" label="type and vait v2" onkeyup="callRemote(this.value)" />
<div id="out3"></div>
<div id="responseErrors"></div>
</apex:pageBlock>
</apex:form>
</apex:page>
I have a column displaying author name and a command link "Show Documents". I want to expand the row on click of Show Documents command Link as happens on clicking detailStamp facet icon.
Here comes the JSFF code:
<af:table var="row" rowBandingInterval="0" id="rohitT"
value="#{pageFlowScope.parseData.AList}" rowSelection="multiple"
binding="#{pageFlowScope.parseData.dataTable}"
disclosedRowKeys="#{pageFlowScope.parseData.disclosedKeys}">
<af:column sortable="false" headerText="Author" id="c3">
<af:outputText value="#{row.author}" id="ot5"/>
<af:commandLink text="Show Documents" id="sd"
actionListener="#{pageFlowScope.parseData.prepareLOV}" partialSubmit="true" >
</af:commandLink>
<af:outputText value="Disclosed Key::#{pageFlowScope.parseData.disclosedKeys}"
id="ot6"/>
</af:column>
<af:column sortable="false" headerText="Subject area" id="c2">
<af:outputText value="#{row.subjectArea}" id="ot4"/>
</af:column>
<af:column sortable="false" headerText="Select" id="c1">
<af:selectBooleanCheckbox id="sbc1" selected="false" autoSubmit="true"
disabled="#{pageFlowScope.optionSelected ne 'Default' and pageFlowScope.optionSelected ne 'One'}" valueChangeListener="#{pageFlowScope.parseData.onChange}"/>
<af:outputText value="PageFlow::#{pageFlowScope.optionSelected}"
id="ot3"/></af:column>
<f:facet name="detailStamp" >
<af:group id="g1">
<af:selectOneListbox label="Label 1" id="sol1"
value="#{pageFlowScope.parseData.selectedValues}">
<f:selectItems value="#{pageFlowScope.parseData.allValuesList}"
id="si2"/>
</af:selectOneListbox>
</af:group>
</f:facet>
</af:table>
Here comes the managed bean code:
List selectedValues;
private RowKeySetImpl disclosedKeys=null;
List<SelectItem> allValuesList;
public void setDisclosedKeys(RowKeySetImpl disclosedKeys) {
this.disclosedKeys = disclosedKeys;
}
public void setAllValuesList(List<SelectItem> allValuesList) {
this.allValuesList = allValuesList;
}
public List<SelectItem> getAllValuesList() {
if (allValuesList == null) {
allValuesList = new ArrayList<SelectItem>();
allValuesList.add(new SelectItem(1, "India"));
allValuesList.add(new SelectItem(2, "Australia"));
allValuesList.add(new SelectItem(3, "America"));
allValuesList.add(new SelectItem(4, "United Kingdom"));
}
return allValuesList;
}
public void setSelectedValues(List selectedValues) {
this.selectedValues = selectedValues;
}
public List getSelectedValues() {
if (selectedValues == null) {
selectedValues = new ArrayList();
selectedValues.add(1);
selectedValues.add(3);
System.out.println("List is-" + selectedValues);
}
return selectedValues;
}
public void setDisclosedKeys(RowKeySetImpl disclosedKeys) {
this.disclosedKeys = disclosedKeys;
}
public RowKeySetImpl getDisclosedKeys() {
return disclosedKeys;
}
public void prepareLOV(ActionEvent actionEvent) {
disclosedKeys =null;
callDetailStamp();
RowKeySetImpl testDisclosedKey=getDisclosedKeys();
System.out.println("testDisclosedKey before refresh::"+testDisclosedKey);
AdfFacesContext.getCurrentInstance().addPartialTarget(getDataTable());
}
private void callDetailStamp() {
RowKeySet rks = getDataTable().getSelectedRowKeys();
Key currentKey = null;
Object objKey=null;
Iterator rksIter = rks.iterator();
while (rksIter.hasNext())
{
System.out.println("Entered Loop");
objKey=(Object)rksIter.next();
System.out.println("Obj Key::"+objKey);
}
if (rks.size()>0) {
System.out.println("Setting disclosed keys");
disclosedKeys = new RowKeySetImpl();
if (getDataTable() != null) {
ArrayList newKeys = new ArrayList();
ArrayList temp = new ArrayList();
temp.add(0,objKey);
newKeys.add(temp);
disclosedKeys.addAll(newKeys);
}
}
else
{
disclosedKeys = new RowKeySetImpl();
}
System.out.println("Disclosed Key::"+disclosedKeys.toString());
setDisclosedKeys(disclosedKeys);
}
I think I am able to set disclosedRowKeys property as I am able to print its value in output text. Whatever row I select, I get the disclosedKeyValue as an Array of index of the particular selected row. Clicking on link sets disclosedKeys value and refreshes the table but not expanding the row. Am I missing out something here ?
I'm trying to save and display a image for each contact individually. I was able to save the image successfully at respective contact. But, when i refreshed the page the photo i have attached is not displaying.
Here is the code:
<apex:page standardController="Contact" extensions="photo_attachmentcls">
<apex:pageBlock >
<apex:form >
<apex:inputFile value="{!attach}" fileName="{!fileName}"></apex:inputFile>
<apex:commandButton value="Load" action="{!loader}" />
<apex:actionSupport event="onchange" />
</apex:form>
<apex:outputPanel id="iidd1">
<img id="theImage" src="/servlet/servlet.FileDownload?file={!dsa}" width="100" height="100" />
</apex:outputPanel>
</apex:pageBlock>
</apex:page>
public class photo_attachmentcls {
public Attachment asd{get;set;}
public string purl{get;set;}
public blob attach{get;set;}
public String fileName{get;set;}
public Id recId{get;set;}
public String dsa {get;set;}
public photo_attachmentcls(ApexPages.StandardController ctrl) {
recId = ApexPages.currentPage().getParameters().get('id');
asd = new Attachment();
}
public void loader()
{
asd.body = attach;
asd.Name = fileName;
asd.ParentId = recId;
insert asd;
system.debug('nnnn'+asd);
dsa = asd.id;
system.debug('ddddd'+dsa);
}
}
Thanks in Advance.
You need to check for the existence of the attachment for that record when the page loads:
public class photo_attachmentcls {
public Attachment asd{get;set;}
public string purl{get;set;}
public blob attach{get;set;}
public String fileName{get;set;}
public Id recId{get;set;}
public String dsa {get;set;}
public photo_attachmentcls(ApexPages.StandardController ctrl) {
recId = ApexPages.currentPage().getParameters().get('id');
// check if the contact already has an attachment:
Contact thisRecord = [select id, (Select Id From NotesAndAttachments) from Contact where Id =: recId];
if(!thisRecord.NotesAndAttachments.isEmpty()){
dsa = thisRecord.NotesAndAttachments[0].Id;
} else{
asd = new Attachment();
}
}
public void loader()
{
asd.body = attach;
asd.Name = fileName;
asd.ParentId = recId;
insert asd;
system.debug('nnnn'+asd);
dsa = asd.id;
system.debug('ddddd'+dsa);
}
}
I'll recommend to choose better variable names as well, but I recognize is a prototype of some sort.
I am using Primefaces 4.0 with jsf2.2.
When I use dataTable with checkbox, it won't send back any record whenever I select records.
It supposed to send back the record once I click the checkbox.
I remove extra stuff and make my code simple to test it:
There is a polling printing selected items every second, so that I can check is there something sent back.
And after doing this, I found there's nothing sent back.
Here's my code:
Page:
<h:head>
<title>System Monitor</title>
</h:head>
<h:body>
<h:form>
<p:poll interval="1" listener="#{indexBean.printSelect()}"/>
</h:form>
<h:form>
<p:dataTable id='data' var="proc" value="#{indexBean.procStatus}"
rowKey="#{proc.pid}" selection="#{indexBean.selectedProcs}">
<p:column>
<f:facet name='header'>
<h:outputText value='Process Name'/>
</f:facet>
<h:outputText styleClass="outputCell" id="pname" value='#{proc.name}'/>
</p:column>
<p:column selectionMode="multiple"/>
</p:dataTable>
</h:form>
</h:body>
Backing Bean:
#ManagedBean(name = "indexBean")
#ViewScoped
public class indexBean implements Serializable {
private ProcStatDataModel procStatus;
private SingleProcess[] selectedProcs;
#PostConstruct
public void loadProcStat() {
List<SingleProcess> temp = new ArrayList<>();
temp.add(new SingleProcess("test1"));
temp.add(new SingleProcess("test2"));
temp.add(new SingleProcess("test3"));
procStatus = new ProcStatDataModel(temp);
}
public void printSelect() {
if (selectedProcs != null) {
String str = "";
for (SingleProcess sp : selectedProcs) {
str += sp.getName() + "_";
}
System.out.print(str);
} else {
System.out.println("selectedProcs is null");
}
}
public ProcStatDataModel getProcStatus() {
return procStatus;
}
public SingleProcess[] getSelectedProcs() {
return selectedProcs;
}
public void setSelectedProcs(SingleProcess[] selectedProcs) {
this.selectedProcs = selectedProcs;
}
}
I've tried attaching rowCheckListener like this before but in vain.
Also, I tried adding f:view contentType="text/html" like this and it was not help.
It's weird because I do the same thing with the case of primefaces show case and it acts as I think. So I think this approach is OK, there should be something wrong in my code.
Any help is appreciated. Thanks in advance.
I made a few changes and it worked. Not sure, which one is the solution.
1) Changed all the ' to ".
2) Datatable's value="#{indexBean.procStatus}" is wrong i think. I changed it to the name of the ArrayList inside the class. So, it became value="#{indexBean.procStatus.mylist}"
3) Added an ajax listener, like the one you mentioned in the question. <p:ajax event="rowSelectCheckbox" listener="#{indexBean.check}" />.
4) Added pid to the constructor which will be our rowKey.
5) Now, the poll prints the array.
Resultin xhtml is as follows:
<h:head>
<title>System Monitor</title>
</h:head>
<h:body>
<h:form>
<p:poll interval="1" listener="#{indexBean.printSelect()}"/>
</h:form>
<h:form>
<p:dataTable id="data" var="proc" value="#{indexBean.procStatus.mylist}"
rowKey="#{proc.pid}" selection="#{indexBean.selectedProcs}">
<p:ajax event="rowSelectCheckbox" listener="#{indexBean.check}" />
<p:column>
<f:facet name="header">
<h:outputText value="Process Name"/>
</f:facet>
<h:outputText styleClass="outputCell" id="pname" value="#{proc.name}"/>
</p:column>
<p:column selectionMode="multiple"/>
</p:dataTable>
</h:form>
</h:body>
</ui:composition>
Bean:
#ManagedBean(name = "indexBean")
#ViewScoped
public class indexBean implements Serializable {
private ProcStatDataModel procStatus;
private SingleProcess[] selectedProcs;
#PostConstruct
public void loadProcStat() {
List<SingleProcess> temp = new ArrayList<SingleProcess>();
temp.add(new SingleProcess("test1",1));
temp.add(new SingleProcess("test2",2));
temp.add(new SingleProcess("test3",3));
procStatus = new ProcStatDataModel(temp);
}
public void printSelect() {
if (selectedProcs != null) {
String str = "";
for (SingleProcess sp : selectedProcs) {
str += sp.getName() + "_";
}
System.out.print(str);
} else {
System.out.println("selectedProcs is null");
}
}
public ProcStatDataModel getProcStatus() {
return procStatus;
}
public SingleProcess[] getSelectedProcs() {
return selectedProcs;
}
public void setSelectedProcs(SingleProcess[] selectedProcs) {
this.selectedProcs = selectedProcs;
}
public void check(SelectEvent event) {
System.out.println("in check");
}
}
I am having a few struggles on how to wire up my interconnected comboboxes when using MVVM. I have a DTO that represents an Order which contains a CustomerId and an OrderTypeId. Thesere are then wrapped inside an OrderViewModel. I also have an EditOrderViewModel which loads from a db a list of Customers.
What I would like to do is Load an Order from the DB (similar to the Load function) choose the right item in the ComboBox (the items source of which is a List, display the name of the selected customer in a text block to the right of the combobox and finally load the list of OrderTypes that belong to that Customer in the next combobox and again select the correct OrderType and display the OrderTypeName in a TextBlock to the right.
I have managed to get some of the behaviour to work when I use SelectedItem from the combobox but this is only when I select the item manually as I am not sure how in my viewmodel I can convert Order.CustomerId (type int) into the correct SelectedItem (type CustomerDTO). Below is some code which shows generally what I am trying to achieve whilst using in-memory datasources. Thanks Alex
<ComboBox Height="25" Width="150" ItemsSource="{Binding Path=Customers}" SelectedValue="{Binding Path=Order.CustomerId}" SelectedValuePath="Id">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Code}"></TextBlock>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding Path=Name,Mode=OneWay,NotifyOnSourceUpdated=True}"></TextBlock>
public class EditOrderViewModel : VMBase
{
public OrderViewModel Order{get;set;}
public void Load()
{
Order = new OrderViewModel(new OrderDto{CustomerId=1,OrderTypeId=2});
Order.PropertyChanged += MainWindowViewModel_PropertyChanged;
}
public EditOrderViewModel()
{
Order = new OrderViewModel(new OrderDto());
Order.PropertyChanged += OrderViewModel_PropertyChanged;
Customers = new List<CustomerDto> {
new CustomerDto{ Id = 1, Code = "ACME", Name = "ACME CORP" },
new CustomerDto{ Id = 2, Code = "MSFT", Name="MICROSOFT CORP" },
new CustomerDto{ Id = 3, Code = "APP", Name = "APPLE" }};
OrderTypes = new List<OrderTypeDto>{
new OrderTypeDto{OrderTypeId=1, CustomerId =1, Name = "Cake Order"},
new OrderTypeDto{OrderTypeId=2, CustomerId =1, Name = "Sandwich Order"},
new OrderTypeDto{OrderTypeId=3, CustomerId =2, Name = "Chocolate Order"},
new OrderTypeDto{OrderTypeId=4, CustomerId =2, Name = "Bread Order"},
new OrderTypeDto{OrderTypeId=5, CustomerId =3, Name = "Drinks Order"}};
}
void OrderViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "OrderTypeId":
break;
case "SelectedCustomer":
break;
default:
break;
}
}
public List<OrderTypeDto> OrderTypes { get; set; }
public List<CustomerDto> Customers { get; set; }
}
public class OrderDto
{
public int CustomerId { get; set; }
public int OrderTypeId { get; set; }
}
public class CustomerDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
}
public class OrderViewModel : VMBase
{
private OrderDto _orderDto;
private string _customerName;
public OrderViewModel(OrderDto orderDto)
{
_orderDto = orderDto;
}
public int CustomerId {
get { return _orderDto.CustomerId; }
set
{
_orderDto.CustomerId = value;
RaisePropertyChanged("CustomerId");
}
}
public string CustomerName {
get { return _customerName; }
set {_customerName = value;
RaisePropertyChanged("CustomerName");
}
}
public int OrderTypeId
{
get { return _orderDto.OrderTypeId; }
set
{
_orderDto.OrderTypeId = value;
RaisePropertyChanged("OrderTypeId");
}
}
}
Don't set ComboBox.SelectedValue in your XAML binding. You should be binding ComboBox.SelectedItem to your model so you can have the CustomDTO easily available. You should add a property to your OrderViewModel called Customer (of type CustomerDTO) instead of trying to recreate the CustomerDTO using several properties (CustomerID, CustomerName, etc.).