Display value as returned from controller method - salesforce

Beginner in Salesforce so please bear with me.
I have created a lightning component and I would like to display on a page a value as returned by a component controller.
public class My_Controller {
#AuraEnabled
public static Decimal getRate(String currFrom, String currTo) {
Decimal value = 1.067773;
return value;
}
}
<aura:component controller="My_Controller">
<lightning:input type="string" name="res" aura:id="res" value= "
{!c.My_Controller.getRate('A', 'B')}" label="Result"/>
But it could not be so simple :) as I get: "Failed to save Rate.cmp: unexpected token: '(' at column 46 of expression: c.My_Controller.getRate('A', 'B'): Source"
What is the proper way to call the method?

You cannot call an Apex server controller method directly from Lightning clientside markup.
Instead, you'd need to declare an <aura:attribute> in your component markup and bind the value to that attribute.
<aura:attribute name="rate" type="String" />
<lightning:input type="string" name="res" aura:id="res" value="{! v.rate }" label="Result"/>
Then, your JavaScript client-side controller needs to make a server-side call, asynchronously, to get the value from Apex. Finally, the Lightning JavaScript callback from that async method would populate the return value into the <aura:attribute>, and the framework's data binding infrastructure will take care of updating the <lightning:input>.
It sounds complex, but it's mostly boilerplate code. The documentation linked above includes detailed examples.

public class My_Controller {
#AuraEnabled
public static Decimal getRate(String currFrom, String currTo) {
Decimal value = 1.067773;
return value;
}
}
<aura:component controller="My_Controller">
<aura:attribute name = "value" type= "Decimal"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<lightning:input type="string" name="res" aura:id="res" value= "
{!v.value}" label="Result"/>
<aura:component>
add a new method in controller.js:-
({
doInit : function(component, event, helper) {
var action = component.get("c.getRate");
action.setParams({
"currFrom": 'Test',
"currTo" : 'Test'
});
action.setCallback( this, function(actionResult) {
var state = actionResult.getState();
component.set('v.spinner',false);
if (state === "SUCCESS"){
var result = actionResult.getReturnValue();
component.set("v.value",result);
}
});
$A.enqueueAction(action);
}
})

Related

Custom Salesforce Lightning App giving "You dont have access to this record"

