2

I'm working on a project that requires the users to define multiple texts. These texts will be related to (for instance) an Account record. Based on which record it's linked to some merge-fields need to be filled in. Basically exactly the same as an e-mailtemplate, except that the source text is a custom field on a custom object.

Example: I have a custom merge text, which reads: "Hello {!Account.Name}, your total store credit has been altered to {!Account.Store_Credit__r.value}."

After the text is related to a specific account it should read: "Hello GenePoint, your total store credit has been altered to 250."

The text will be rendered on a VF page or maybe it'll be retrieved through the API. Is there any way to use merge-fields like this in Apex, without having to parse the text and replace them manually? I can't use formula fields, since the source texts are going to be maintained by non-technical people that do not know how to use formula's.

2
  • I think a text-field that accepts merge fields like a formula field would be technically more difficult for a user to use then a formula field. The formula field editor has a UI which doesnt require the user to understand merge fields, has built in error handling if they define something incorrectly, and also includes a list of functions with definition if they wanted to do something more complex. Train the users, dont rebuild formula fields.
    – Phil Rymek
    Commented Nov 17, 2013 at 16:31
  • Actually, I'm working on a WYSISYG-editor that has an option to insert merge-fields. So there will be an interface, it was never an option to let the user type in the merge-fields by hand. Commented Nov 17, 2013 at 17:14

2 Answers 2

2

I think theGreatDanton's answer is pretty good, and will probably be useable in most cases. For some reason I can't get it to work though, so I created my own little replacer:

public static String MergeText(String source, SObject obj){
    String s = '';      
    if(source != null && obj != null){
        Integer curPos = 0;
        Pattern mergeField = Pattern.compile('\\{![a-zA-Z0-9_\\.]*\\}');
        Matcher m = mergeField.matcher(source);
        while(m.find()){
            //add the piece of text from the end of the previous match
            //until the start of this one.
            s += source.substring(curPos, m.start());
            //get the actual field name (without {! })
            String fieldName = source.substring(m.start()+2,m.end()-1);
            //split the fieldname to get the different relations as parts
            String[] relations = fieldName.split('\\.');

            try {
                //iterate over the relations to get to the object that's needed.
                SObject tmp = obj;
                for(Integer i=0; i<relations.size()-1; i++){
                    tmp = tmp.getSObject(relations[i]);
                }
                //now get the value of the field
                s += tmp.get(relations[relations.size()-1]);
            } catch(Exception ex){
                s += '[Invalid merge field: ' + m.group() + ']';
            }
            curPos = m.end();
        }
        //add the end of the original string
        s += source.substring(curPos);
    }
    return s;
}

One major drawback of this solution (I don't know if this also applies for theGreatDanton's method) is that any field used in the text should be available on the apex page that it's rendered on. So if you would use the Phone field of Account in the custom text, but it's not referenced on the VF page, you should add <apex:outputText value="{!Account.Phone}" rendered="false" /> to the VF page, just like you would when you only use a field in the controller.

1

You can use the dynamic VF component to construct the string based (including the merge fields) on a value entered by User.

Apex Class:

    public class dynamicVF{
    public account acc{get;set;}
    public innerClass inner{get;set;}

    public dynamicVF(){
        acc = [select id, name, phone from account limit 1];
        inner = new innerClass();
    }

    public class innerClass{

        public component.apex.outputText getOutputText(){
            custom_object__c cont = [SELECT custom_field__c from custom_Object__c where filterTheCorrectRecord'];
            component.Apex.OutputText oText = new component.Apex.OutputText();
            //set this value dynamically.
            oText.expressions.value=cont.custom_field__c ;
            return oText;

        }
    }
}

VF page:

<apex:page controller="dynamicVF">
    <apex:dynamicComponent componentValue="{!inner.OutputText}"/>
</apex:page>
6
  • Thanks for your answer. In my case this isn't working, since the value I want to give the outputText is dynamic. It's from a custom field on a custom object. I already wrote a merge-field replacer, if you're interested I can share it with you. Commented Nov 18, 2013 at 7:57
  • updated my answer so that the field are picked up dynamically. Please share your approach as well as that will definitely help others who might get the same problem. :)
    – Sam
    Commented Nov 18, 2013 at 8:16
  • Strangely enough, your solution doesn't work for me. I get empty output, regardless of the input text. This only happens if I put the function that gets the outputtext inside in inner class, otherwise I do get output. Commented Nov 18, 2013 at 8:20
  • are you using with sharing keyword on the inner class?
    – Sam
    Commented Nov 18, 2013 at 8:21
  • Updated again with inner class
    – Sam
    Commented Nov 18, 2013 at 8:40

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .