为什么我的链接没有从使用JavaScript Remoting的Apex方法返回



我有以下代码,这些代码是我根据对Stackoverflow上另一个问题的回答改编的。我非常感谢@eyescream提供的答案。

我现在正试图显示一个指向帐户对象的链接,该链接是从JavaScript Remoting调用Apex方法返回的。我得到了名字和ID,但没有得到我要返回的URL。

这是页面:

<apex:page docType="html-5.0" controller="Stack73082136">
<h1>Account Type-Ahead Search Demo VF</h1>
<apex:form >
<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) => { console.log(item.name); console.log(item.link); target.append('<A href="' + item.link + '">' + item.name + '|' + item.link + '</A>' ) } );
} 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>

这是Apex:

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);
system.debug('#@# matching mar.name = ' + mar.name);
system.debug('#@# matching mar.link = ' + mar.link);
}
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
}
public 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调试日志显示链接属性已填充,但JavaScript console.log没有。当我搜索st:时,这就是我在页面上显示的内容

<A href="undefined">Express Logistics and Transport|undefined</A><A href="undefined">Pyramid Construction Inc.|undefined</A>

我确实发现,如果我像这样在构造函数中设置link的值,那么稍后只使用简单的link属性:

key = k;
name = n;
link = URL.getSalesforceBaseUrl().toExternalForm() + '/' + k;
}
public MatchingAccountsWrapper(String k, String n, List<Contact> c) {
key = k;
name = n;
relatedContacts = c;
link = URL.getSalesforceBaseUrl().toExternalForm() + '/' + k;
}
//        @AuraEnabled
//        global string link {get {
//            return URL.getSalesforceBaseUrl().toExternalForm() + '/' + this.key;
//        } set;}
@AuraEnabled
global string link {get; set;}

我不知道为什么这是必要的。它与此页面代码一起工作:

<apex:page docType="html-5.0" controller="Stack73082136">
<h1>Account Type-Ahead Search Demo VF</h1>
<apex:form >
<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) => {
console.log(item.name);
console.log(item.link); 
let a = document.createElement('a');
let text = document.createTextNode(item.name);
a.appendChild(text);
a.title = item.name;
a.href = item.link;
a.target = '_blank';
target.appendChild(a);
let br = document.createElement('br');
target.appendChild(br);
} );
} 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>

这是古老的互联网历史,我可能使用了错误的术语,不要杀死信使。在过去,Java被广泛认为是了不起的;(有一个关于豆子的概念。封装数据(但不封装行为(以在系统之间传递的简单对象。这个概念有点死胡同,但这个词作为POJO在某种程度上幸存了下来。

我的观点是,从Apex传递到Visualforce/Arra/LWC的东西必须很简单。JSON可序列化。纯数据。基元(integer、string、date(、集合(好吧,列表往往最有效(以及由这些基元构建的类的对象。理想情况下,字段标记为public。不要指望传递transientprivate和函数(behavio(u(r!(。

您的原始代码包含一个getter函数,而不是一个真正的字段。

public string link {get {
return URL.getSalesforceBaseUrl().toExternalForm() + '/' + this.key;
} set;}

是用于定义两个函数的简写语法,string getLink(),void setLink(字符串s(`。(无耻的插头:https://salesforce.stackexchange.com/a/9171/799,我2013年的回答(

由于它是一个函数,而不是一个真正的字段,所以在序列化和传递给JS的过程中丢失了它。JS不会自动调用Apex来运行它,只要你访问这个";字段"-那将是表演杀手。

让它成为普通的旧字段,并在Apex中设置它(就像您已经做过的那样(,或者在处理调用结果时在JS中动态创建字段。

最新更新