Custom Lightning App named "Stack" giving "You dont have access to this record Contact "
Trying to follow steps in How To Implement Full Search in Case Type using Salesforce?
Here is Org wide defaults of the custom object ERT Case Type data
Here is Apex code of stack.aspx
public class Stack {
#AuraEnabled(cacheable=true)
public static List<LookupSearchResult> search(String searchTerm, List<String> selectedIds){
if(String.isBlank(searchTerm) || searchTerm.length() < 2){
return null;
}
String t = '%' + searchTerm + '%'; // decide how you want to search, "starts with", "includes" or what
List<ERT_Case_Type_Data__c> records = [SELECT Id, Name, Level_1__c, Level_2__c, Level_3__c
FROM ERT_Case_Type_Data__c
WHERE Level_1__c LIKE :t OR Level_2__c LIKE :t OR Level_3__c LIKE :t
ORDER BY Level_1__c, Level_2__c, Level_3__c
LIMIT 20];
/* You could also experiment with SOSL?
records = [FIND :('*' + searchTerm + '*') IN ALL FIELDS
RETURNING Case_Type_Data__c(Id, Name, Level_1__c, Level_2__c, Level_3__c)][0];
*/
List<LookupSearchResult> results = new List<LookupSearchResult>();
for(ERT_Case_Type_Data__c ctd : records){
results.add(new LookupSearchResult(ctd.Id, 'ERT_Case_Type_Data__c', 'standard:case_wrap_up', ctd.Name,
String.join(new List<String>{ctd.Level_1__c , ctd.Level_2__c, ctd.Level_3__c}, '; ')
));
}
return results;
}
}
Here is Aura component(html part)
<aura:component implements="force:hasRecordId,force:appHostable,flexipage:availableForAllPageTypes,force:lightningQuickAction" access="global" controller="Stack">
<aura:attribute access="global" type="List" name="selection" default="[]"/>
<aura:attribute access="global" type="List" name="errors" default="[]"/>
<lightning:card title="New Case Type">
<lightning:recordEditForm aura:id="myForm" objectApiName="ERT_Case_Type__c" onsubmit="{!c.onSubmit}" onsuccess="{!c.onSuccess}">
<lightning:messages />
<c:Lookup selection="{!v.selection}" onSearch="{!c.lookupSearch}" onSelection="{!c.useSelected}" errors="{!v.errors}" label="Search" placeholder="Search Case Types Data"/>
<lightning:inputField aura:id="Level_1__c" fieldName="Level_1__c" />
<lightning:inputField aura:id="Level_2__c" fieldName="Level_2__c" />
<lightning:inputField aura:id="Level_3__c" fieldName="Level_3__c" />
<lightning:button class="slds-m-top_small" variant="brand" type="submit" name="save" label="Save" />
</lightning:recordEditForm>
</lightning:card>
</aura:component>
Here is Aura component - JS controller part
({
lookupSearch : function(component, event, helper) {
// Get the lookup component that fired the search event
const lookupComponent = event.getSource();
const serverSearchAction = component.get('c.search');
lookupComponent.search(serverSearchAction);
},
useSelected: function(component, event, helper) {
const selection = component.get('v.selection');
const errors = component.get('v.errors');
if (selection.length) {
if(errors.length){ // Clear errors, if any
component.set('v.errors', []);
}
let levels = selection[0].subtitle.split('; ');
component.find('Level_1__c').set('v.value', levels[0]);
component.find('Level_2__c').set('v.value', levels[1]);
component.find('Level_3__c').set('v.value', levels[2]);
}
},
onSubmit: function(component, event, helper) {
debugger;
event.preventDefault(); // stop the form from submitting
var fields = event.getParam('fields');
fields.Case__c = component.get('v.recordId'); // link to "this" Case
component.find('myForm').submit(fields);
},
onSuccess: function(component, event, helper){
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"message": "Case Type saved OK, refreshing",
"type": "success"
});
toastEvent.fire();
$A.get('e.force:refreshView').fire(); // reload page
}
})
Please help me in removing this access error
Regards,
Carolyn
I doubt it's sharing related (so not the org-wide settings). If it was sharing it'd simply always return 0 results but no big red errors.
If you remove the line with <c:Lookup selection="{!v.selection}" onSearch="{!c.lookupSearch}" onSelection="{!c.useSelected}" errors="{!v.errors}" label="Search" placeholder="Search Case Types Data"/> does the error go away? If it stays - it's problem with permissions around the custom code or Case_Type_Data__c). If it goes away - it's something with creating ERT_Case_Type__c)
Check the Profile (or Permission Sets if you use them) rights to:
Read the source object (Case_Type_Data__c) and all referenced fields (Level_1__c...)
Create the target object (ERT_Case_Type__c) and read/edit all referenced fields (Level1__c... but also Case__c)
Read on the Case object and Case.CaseNumber, Case.Subject fields
Permission to execute the Apex class (renamed to Stack, right?). And maybe add permission to run LookupSearchResult too.
I suspect you have enabled some critical updates (Setup -> Critical updates or Setup -> Release Updates) like "Restrict Access to #AuraEnabled Apex Methods for Authenticated Users Based on User Profile" or "Require Permission to View Record Names in Lookup Fields"

Unable to access merge field values inside visualforce component in angularJs

