5

Currently I have created a text box that allows the user to enter a comma delimited list of values that will be parsed and create new records en masse.

I am now trying to re-create this functionality, but by allowing input via multiple input boxes. Specifically, I would like to allow the user to dynamically add/remove new input boxes to the Visualforce layout and submit them all when a button is clicked.

This way, the controller can then create a new record for each input box that has a value in it rather than having to parse a comma-delimited list that may be incorrect.

Is this possible?

3

The most basic example will be something like the following (haven't tested it, just typed on the fly):

Controller:

public with sharing class YourClass
{   
    public class CustomAccount
    {
        public String accountName {get; set;}
    }

    public List <CustomAccount> customAccounts {get; set;}

    public YourClass()
    {
        customAccounts = new List <CustomAccount> ();
    }

    public void addNewRecord()
    {
        CustomAccount customAccount = new CustomAccount();
        customAccounts.add(customAccount);
    }

    public void insertAccounts()
    {
        List <Account> accounts = new List <Account> ();

        for (CustomAccount customAccount : customAccounts)
        {
            Account account = new Account();
            account.Name = customAccount.accountName;
            accounts.add(account);
        }

        insert accounts;
    }
}

Page:

<apex:page controller="YourClass">
    <apex:form id="form">
        <apex:repeat var="customAccount" value="{!customAccounts}">
            <apex:inputText value="{!customAccount.accountName}" />
        </apex:repeat>
        <apex:commandButton action="{!addNewRecord}" value="Add New" rerender="form" />
        <apex:commandButton action="{!insertAccounts}" value="Insert All" rerender="form" />
    </apex:form>
</apex:page>

It should get you started, at least to get the idea, then you can modify it as you need to.

1

VisualForce:

<apex:page standardController="Estimates__c" extensions="EstimatesExtension">
<apex:form>
    <apex:pageBlock title="Here you can fill the estimates" mode="edit">
        <apex:pageBlockButtons >
            <apex:commandButton action="{!save}" value="Save"/>
        </apex:pageBlockButtons>
        <apex:repeat value="{!listWrappers}" var="wrapper">
            <apex:pageBlockSection title="Estimates">
                <!-- "Rerender is not currently supported with rich text editing enabled" - so we can not use ajax -->
                <apex:inputField value="{!wrapper.estimate.Project__c}"/>
                <apex:inputField value="{!wrapper.estimate.Customer__c}"/>
                <apex:inputField value="{!wrapper.estimate.ContactPerson__c}"/>
                <apex:inputField value="{!wrapper.estimate.Status__c}"/>
                <apex:inputField value="{!wrapper.estimate.Notes__c}"/>
            </apex:pageBlockSection>
        </apex:repeat>

        <!--  immediate="true": execute action immediatly - without validation of fields - 
        so we can duplicate fields if on page some another required fields is unfilled -->
        <apex:commandButton value="Add new record" action="{!addEstimate}" immediate="true"/>
    </apex:pageBlock>
</apex:form>

apex, here I can not use ajax because I have richtext field that currently unsupported for ajax. Here we use wrapper-class (it is impossible not to use wrapper in such cases) because every adding of new duplicated inputs must reload the page - but during reload we do not save filled inputs to database (and without wrapper we will lose number or sObjects that we need to give to the Visualforce). This code have one problem - if you fill first form and add second - you will lose content of first form, so you can improve this:

public class EstimatesExtension {
    public WrapperEstimate wrapper {get; set;}
    public List<WrapperEstimate> listWrappers {get; set;}

    // here we init first block
    public EstimatesExtension(ApexPages.StandardController controller) {
        listWrappers = new List<WrapperEstimate>();
        wrapper = new WrapperEstimate();
        listWrappers.add(wrapper);
    }

    public class WrapperEstimate {
        public Estimates__c estimate {get; set;} // our target Estimate inside Wrapper
        public WrapperEstimate() {
            estimate = new Estimates__c(); // every Wrapper have Estimate
        }
    }

    public PageReference addEstimate() { // called from vf
        WrapperEstimate wrapper = new WrapperEstimate();
        listWrappers.add(wrapper); // vf render this list with new added wrapper
        return null;
    }

    public PageReference save() { // override
        List<Estimates__c> listEstimates = new List<Estimates__c>();
        for (WrapperEstimate wrapper : listWrappers) { // preparing new list of Estimates for insert
            listEstimates.add(wrapper.estimate);
        }
        insert listEstimates;
        return new PageReference('/' + Schema.getGlobalDescribe().get('Estimates__c').getDescribe().getKeyPrefix() + '/o'); // view all Estimates
    }
}
  • Hi Vitaly, welcome to SFSE! Can you please add some explanation outside of your code blocks? Thanks! – Adrian Larson May 24 '16 at 16:56
  • @AdrianLarson ok, I added some explanation. – Vitaly Zdanevich May 25 '16 at 6:29
  • This is awesome! – Maneesh Nov 6 '18 at 4:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.