Displaying custom controller data in visualforce page after onchange event - salesforce

I have a picklist containing names of records of an object, "Test_Script".When I select any option from a picklist("selectlist" & selectoption are used for implementing picklist),at onchange event the other fields related to that record name should be displayed on visualforce page.
VF Page:
<h1>Choose Script:</h1>
<apex:selectlist value="{!selectedValue}" size="1"onchange="{!setValues}">
<apex:selectOptions value="{!scriptoptions}" />
</apex:selectlist>
<br/>
Executioner Name:
<outputfield value="{!valueResult.executioner_name}"/>
<br/>Planner Name:
<outputfield value="{!valueResult.planner_name}"/>
<br/>Reviewer Name:
<outputfield value="{!valueResult.reviewer_name}"/>
Controller:
public class ScriptAttributesController
{
public String setValues { get; set; }
public List<Test_script__c> scriptListWithValues = [select name, id, Executioner__c, Planner__c, Reviewer__c from Test_Script__c];
public static Test_Script__c valueResult=new Test_Script__c();
public String selectedValue {get;set;}
public void ScriptAttributesController()
{
}
public List<SelectOption> getScriptoptions()
{
List<SelectOption> options = new List<SelectOption>();
options.add(new SelectOption('select a value','select a value'));
for(Test_Script__c s: scriptListWithValues )
{
options.add(new SelectOption(s.id,s.name));
}
return options;
}
public void setValues()
{
valueResult=[select name, id, Executioner__c, Planner__c, Reviewer__c, Iteration__c from Test_Script__c where name='selectedValue' limit 1];
}
}
I am not able to see value on screen on change og picklist value

I would say that a getter is missing for your valueResult.
public Test_Script__c valueResult {get; set;}
and in the controller you can init the object.

Related

Why can't I return a List<Account> or an array of wrappers?

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>

Why can't I iterate through a list of a Wrapper class in Salesforce Visualforce?

I am trying to iterate through a list of records inside a wrapper class and show them on a Visualforce page. The custom object is called Campaign_Products__c, and the wrapper class is meant to show if the product has been selected by the user to add to a "cart".
Apex Controller code (extraneous bits removed):
public with sharing class CONTROLLER_Store {
...
public List<productOption> cpList { get; set; }
public class productOption {
public Campaign_Product__c product;
public Boolean inCart;
public Integer quantity;
}
...
public CONTROLLER_Store(){
...
List<Campaign> cmpList = getCampaignWithProducts(CampaignId,'');
// method above calls a campaign with a related list of Campaign Product records
if(cmpList.size() > 0){
cmp = cmpList[0];
cpList = new List<productOption>();
for(Campaign_Product__c pro : cmp.Campaign_Products__r){
productOption option = new productOption();
option.product = pro;
option.inCart = false;
option.quantity = 0;
cpList.add(option);
}
} else {
cmp = new Campaign();
CampaignId = null;
cpList = new List<productOption>();
}
....
}
Visualforce page (extraneous bits removed)
<apex:page controller="CONTROLLER_Store" >
<apex:repeat value="{! cpList }" var="option">
{!option.product.Product__r.Name}
<apex:inputCheckbox value="{! option.inCart }"/>
</apex:repeat>
</apex:page>
I get this error when trying to save the visualforce page:
Unknown property 'CONTROLLER_Store.productOption.product'
You need to make the properties in your wrapper visible to VF too. Something like
public class productOption {
public Campaign_Product__c product {get; private set};
public Boolean inCart {get; set};
public Integer quantity {get; set};
}
(assuming product should be readonly in VF). You need these access modifiers or full getter/setter methods.

How Filter ng-options base on property in array property of object

I'm Using ng-options for fill my comb-box.I want to filter comb-box base on property of option which is array of class, I want to show option that the property contain the value I want.
I have Two comb-box, One of them List of hotel, other is list of the user which has list of Hotel they have access. I want when option in Hotel comb-box change the user com-box option show users that in listofhotelsaccses has hotel that its Id is equal with selected Hotel or isadmin property is true.
in C# I can use
' lstUserVm.Where(q => q.LstAccessHotelVms.Any(x => x.HotelID == hotelid) || q.IsAdmin == true).ToList();'
in angular I am amateur. I don't know how to filter the list.
class AccessHotel
{
public int HotelID { get; set; }
public string HotelName { get; set; }
public bool AccessFlag { get; set; }
}
class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
public bool IsAdmin { get; set; }
public List<AccessHotelVm> LstAccessHotelVms { get; set; }
}
class Hotel
{
public int HotelID { get; set; }
public string HotelName { get; set; }
}
<select class="form-control " id="mHotel" name="mHotel" ng-model="mHotel" data-ng-options="Hotel.HotelID as Hotel.Name for Hotel in LstHotel"
></select>
<select class="form-control " id="mUser" name="mUser" ng-model="mUser" data-ng-options="user.Id as user.FullName for user in LstUser " ></select>
I want To Change in hotel result in option in User but I Don't know how to filter it In angular.
I used something like this for Country and State filtering in my Project.
<select ng-model="userState" ng-options="state.name for state in ( states | filter: {country: { code: userCountry.code }}) track by state.name">
Try this Fiddle:
It'll help.
Click to view Fiddle.
You can use ng-change directive on your first select to call a function when user selects a value.
<select class="form-control" ng-change="filterUsers()" id="mHotel" name="mHotel" ng-model="mHotel" data-ng-options="Hotel.HotelID as Hotel.Name for Hotel in LstHotel"></select>
inside your filterUsers function you can filter LstUser according to the value selected.
$scope.mHotel contains the selected value from your first select
$scope.LstUser is the list that your select is populated with
You can update LstUser according to the selected value to only show the relevant items.
$scope.filterUser = function(){
// write your filter logic here
}