View:
<div ng-controller = "ClPortalRegistrationController">
<div ng-repeat="(key, value) in ObjectApiFieldsetMap">
{{key}} {{value}} //this is printing correct result
<c:FieldSetComponent objectAPIName="'{{key}}'" fieldSet="'{{value}}'" cid="'{{key}}'"
sectionTitle="Section 1" columns="2" textAlign="center"></c:FieldSetComponent>
</div>
</div>
Controller:
$scope.ObjectApiFieldsetMap = {
Applications__c: "Application_FieldSet_One",
clcommon__Collateral__c: "Collateral_FieldSet_One"
};
Now when I'm trying to access {{key}},{{value}} inside c:FieldSetComponent ,its only passing string as {{key}} and {{value}} and not the converted result. How can I access values stored inside key, value inside component?
Posting the solution which I implemented as a work around.
Turns out that you cannot access angular merge field values inside visualforce components. So instead of manipulating(segregating input into key-value pair) values inside angular controller, I have to push the logic to apex controller.
<apex:component controller="RegistrationController" access="global">
<apex:repeat value="{!ObjectApiFieldsetMap}" var="apiName">
<c:FieldSetComponent objectAPIName="{!apiName}" fieldSet="{!ObjectApiFieldsetMap[apiName]}"
cid="{!apiName}{!ObjectApiFieldsetMap[apiName]}"
columns="1" textAlign="center">
</c:FieldSetComponent>
</apex:repeat>
</apex:component>
And in my apex controller i.e RegistrationController , I have set the logic to segregate key values from a map input which I'm using inside visualforce component
global class RegistrationController {
global Map<String,String> ObjectApiFieldsetMap {
get {
ObjectApiFieldsetMap = arrangeApiFieldSetsByOrder();
return ObjectApiFieldsetMap;
}
set;
}
global Map<String,String> arrangeApiFieldSetsByOrder() {
Map<String,String> ObjectApiFieldsetMap = new Map<String,String>();
/* logic for segregation */
return ObjectApiFieldsetMap;
}
}

Unable to bind Component attribute with controller

I am trying to develop a visualforce custom component which takes an attribute from a visual force page. I need to access that attribute in controller's Constructor so that i can brings some records from database and i need to display those records in the Component. But problem is that i am not getting Attribute value in Controller.
See the below code to understand the problem clearly..
Controller :
public with sharing class AdditionalQuestionController {
public String CRFType {get;set;}
public AdditionalQuestionController () {
system.debug('CRFType : '+CRFType);
List<AdditoinalQuestion__c> lstAddQues = [Select AddQues__c from AdditoinalQuestion__c wehre CRFType = :CRFType];
system.debug('lstAddQue : '+lstAddQue);
}
}
Component :
<apex:component controller="AdditionalQuestionController" allowDML="true">
<apex:attribute name="CRFType" description="This is CRF Type." type="String" required="true" assignTo="{!CRFType}" />
<apex:repeat value="{!lstAddQue}" var="que">
{!que}<br />
</apex:repeat>
</apex:component>
VisualForce page :
<apex:page >
<c:AdditionalQuestionComponent CRFType="STE" />
</apex:page>
Thanks,
Vivek
I believe the issue here is that you're expecting the member variable to have a value inside the constructor — the snag is that the instance of the class is being constructed! It doesn't exist yet and so there is no way that a non-static member variable could be given a value prior.
Instead of doing the query in your constructor, specify your own getter for lstAddQue and do the query in there when you need the data. Of course, you may want to cache the value so that the query is not run every time, but from the looks of things that won't be relevant here.
Setter methods on the attributes in a VF component appear to be called after the constructor has returned, unfortunately. Here's an alternative solution for your controller that uses a getter method to populate your list (which would be called after your CRFType member variable has been set):
public with sharing class AdditionalQuestionController {
public String CRFType {set;}
public AdditionalQuestionController () {
system.debug('CRFType : '+CRFType); // this will be null in the constructor
}
public List<AdditoinalQuestion__c> getLstAddQue() {
system.debug('CRFType : '+CRFType); // this will now be set
List<AdditoinalQuestion__c> lstAddQues = [Select AddQues__c from AdditoinalQuestion__c wehre CRFType = :CRFType];
system.debug('lstAddQue : '+lstAddQue);
return lstAddQue;
}
}

How to bind form to model in NancyFX

I'm new to NancyFX and trying to simply bind a posted form to my model.
In the module when trying to access the posted values I run following statement:
string email = this.Context.Request.Form["Email"];
Debug.WriteLine(email);
Result is:
"Nancy.DynamicDictionaryValue" instead of posted value
Can anybody tell me what newbie mistake I'm doing:
The form looks like:
<form method="post" action="account">
<input type="text" id="Email" />
<input type="password" id="Password" />
<input type="submit" value="Create" />
</form>
the routing in Module contructor:
Post["/"] = parameters => CreateAccount(parameters);
The dynamic dictionary returns a dynamic value, if you cast it to a string (implicitly or explicitly) you'll get what you want, or just use the build in model binder https://github.com/NancyFx/Nancy/wiki/Model-binding
Just adding to the correct answer above in the hope it is useful to nancy-newbies like me.
Because the Nancy Form and Query are of type dynamic you can access the values using the name of the form or query-string param (see terms and max in the example code). I use a simple base class just to make the syntax terser throughout the rest of my modules.
Note: The ExpandoObject Model in the base class is there so I can just throw values at my view-model and not have to worry about cluttering things up with strongly typed data-transfer classes (this also helps prevent exposing any secret domain instance data).
public class SearchModule : _BaseModule
{
public SearchModule(ISearchService searchService)
{
Get["/search"] = _ =>
{
if (!Query.terms.HasValue) return HttpStatusCode.BadRequest;
var terms = (string) Query.terms;
var max = (Query.max.HasValue) ? (int) Query.max : 3;
Model.SearchResults = searchService.GetResults(max, terms);
...
};
}
}
public class _BaseModule : NancyModule
{
protected dynamic Model = new ExpandoObject();
public dynamic Query { get { return Request.Query; } }
public dynamic Form { get { return Request.Form; } }
}

inputField databinding issue with VisualForce

I'm having strange results with a Visualforce page (yes, Salesforce.com is icky, I know). My issue is that I am trying to use the inputField to bind data on a custom sObject, but in my custom controller, it is not recognizing the user input data.
Here is the code snippet from the page:
<apex:pageBlockSection title="Enter New Fee" rendered="{!isRenderedFees}" >
<apex:inputField value="{!workingFee.Fee_Type__c}" required="True"/>
<apex:inputField value="{!workingFee.Fee__c}" required="True"/>
<apex:pageBlockSectionItem >
<apex:CommandButton value="Save Fee" action="{!saveFee}" immediate="true" />
<apex:CommandButton value="Cancel" action="{!cancelFee}" immediate="true" />
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
and here is the code from the controller:
public Fee__c workingFee {get; set;}
....
public PageReference saveFee(){
this.workingFee.Trade_Group__c = tradeGroup.id;
try{
System.debug('~~~~#~~#~~workingFee: '+workingFee.Fee_Type__c +'='+workingFee.Fee__c);
upsert workingFee;
}catch (System.Dmlexception e){
ApexPages.addMessages(e);
return null;
}
System.debug('~~~~#~~#~~workingFee: '+workingFee.Fee_Type__c +'='+workingFee.Fee__c);
//savedFees.add(workingFee.clone());
//re-render the page
this.isRenderedFees = False;
return null;
}
I've made sure the workingFee property is not null. Whenever I hit the "Save Fee" button after entering the values, it reloads the page and gives me the message "Error: Required fields are missing: [Fee__c]" (note, Fee__c here is a currency field -- it's not that it expects this to be an sObject, is it?)
The debug statement in the saveFee() method shows that workingFee's important fields are null, when I would expect them to have been assigned the values input by the user.
I have had a whole heap of issues binding controls to a property exposed with the simple { get; set; } notation... The rest of your code will see the properties, but for some bizarre reason, your View won't (always) bind...
Try writing explicit get/set methods, like
private workingFee;
public Fee__c getWorkingFee() {
return workingFee;
}
public void setWorkingFee(Fee__c value) {
workingFee = value;
}
There is no logical reason why this should work any different to
public Fee__c workingFee { get; set; }
but in my experience, it sometimes does...
what did you say about it being icky? ;)
Immediate=false/true has bitten me quite a few times too. Good catch Ian.

Resources