MVC DropDownList from database

I am new in c# and MVC and this is my question:
For my new website, I would like to have a DropDownList in my header so the visitors can choose a Currency.
I have a SQL server table named CurrencyList
I have a Controller named BaseController : Controller
I have a model:
public partial class CurrencyList
{
public int CurrencyId { get; set; }
public string CurrencyName { get; set; }
public string CurrencySymbol { get; set; }
public Nullable<decimal> CurrencyValue { get; set; }
public string Currency { get; set; }
public Nullable<System.DateTime> LastUpdate { get; set; }
}
Now, how do I get the datas in the select? And how do I manage the Post?
For my select, I will use only the field Currency, and by default EUR will be selected.
Onchange, I would like to save the Currency value in a session and/or cookie.
Thanks in advance.
You can use Ajax Get method to call the method of controller which get data for bind dropdown list

How do I display the results of an aggregate SOQL query on a Visualforce page?

I'm very new to Visualforce.
I'm looking at this page here: http://force.siddheshkabe.co.in/2010/11/displaying-aggregate-result-on.html
So when I added this code onto a VisualForce page:
AggregateResult[] groupedResults = [SELECT Name, Days__c FROM Contact WHERE Days__c != ];
for (AggregateResult ar : groupedResults) {
System.debug('Name: ' + ar.get('Name') + '\nDays Taken : ' + ar.get('Days__c') + '\n');
But all it does is print the code instead of executing it. What should I be doing? Thanks for any guidance.
The Apex code goes into a custom controller or controller extension. The VisualForce page is a separate file from the controller. The page you referenced doesn't show the VF page. Also, I don't think you can bind VF components to AggregateResult, so you'll need a wrapper class.
Here's some working code.
Controller:
public with sharing class TestController {
public Summary[] Summaries { get; set; }
public TestController() {
AggregateResult[] results = [
SELECT Name, Count(Id) Quantity FROM Opportunity GROUP BY Name
];
Summaries = new List<Summary>();
for (AggregateResult ar : results) {
Summaries.add(new Summary(ar));
}
}
// wrapper class to hold aggregate data
public class Summary {
public Integer Quantity { get; private set; }
public String Name { get; private set; }
public Summary(AggregateResult ar) {
Quantity = (Integer) ar.get('Quantity');
Name = (String) ar.get('Name');
}
}
}
VF page:
<apex:page controller="TestController">
<apex:form >
<apex:repeat value="{!Summaries}" var="summary">
{!summary.Name}: {!summary.Quantity}<br/>
</apex:repeat>
</apex:form>
</apex:page>

Resources