Регистрация  |  Вход

Salesforce pagination custom controller example

Всем привет. Нужна помощь опытных программистов.

Задача (тестовое задание).

(удалено по просьбе правообладателя)

На текущем этапе, ПОЖАЛУЙСТА помогите разобраться, как правильно сделать пагинацию в кастомном контроллере?

У кого такое же задание или кто может помочь и сидит в ВК, пожалуйста пишите в личку vk.com/domovikx любой помощи пуду рад, спасибо заранее!

Всем привет. Нужна помощь опытных программистов. 

Задача (тестовое задание).

(удалено по просьбе правообладателя)

На текущем этапе, ПОЖАЛУЙСТА помогите разобраться, как правильно сделать пагинацию в кастомном контроллере?

У кого такое же задание или кто может помочь и сидит в ВК, пожалуйста пишите в личку [url=https://vk.com/domovikx]vk.com/domovikx[/url] любой помощи пуду рад, спасибо заранее!

Вот тут есть пример
https://salesforce-developer.ru/paginatsiya-na-visualforce-stranitse-pagination-using-standardsetcontroller

gitlab.com/domovikx/tx002
Да, классно уже народ подсказал, тут мои текущие коды, когда закончу ТЗ, выложу, что получилось на форуме в чистовом варианте.

[url=https://gitlab.com/domovikx/tx002]gitlab.com/domovikx/tx002[/url]
Да, классно уже народ подсказал, тут мои текущие коды, когда закончу ТЗ, выложу, что получилось на форуме в чистовом варианте.

Промежуточный этап. Пагинация + сортировка готовы и работают.


<apex:page controller="CustomSorting181112Controller">

<!--
Вариант с сортировкой по столбцу
'SELECT Id, Name, Email, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate '

Необходимо отобразить следующие поля:
1 Name (link), 2 Email, 3 Contact Level (picklist), 4 Account (lookup),
5 Owner (lookup), 6 Created By (lookup), 7 Created Date.
-->

<apex:form>
<apex:pageBlock id="thisBlock">


<!-- табличка -->
<apex:pageBlockTable value="{! Contact }" var="ct" id="thisTable">

<apex:column value="{! ct.Name }">
<apex:facet name="header">
<apex:commandLink action="{! sortByName }" reRender="thisBlock">
<apex:outputText value="1. {! $ObjectType.Contact.Fields.Name.Label }" />
</apex:commandLink>
</apex:facet>
</apex:column>

<apex:column value="{! ct.Email }">
<apex:facet name="header">
<apex:commandLink action="{! sortByEmail }" reRender="thisBlock">
<apex:outputText value="2. {! $ObjectType.Contact.Fields.Email.Label }" />
</apex:commandLink>
</apex:facet>
</apex:column>

<apex:column value="{! ct.Contact_Level__c }">
<apex:facet name="header">
<apex:commandLink action="{! sortByContactLevel }" reRender="thisBlock">
<apex:outputText value="3. {! $ObjectType.Contact.Fields.Contact_Level__c.Label }" />
</apex:commandLink>
</apex:facet>
</apex:column>

<apex:column value="{! ct.AccountId }">
<apex:facet name="header">
<apex:commandLink action="{! sortByAccountId }" reRender="thisBlock">
<apex:outputText value="4. Accounts" />
</apex:commandLink>
</apex:facet>
</apex:column>

<apex:column value="{! ct.OwnerId }">
<apex:facet name="header">
<apex:commandLink action="{! sortByOwnerId }" reRender="thisBlock">
<apex:outputText value="5. Owners" />
</apex:commandLink>
</apex:facet>
</apex:column>

<apex:column value="{! ct.CreatedById }">
<apex:facet name="header">
<apex:commandLink action="{! sortByCreatedById }" reRender="thisBlock">
<apex:outputText value="6. Created By" />
</apex:commandLink>
</apex:facet>
</apex:column>

<apex:column value="{! ct.CreatedDate }">
<apex:facet name="header">
<apex:commandLink action="{! sortByCreatedDate }" reRender="thisBlock">
<apex:outputText value="7. {! $ObjectType.Contact.Fields.CreatedDate.Label }" />
</apex:commandLink>
</apex:facet>
</apex:column>

</apex:pageBlockTable>


<!-- кнопочки вперед-назад... -->
<div align="center" id="button">
<apex:commandButton action="{! setList.first }" value=" << " title="First Page" disabled="{!!setList.HasPrevious}" reRender="thisBlock,button"
/>
<apex:commandButton action="{! setList.previous }" value=" Previous " disabled="{!!setList.HasPrevious}" reRender="thisBlock,button"
/>
<apex:commandButton action="{! setList.next }" value=" Next > " disabled="{!!setList.HasNext}" reRender="thisBlock,button"
/>
<apex:commandButton action="{! setList.last }" value=" >> " title="Last Page" disabled="{!!setList.HasNext}" reRender="thisBlock,button"
/>

<!-- поле - колво страниц -->
<span style="float:right">
<apex:outputLabel value=" Page " />
<apex:InputText value="{! PageNumber }" maxLength="4" size="1" />
<apex:outputLabel value=" of {! TotalPages }" />
</span>

</div>

<div>
<!-- выпадающий список - типо пагинация -->
<span style="float:right">
<apex:SelectList value="{! PageSize }" size="1">
<apex:selectOptions value="{! PageSizeList }" />
<apex:actionSupport event="onchange" reRender="thisBlock" />
</apex:SelectList>
</span>
</div>

<!-- ссылка - создать новый аккаунт -->
<apex:outputLink value="{!URLFOR($Action.Contact.NewContact)}" target="_blank">
Create New Contact
</apex:outputLink>

</apex:pageBlock>

</apex:form>
</apex:page>

public class CustomSorting181112Controller {

public String sortingColumn = 'Name'; // первичный столбец
public String sortingOrder = ' ASC '; // первичная сортировка
public String column = sortingColumn; // первичное значение нового столбца

public String query = 'SELECT Id, Name, Email, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate ' +
'FROM Contact ' +
'ORDER BY ' + sortingColumn + sortingOrder;


// Сортировка начинается здесь ---------------------------------------
// вначале пишем методы которые будут вызываться командой со стринички
public void sortByName() {
sortingColumn = 'Name';
sortingOrder();
}
public void sortByEmail() {
sortingColumn = 'Email';
sortingOrder();
}
public void sortByContactLevel() {
sortingColumn = 'Contact_Level__c';
sortingOrder();
}
public void sortByAccountId() {
sortingColumn = 'AccountId';
sortingOrder();
}
public void sortByOwnerId() {
sortingColumn = 'OwnerId';
sortingOrder();
}
public void sortByCreatedById() {
sortingColumn = 'CreatedById';
sortingOrder();
}
public void sortByCreatedDate() {
sortingColumn = 'CreatedDate';
sortingOrder();
}

// проверяем- новый столбец или тот же
// если столбец новый сортировка - ASC и обновляется запрос.
public void sortingOrder() {
if (sortingColumn == column) {
sortingOrder = (sortingOrder == ' ASC ') ? ' DESC ' : ' ASC ';
} else {
sortingOrder = ' ASC ';
column = sortingColumn;
}
// получаем наш запрос с нужной нам сортировкой, аминь!
query = 'SELECT Id, Name, Email, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate ' +
'FROM Contact ' +
'ORDER BY ' + sortingColumn + sortingOrder;

// после сортировки ОБЯЗАТЕЛЬНО создается новый экземпляр контроллера с новыми значениями
setList = new ApexPages.StandardSetController(Database.query(query));
}
// конец сортировки --------------------------------------


// Пагинация начинается здесь ---------------------------------
// Вначале делаем выпадающий список - количество записей на странице
public list<SelectOption> getPageSizeList(){ // выпадающий список на странице
list<SelectOption> options = new list<SelectOption>();
options.add(new selectOption('5','5'));
options.add(new selectOption('10','10'));
options.add(new selectOption('25','25'));
options.add(new selectOption('50','50'));
options.add(new selectOption('100','100'));
return options;
}
public ApexPages.StandardSetController setList {
get {
if (setList == null) { // проверка на наличие экземпляра контроллера
setList = new ApexPages.StandardSetController(Database.query(query));
}
if (this.PageSize == null) PageSize = 10; // дефаултное значение записей/стр, попробуй 7 или 8 =)
setList.setPageSize(PageSize); // количество записей/страница
return setList;
} set;
}
public Integer PageSize {
get; set { // Выбираем количество записей на странице
if(value != null) this.PageSize = value;
}
}
public Integer PageNumber {
get { // получаем текущий номер страницы
this.PageNumber = setList.getPageNumber();
return this.PageNumber;
}
set { // это чтобы перейти к введенному номеру страницы
setList.setPageNumber(value); //
}
}
public Integer TotalPages { // Считаем количество страниц
// это я скопировал у других, кто знает как это упростить пишите. это работает
get {
if (setList.getResultSize() <= 10)
this.TotalPages = 1;
if (Math.Mod ( setList.getResultSize(),setList.getPageSize() ) == 0)
this.TotalPages = ( setList.getResultSize()/setList.getPageSize() );
else this.TotalPages = ( setList.getResultSize()/setList.getPageSize() )+1;
return totalpages;
}
set;
}
public List<Contact> getContact() {
return (List<Contact>) setList.getRecords();
}
// Конец пагинации ----------------------------------------


} // конец контроллера =)


Промежуточный этап. Пагинация + сортировка готовы и работают.

[size=50]
[code]<apex:page controller="CustomSorting181112Controller">

    <!-- 
Вариант с сортировкой по столбцу
'SELECT Id, Name, Email, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate '

Необходимо отобразить следующие поля: 
1 Name (link), 2 Email, 3 Contact Level (picklist), 4 Account (lookup), 
5 Owner (lookup), 6 Created By (lookup), 7 Created Date.
-->

    <apex:form>
        <apex:pageBlock id="thisBlock">


            <!-- табличка  -->
            <apex:pageBlockTable value="{! Contact }" var="ct" id="thisTable">

                <apex:column value="{! ct.Name }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByName }" reRender="thisBlock">
                            <apex:outputText value="1. {! $ObjectType.Contact.Fields.Name.Label }" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

                <apex:column value="{! ct.Email }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByEmail }" reRender="thisBlock">
                            <apex:outputText value="2. {! $ObjectType.Contact.Fields.Email.Label }" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

                <apex:column value="{! ct.Contact_Level__c }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByContactLevel }" reRender="thisBlock">
                            <apex:outputText value="3. {! $ObjectType.Contact.Fields.Contact_Level__c.Label }" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

                <apex:column value="{! ct.AccountId }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByAccountId }" reRender="thisBlock">
                            <apex:outputText value="4. Accounts" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

                <apex:column value="{! ct.OwnerId }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByOwnerId }" reRender="thisBlock">
                            <apex:outputText value="5. Owners" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

                <apex:column value="{! ct.CreatedById }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByCreatedById }" reRender="thisBlock">
                            <apex:outputText value="6. Created By" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

                <apex:column value="{! ct.CreatedDate }">
                    <apex:facet name="header">
                        <apex:commandLink action="{! sortByCreatedDate }" reRender="thisBlock">
                            <apex:outputText value="7. {! $ObjectType.Contact.Fields.CreatedDate.Label }" />
                        </apex:commandLink>
                    </apex:facet>
                </apex:column>

            </apex:pageBlockTable>


            <!-- кнопочки вперед-назад...  -->
            <div align="center" id="button">
                <apex:commandButton action="{! setList.first }" value=" << " title="First Page" disabled="{!!setList.HasPrevious}" reRender="thisBlock,button"
                />
                <apex:commandButton action="{! setList.previous }" value=" Previous " disabled="{!!setList.HasPrevious}" reRender="thisBlock,button"
                />
                <apex:commandButton action="{! setList.next }" value=" Next > " disabled="{!!setList.HasNext}" reRender="thisBlock,button"
                />
                <apex:commandButton action="{! setList.last }" value=" >> " title="Last Page" disabled="{!!setList.HasNext}" reRender="thisBlock,button"
                />

                <!-- поле - колво страниц -->
                <span style="float:right">
                    <apex:outputLabel value=" Page " />
                    <apex:InputText value="{! PageNumber }" maxLength="4" size="1" />
                    <apex:outputLabel value=" of {! TotalPages }" />
                </span>

            </div>

            <div>
                <!-- выпадающий список - типо пагинация -->
                <span style="float:right">
                    <apex:SelectList value="{! PageSize }" size="1">
                        <apex:selectOptions value="{! PageSizeList }" />
                        <apex:actionSupport event="onchange" reRender="thisBlock" />
                    </apex:SelectList>
                </span>
            </div>

            <!-- ссылка - создать новый аккаунт -->
            <apex:outputLink value="{!URLFOR($Action.Contact.NewContact)}" target="_blank">
                Create New Contact
            </apex:outputLink>

        </apex:pageBlock>

    </apex:form>
</apex:page>[/code]

[code]public class CustomSorting181112Controller {

public String sortingColumn = 'Name'; // первичный столбец
public String sortingOrder = ' ASC '; // первичная сортировка
public String column = sortingColumn; // первичное значение нового столбца

public String query = 'SELECT Id, Name, Email, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate ' +
                      'FROM Contact ' +
                      'ORDER BY ' + sortingColumn + sortingOrder;


// Сортировка начинается здесь ---------------------------------------
// вначале пишем методы которые будут вызываться командой со стринички
public void sortByName() {
	sortingColumn = 'Name';
	sortingOrder();
}
public void sortByEmail() {
	sortingColumn = 'Email';
	sortingOrder();
}
public void sortByContactLevel() {
	sortingColumn = 'Contact_Level__c';
	sortingOrder();
}
public void sortByAccountId() {
	sortingColumn = 'AccountId';
	sortingOrder();
}
public void sortByOwnerId() {
	sortingColumn = 'OwnerId';
	sortingOrder();
}
public void sortByCreatedById() {
	sortingColumn = 'CreatedById';
	sortingOrder();
}
public void sortByCreatedDate() {
	sortingColumn = 'CreatedDate';
	sortingOrder();
}

// проверяем- новый столбец или тот же
// если столбец новый сортировка - ASC и обновляется запрос.
public void sortingOrder() {
	if (sortingColumn == column) {
		sortingOrder = (sortingOrder == ' ASC ') ? ' DESC ' : ' ASC ';
	} else {
		sortingOrder = ' ASC ';
		column = sortingColumn;
	}
	// получаем наш запрос с нужной нам сортировкой, аминь!
	query = 'SELECT Id, Name, Email, Contact_Level__c, AccountId, OwnerId, CreatedById, CreatedDate ' +
	        'FROM Contact ' +
	        'ORDER BY ' + sortingColumn + sortingOrder;

	// после сортировки ОБЯЗАТЕЛЬНО создается новый экземпляр контроллера с новыми значениями
	setList = new ApexPages.StandardSetController(Database.query(query));
}
// конец сортировки --------------------------------------


// Пагинация начинается здесь ---------------------------------
// Вначале делаем выпадающий список - количество записей на странице
public list<SelectOption> getPageSizeList(){ // выпадающий список на странице
	list<SelectOption>  options = new list<SelectOption>();
	options.add(new selectOption('5','5'));
	options.add(new selectOption('10','10'));
	options.add(new selectOption('25','25'));
	options.add(new selectOption('50','50'));
	options.add(new selectOption('100','100'));
	return options;
}
public ApexPages.StandardSetController setList {
	get {
		if (setList == null) { // проверка на наличие экземпляра контроллера
			setList = new ApexPages.StandardSetController(Database.query(query));
		}
		if (this.PageSize == null) PageSize = 10; // дефаултное значение записей/стр, попробуй 7 или 8 =)
		setList.setPageSize(PageSize); // количество записей/страница
		return setList;
	} set;
}
public Integer PageSize {
	get; set { // Выбираем количество записей на странице
		if(value != null) this.PageSize = value;
	}
}
public Integer PageNumber {
	get { // получаем текущий номер страницы
		this.PageNumber = setList.getPageNumber();
		return this.PageNumber;
	}
	set { // это чтобы перейти к введенному номеру страницы
		setList.setPageNumber(value); //
	}
}
public Integer TotalPages { // Считаем количество страниц
// это я скопировал у других, кто знает как это упростить пишите. это работает
	get {
		if (setList.getResultSize() <= 10)
			this.TotalPages = 1;
		if (Math.Mod ( setList.getResultSize(),setList.getPageSize() ) == 0)
			this.TotalPages = ( setList.getResultSize()/setList.getPageSize() );
		else this.TotalPages = ( setList.getResultSize()/setList.getPageSize() )+1;
		return totalpages;
	}
	set;
}
public List<Contact> getContact() {
	return (List<Contact>) setList.getRecords();
}
// Конец пагинации ----------------------------------------


} // конец контроллера =)[/code]
[/size]

Текущий этап.
Как добавить столбец с кнопками - Edit Del
Как добавить поиск.
Возможно поможет этот код: https://developer.salesforce.com/forums/?id=9060G000000UVLGQA4

Текущий этап.
Как добавить столбец с кнопками - Edit Del 
Как добавить поиск.
Возможно поможет этот код: https://developer.salesforce.com/forums/?id=9060G000000UVLGQA4

На вопрос "КАК СДЕЛАТЬ" сложно дать ответ. Существует 100500 способов сделать одну и ту же штуку.
Это основы! Советую сначала погуглить тему создания CRUD приложение для Salesforce.
Если уже не будет получаться, можно детальнее пообщаться на тему "ПОЧЕМУ".

К тому же тут на форуме присутствуют представители различных компаний и в том числе той куда ты планируешь попасть сделав тестовое задание. Возможность находить решения в интернете и адаптировать их под свои требования ценится не меньше чем наличие знаний

На вопрос "[b]КАК СДЕЛАТЬ[/b]" сложно дать ответ. Существует 100500 способов сделать одну и ту же штуку. 
Это основы! Советую сначала погуглить тему создания CRUD приложение для Salesforce. 
Если уже не будет получаться, можно детальнее пообщаться на тему "[b]ПОЧЕМУ[/b]".

К тому же тут на форуме присутствуют представители различных компаний и в том числе той куда ты планируешь попасть сделав тестовое задание. Возможность находить решения в интернете и адаптировать их под свои требования ценится не меньше чем наличие знаний :) 

На форуме уже не раз обсуждались эти вопросы.
Можно "delete" через поиск поискать.
Вот к примеру
https://salesforce-developer.ru/forum/topic-delete-function
Где упоминается
http://salesforcesource.blogspot.com/2009/09/edit-and-delete-command-for-your.html
С полностью готовым решением.

На форуме уже не раз обсуждались эти вопросы.
Можно "delete" через поиск поискать.
Вот к примеру 
https://salesforce-developer.ru/forum/topic-delete-function
Где упоминается
http://salesforcesource.blogspot.com/2009/09/edit-and-delete-command-for-your.html
С полностью готовым решением.

На счет поиска тоже упоминалось не раз.
Вот к примеру поиск выдал
https://salesforce-developer.ru/forum/topic-nuzhna-pomosch-1ac98fe6-a206-493a-ada3-a5b48d6465da

На счет поиска тоже упоминалось не раз.
Вот к примеру поиск выдал
https://salesforce-developer.ru/forum/topic-nuzhna-pomosch-1ac98fe6-a206-493a-ada3-a5b48d6465da

https://salesforce.stackexchange.com/questions/115757/soql-like-and-wildcards-not-returning-full-results
Как писать динамический запрос для поиска.
Примеры тут:

String query = 'SELECT Id, Name FROM Account WHERE name LIKE \'%' + searchVar + '%\'';
List<Account> accts = Database.query(query);

List<Account> accts = [SELECT Id, Name FROM Account WHERE name LIKE :('%' + searchName + '%')];

https://salesforce.stackexchange.com/questions/115757/soql-like-and-wildcards-not-returning-full-results
Как писать динамический запрос для поиска. 
Примеры тут:
[code]String query = 'SELECT Id, Name FROM Account WHERE name LIKE \'%' + searchVar + '%\'';
List<Account> accts = Database.query(query);[/code]
[code]List<Account> accts = [SELECT Id, Name FROM Account WHERE name LIKE :('%' + searchName + '%')];[/code]

Второй вариант предпочтительнее из-за правильного биндинга переменных в запрос.
В первом случае можно словить SOQL Injection если правильно не проверить переменную searchVar.
Это советы на будущее.

Второй вариант предпочтительнее из-за [b]правильного[/b] биндинга переменных в запрос.
В первом случае можно словить SOQL Injection если правильно не проверить переменную searchVar.
Это советы на будущее.

Спасибо за примеры. Помогло. Сегодня сделал колонку с edit / del
особенно помог пример:
http://salesforcesource.blogspot.com/2009/09/edit-and-delete-command-for-your.html

текущие рабочие коды можно посмотреть тут: https://gitlab.com/domovikx/tx002/tree/master

Следующая задача
- Сделать кнопку ‘New Contact’,
- которая откроет popup на той же странице,
- где можно будет ввести: First Name, Last Name *, Email *, Contact Level, Account
- и нажать Save или Cancel

Кто можешь подсказать ресурсы, которые почитать? Благодарю заранее!

Спасибо за примеры. Помогло. Сегодня сделал колонку с edit / del
особенно помог пример:
http://salesforcesource.blogspot.com/2009/09/edit-and-delete-command-for-your.html

текущие рабочие коды можно посмотреть тут: https://gitlab.com/domovikx/tx002/tree/master

[b]Следующая задача[/b] 
- Сделать кнопку ‘New Contact’, 
- которая откроет popup на той же странице, 
- где можно будет ввести: First Name, Last Name *, Email *, Contact Level, Account
- и нажать Save или Cancel

Кто можешь подсказать ресурсы, которые почитать? Благодарю заранее!

DomovikX
- которая откроет popup на той же странице,

Если хочется именно popup то это уже надо смотреть в сторону Javascript. Честно даже не представляю как это сделать с помощью чистого VF.

[quote="DomovikX"]- которая откроет popup на той же странице, [/quote]
Если хочется именно popup то это уже надо смотреть в сторону Javascript. Честно даже не представляю как это сделать с помощью чистого VF. 

Итак это popup in salesforce решается с помощью модальных окон.
Create a Modal Popup in Salesforce

для контроллера

// Наш попап
public boolean displayPopup {get; set;}
public void popupShow(){
displayPopup = true;
}
public void popupClose(){
displayPopup = false;
}
// конец попапа

для страницы

<!-- Popup -->
<apex:commandButton value="Create New Contact Popup" action="{!popupShow}" rerender="popupPanel" />
<apex:outputPanel id="popupPanel">
<apex:outputPanel styleClass="popupBackground" layout="block" rendered="{!displayPopUp}" />
<apex:outputPanel styleClass="popupCust" layout="block" rendered="{!displayPopUp}">

<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Лучший в мире заголовок</h2>
<p> Clicking on this link you can express your financial gratitude to the developer of this application in dollars equivalent.
<a href="https://vk.com/domovikx" target="_blank">Your developer DomovikX</a>.</p>

<apex:commandButton value="Close" action="{!popupClose}" rerender="popupPanel" />
</apex:outputPanel>
</apex:outputPanel>

<style type="text/css">
.popupCust {
background-color: white;
border-width: 0px;
border-style: solid;
z-index: 9001;
left: 50%;
padding: 11px;
position: absolute;
width: 600px;
margin-left: -240px;
top: 100px;
}

.popupBackground {
background-color: black;
opacity: 0.20;
filter: alpha(opacity=20);
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 9000;
}
</style>

Итак это popup in salesforce решается с помощью модальных окон.
[url=https://www.forcetalks.com/blog/create-a-modal-popup-in-salesforce/]Create a Modal Popup in Salesforce[/url]

для контроллера
[code]// Наш попап
public boolean displayPopup {get; set;}
public void popupShow(){
	displayPopup = true;
}
public void popupClose(){ 
	displayPopup = false;
}
// конец попапа [/code]

для страницы
[code]<!-- Popup -->
<apex:commandButton value="Create New Contact Popup" action="{!popupShow}" rerender="popupPanel" />
<apex:outputPanel id="popupPanel">
    <apex:outputPanel styleClass="popupBackground" layout="block" rendered="{!displayPopUp}" />
    <apex:outputPanel styleClass="popupCust" layout="block" rendered="{!displayPopUp}">

        <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Лучший в мире заголовок</h2>
        <p> Clicking on this link you can express your financial gratitude to the developer of this application in dollars equivalent.
            <a href="https://vk.com/domovikx" target="_blank">Your developer DomovikX</a>.</p>

        <apex:commandButton value="Close" action="{!popupClose}" rerender="popupPanel" />
    </apex:outputPanel>
</apex:outputPanel>

    <style type="text/css">
        .popupCust {
            background-color: white;
            border-width: 0px;
            border-style: solid;
            z-index: 9001;
            left: 50%;
            padding: 11px;
            position: absolute;
            width: 600px;
            margin-left: -240px;
            top: 100px;
        }

        .popupBackground {
            background-color: black;
            opacity: 0.20;
            filter: alpha(opacity=20);
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            z-index: 9000;
        }
    </style>[/code]

От млин уже за столько лет и позабыл про "rerender". Только хотел поругаться что для открытия окна перезагружать страницу сильно нерационально. А оказывается вот оно как Перезагрузки нет, но все равно задержка присутствует. Не самый оптимальный вариант, но полностью классический для Visualforce.

В принципе раз на бэкенде нет никакой хитрой логики на открытие окна то лучше заморочиться css + jquery.

От млин уже за столько лет и позабыл про "rerender". Только хотел поругаться что для открытия окна перезагружать страницу сильно нерационально. А оказывается вот оно как :) Перезагрузки нет, но все равно задержка присутствует. Не самый оптимальный вариант, но полностью классический для Visualforce.

В принципе раз на бэкенде нет никакой хитрой логики на открытие окна то лучше заморочиться css + jquery.

Пользуясь случаем.
Как написать кнопку для открытия ссылки в новой странице. Имитация ссылки под кнопку.

<!-- Это имитация ссылки под кнопку Create New Contact -->
<apex:commandLink target="_blank" styleClass="btn" style="text-decoration:none; padding:4px; float:right" action="{!URLFOR($Action.Contact.NewContact)}"
value="Create New Contact linke" />
<!-- аналог в виде ссылки
<apex:outputLink value="{!URLFOR($Action.Contact.NewContact)}" target="_blank" style="font-weight:bold; float:right">
Create New Contact
</apex:outputLink>
-->

Люди добрый, подскажите, как сделать:
поля First Name, Last Name (обязательное), Email (обязательное с проверкой на емайл), Contact Level (список), Account
и кнопки Save или Cancel

Сейчас разбираю это. Спасибо за ссылки на инфу.

Пользуясь случаем.
Как написать кнопку для открытия ссылки в новой странице. Имитация ссылки под кнопку. 
[code]<!-- Это имитация ссылки под кнопку Create New Contact -->
<apex:commandLink target="_blank" styleClass="btn" style="text-decoration:none; padding:4px; float:right" action="{!URLFOR($Action.Contact.NewContact)}"
    value="Create New Contact linke" />
<!-- аналог в виде ссылки
<apex:outputLink value="{!URLFOR($Action.Contact.NewContact)}" target="_blank" style="font-weight:bold; float:right">
    Create New Contact
</apex:outputLink>
-->[/code]

[b]Люди добрый, подскажите, как сделать:[/b]
поля First Name, Last Name (обязательное), Email (обязательное с проверкой на емайл), Contact Level (список), Account
и кнопки Save или Cancel

Сейчас разбираю это. Спасибо за ссылки на инфу.

https://salesforce.stackexchange.com/questions/180172/using-custom-controller-to-create-new-contact
https://success.salesforce.com/answers?id=9063A000000e1U2QAI

Сейчас хочу попробовать вот этот вариант - Using custom controller to create new Contact

Вот кривой но рабочий код. работает ровно на одну запись. Как доделаю, перезалью пост.

<apex:page controller="ContactCreateController">
<!--
- 1.First Name, 2.Last Name *, 3.Email *, 4.Contact Level, 5.Account
- кнопки - Save или Cancel
- * обязательные
- Contact Level - список: Primary, Secondary, Tertiary
-->
<apex:pageBlock>
<apex:form>
<apex:pageBlockSection columns="1">
<apex:inputText label="1. First Name" value="{!firstName}" />
<apex:inputText label="2. Last Name" value="{!lastName}" />
<apex:inputText label="Phone" value="{!phone}" />
<apex:commandButton value="Save" action="{!save}" />
</apex:pageBlockSection>
</apex:form>
</apex:pageBlock>
</apex:page>

public class ContactCreateController {

public String firstName {get; set;}
public String lastName {get; set;}
public String phone {get; set;}
list <contact> conList;

public ContactCreateController(){
conlist=new List<contact>();
}

public pageReference save(){
Contact con = new Contact(firstname = firstname, lastname = lastname, phone = phone);
conList.add(con);
insert conList;
return null;
}

}

https://salesforce.stackexchange.com/questions/180172/using-custom-controller-to-create-new-contact
https://success.salesforce.com/answers?id=9063A000000e1U2QAI

Сейчас хочу попробовать вот этот вариант - Using custom controller to create new Contact

Вот кривой но рабочий код. работает ровно на одну запись. Как доделаю, перезалью пост.
[code]
<apex:page controller="ContactCreateController">
    <!--  
- 1.First Name, 2.Last Name *, 3.Email *, 4.Contact Level, 5.Account 
- кнопки - Save или Cancel 
- * обязательные 
- Contact Level - список: Primary, Secondary, Tertiary 
-->
    <apex:pageBlock>
        <apex:form>
            <apex:pageBlockSection columns="1">
                <apex:inputText label="1. First Name" value="{!firstName}" />
                <apex:inputText label="2. Last Name" value="{!lastName}" />
                <apex:inputText label="Phone" value="{!phone}" />
                <apex:commandButton value="Save" action="{!save}" />
            </apex:pageBlockSection>
        </apex:form>
    </apex:pageBlock>
</apex:page>
[/code]

[code]
public class ContactCreateController {

public String firstName {get; set;}
public String lastName {get; set;}
public String phone {get; set;}
list <contact> conList;

public ContactCreateController(){
	conlist=new List<contact>();
}

public pageReference save(){
	Contact con = new Contact(firstname = firstname, lastname = lastname, phone = phone);
	conList.add(con);
	insert conList;
	return null;
}

}
[/code]

Немнго оптимизации.
DML операции работают не только с List, но и с одиночными записями.
в строке 15 можно написать

insert con;

и тогда не нужны будут 6, 9, 14

Немнго оптимизации.
DML операции работают не только с List, но и с одиночными записями.
в строке 15 можно написать

insert con;

и тогда не нужны будут 6, 9, 14

Как и обещал. Код для добавления нового контакта+аккаунт.
Добавляется в окно попап.
Код полностью можно посмотреть на гите:
https://gitlab.com/domovikx/tx002/tree/master

Код для страницы

<!-- Popup -->
<apex:commandButton value=" Create a New Contact " action="{!popupShow}" rerender="popupPanel"/>
<apex:outputPanel id="popupPanel">
<apex:outputPanel styleClass="popupBackground" layout="block" rendered="{!displayPopUp}" />
<apex:outputPanel styleClass="popupCust" layout="block" rendered="{!displayPopUp}">

<h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Create a New Contact</h2>
<p align="center"> Clicking on this link you can express your financial gratitude to the developer of this application
in dollars equivalent. Your developer
<a href="https://vk.com/domovikx" target="_blank" style="font-weight:bold"> VK.com/DomovikX </a>.</p>


<!-- Create New Contact-->
<!--
- 1.First Name, 2.Last Name *, 3.Email *, 4.Account
- 5.Contact Level,
- кнопки - Save или Cancel
- * обязательные
- Contact Level - список: Primary, Secondary, Tertiary
-->
<p>
<apex:pageBlockSection columns="2">
<apex:inputText label="1. First Name" value="{!newFirstName}" />
<apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />
<apex:inputText label="3. E-mail * " value="{!newEmail}" required="true" />
<apex:inputText label="4. Phone" value="{!newPhone}" />
<apex:inputText label="5. Account" value="{!newAccount}" />

<apex:SelectList label="6. Contact Level" size="1" value="{!newContactLevel}">
<apex:selectOptions value="{!ContactLevel}" />
</apex:SelectList>

</apex:pageBlockSection>
<p align="right"><span class="colorTextBold">*</span> - Required fields</p>
<p align="center">
<apex:commandButton value="Save" action="{!save}" rerender="popupPanel" />
<apex:commandButton value="Close" immediate="true" html-formnovalidate="formnovalidate" action="{!popupClose}" rerender="popupPanel"/>
</p>
</p>
<apex:pageMessages />
<!-- сообщение об ощибках сюда -->
</apex:outputPanel>
</apex:outputPanel>

код для контроллера

// Добавление нового контакта --------------------------------
public String newFirstName {get; set;} // 1
public String newLastName {get; set;} // 2
public String newEmail {get; set;} // 3
public String newPhone {get; set;} // 4
public String newAccount {get; set;} // 5

public String newContactLevel {get; set;} // 6 . Сетим выбранное в списке
public list<SelectOption> getContactLevel(){ // Гетим наш список
list<SelectOption> ContactLevel = new list<SelectOption>();
ContactLevel.add(new SelectOption('Primary','Primary'));
ContactLevel.add(new SelectOption('Secondary','Secondary'));
ContactLevel.add(new SelectOption('Tertiary','Tertiary'));
return ContactLevel;
}

// памятка по API name
// https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_contact.htm
public pageReference save(){

if (newAccount != '') { // если строчка Аккаунта имеет данные

try { // пробуем добавить данные в БД
Account acc = new Account(Name = newAccount);
insert acc; // Вначале добавляем аккаунт, чтобы потом взять его ID

Contact con = new Contact(
FirstName = newFirstName, LastName = newLastName, Email = newEmail,
Phone = newPhone, Contact_Level__c = newContactLevel, AccountId = acc.ID);
insert con;
}
catch (DMLException e) {
ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Error creating new contact.'));
return null;
}
}
else { // если строчка Аккаунта пуста

try {
Contact con = new Contact(
FirstName = newFirstName, LastName = newLastName, Email = newEmail,
Phone = newPhone, Contact_Level__c = newContactLevel);
insert con;
}
catch (DMLException e) {
ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Error creating new contact.'));
return null;
}
}

ApexPages.addMessage(new ApexPages.message(ApexPages.severity.CONFIRM,'New contact "'+newFirstName+' '+newLastName+'" successfully created.'));
newFirstName=''; // возвращаем значения поумолчанию
newLastName='';
newEmail='';
newPhone='';
newAccount='';
newContactLevel='Primary';

//popupClose(); // если понадобится закрытие окошка сразу после создания
return null;
}
// -----------------------------------------------------------

Как и обещал. Код для добавления нового контакта+аккаунт.
Добавляется в окно попап. 
Код полностью можно посмотреть на гите: 
https://gitlab.com/domovikx/tx002/tree/master

Код для страницы
[code]
                <!-- Popup -->
                <apex:commandButton value=" Create a New Contact " action="{!popupShow}" rerender="popupPanel"/>
                <apex:outputPanel id="popupPanel">
                    <apex:outputPanel styleClass="popupBackground" layout="block" rendered="{!displayPopUp}" />
                    <apex:outputPanel styleClass="popupCust" layout="block" rendered="{!displayPopUp}">

                        <h2 id="modal-heading-01" class="slds-text-heading_medium slds-hyphenate">Create a New Contact</h2>
                        <p align="center"> Clicking on this link you can express your financial gratitude to the developer of this application
                            in dollars equivalent. Your developer
                            <a href="https://vk.com/domovikx" target="_blank" style="font-weight:bold"> VK.com/DomovikX </a>.</p>


                        <!-- Create New Contact-->
                        <!--  
- 1.First Name, 2.Last Name *, 3.Email *, 4.Account 
- 5.Contact Level,
- кнопки - Save или Cancel 
- * обязательные 
- Contact Level - список: Primary, Secondary, Tertiary 
-->
                        <p>
                            <apex:pageBlockSection columns="2">
                                <apex:inputText label="1. First Name" value="{!newFirstName}" />
                                <apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />
                                <apex:inputText label="3. E-mail * " value="{!newEmail}" required="true" />
                                <apex:inputText label="4. Phone" value="{!newPhone}" /> 
                                <apex:inputText label="5. Account" value="{!newAccount}" />                                

                                <apex:SelectList label="6. Contact Level" size="1" value="{!newContactLevel}">
                                    <apex:selectOptions value="{!ContactLevel}" />
                                </apex:SelectList>

                            </apex:pageBlockSection>
                            <p align="right"><span class="colorTextBold">*</span> - Required fields</p>
                            <p align="center">
                                <apex:commandButton value="Save" action="{!save}" rerender="popupPanel" />
                                <apex:commandButton value="Close" immediate="true" html-formnovalidate="formnovalidate" action="{!popupClose}" rerender="popupPanel"/>
                            </p>
                        </p>
                        <apex:pageMessages />
                        <!-- сообщение об ощибках сюда -->
                    </apex:outputPanel>
                </apex:outputPanel>
[/code]

код для контроллера
[code]
// Добавление нового контакта --------------------------------
public String newFirstName {get; set;}         // 1
public String newLastName {get; set;}         // 2
public String newEmail {get; set;}         // 3
public String newPhone {get; set;}         // 4
public String newAccount {get; set;}         // 5

public String newContactLevel {get; set;}         // 6 . Сетим выбранное в списке
public list<SelectOption> getContactLevel(){         // Гетим наш список
	list<SelectOption> ContactLevel = new list<SelectOption>();
	ContactLevel.add(new SelectOption('Primary','Primary'));
	ContactLevel.add(new SelectOption('Secondary','Secondary'));
	ContactLevel.add(new SelectOption('Tertiary','Tertiary'));
	return ContactLevel;
}

// памятка по API name
// https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_objects_contact.htm
public pageReference save(){

	if (newAccount != '') { // если строчка Аккаунта имеет данные

		try { // пробуем добавить данные в БД
			Account acc = new Account(Name = newAccount);
			insert acc; // Вначале добавляем аккаунт, чтобы потом взять его ID

			Contact con = new Contact(
				FirstName = newFirstName, LastName = newLastName, Email = newEmail,
				Phone = newPhone, Contact_Level__c = newContactLevel, AccountId = acc.ID);
			insert con;
		}
		catch (DMLException e) {
			ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Error creating new contact.'));
			return null;
		}
	}
	else { // если строчка Аккаунта пуста

		try {
			Contact con = new Contact(
				FirstName = newFirstName, LastName = newLastName, Email = newEmail,
				Phone = newPhone, Contact_Level__c = newContactLevel);
			insert con; 
		}
		catch (DMLException e) {
			ApexPages.addMessage(new ApexPages.message(ApexPages.severity.ERROR,'Error creating new contact.'));
			return null;
		}
	}

	ApexPages.addMessage(new ApexPages.message(ApexPages.severity.CONFIRM,'New contact "'+newFirstName+' '+newLastName+'" successfully created.'));
	newFirstName='';                 // возвращаем значения поумолчанию
	newLastName='';
	newEmail='';
	newPhone='';
	newAccount='';
	newContactLevel='Primary';

	//popupClose(); // если понадобится закрытие окошка сразу после создания
	return null;
}
// -----------------------------------------------------------
[/code]

Ура. Спасибо форуму, и в особенности Димке за источники.
Мое ТЗ готово. Остались Юнит тесты.
В результате получилось следующее.

Ура. Спасибо форуму, и в особенности Димке за источники. 
Мое ТЗ готово. Остались Юнит тесты.
В результате получилось следующее.
[img]https://pp.userapi.com/c850220/v850220709/79921/BYAg9ssjaYE.jpg[/img]
[img]https://pp.userapi.com/c850220/v850220709/79918/8Lcb-IYBs7s.jpg[/img]

Поздравляю с небольшой победой. Смотрится красиво!
Маленький совет для улучшения UI. Панель Errors обычно выводят НАД формой.
Помести ее между приветствием и формой и будет вообще супер!

Поздравляю с небольшой победой. Смотрится красиво! 
Маленький совет для улучшения UI. Панель Errors обычно выводят НАД формой.
Помести ее между приветствием и формой и будет вообще супер!

Илья, при таком

<apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />

подходе у меня при незаполненном поле, проверка выдает
j_id0:form:j_id6: Validation Error: Value is required
Как сделать нормальным?

Илья, при таком
[code]<apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />[/code]
подходе у меня при незаполненном поле, проверка выдает
[color=red]j_id0:form:j_id6: Validation Error: Value is required[/color]
Как сделать нормальным?

SMLG
Илья, при таком
<apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />

подходе у меня при незаполненном поле, проверка выдает
j_id0:form:j_id6: Validation Error: Value is required
Как сделать нормальным?

Так и правильно выдает.
Last Name - Это поле поумолчанию стоит как обязательное, даже если required="false", оно надо.
А вот e-mail - делается обязательным полем.

Остальной код с правками на гите: https://gitlab.com/domovikx/tx002/tree/master.
Пока открыт, потом возможно попросят закрыть. Если недоступен, значит закрыл.

[quote="SMLG"]Илья, при таком
[code]<apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />[/code]
подходе у меня при незаполненном поле, проверка выдает
[color=red]j_id0:form:j_id6: Validation Error: Value is required[/color]
Как сделать нормальным?[/quote]

Так и правильно выдает. 
Last Name - Это поле поумолчанию стоит как обязательное, даже если required="false", оно надо. 
А вот e-mail - делается обязательным полем.

Остальной код с правками на гите: https://gitlab.com/domovikx/tx002/tree/master. 
Пока открыт, потом возможно попросят закрыть. Если недоступен, значит закрыл.

DomovikX
public ApexPages.StandardSetController setList {
get {
if (setList == null) { // проверка на наличие экземпляра контроллера
setList = new ApexPages.StandardSetController(Database.query(query));
}
if (this.PageSize == null) PageSize = 10; // дефаултное значение записей/стр, попробуй 7 или 8 =)
setList.setPageSize(PageSize); // количество записей/страница
return setList;
} set;
}

public ApexPages.StandardSetController setList {
get {
if (setList == null) { // проверка на наличие экземпляра контроллера
setList = new ApexPages.StandardSetController(Database.query(query));
}
if (this.PageSize == null) PageSize = 10; // дефаултное значение записей/стр, попробуй 7 или 8 =)
setList.setPageSize(PageSize); // количество записей/страница
return setList;
} set;
}

Как в тесте "запустить" setList, чтобы тест зашел в него?

[quote="DomovikX"]public ApexPages.StandardSetController setList {
	get {
		if (setList == null) { // проверка на наличие экземпляра контроллера
			setList = new ApexPages.StandardSetController(Database.query(query));
		}
		if (this.PageSize == null) PageSize = 10; // дефаултное значение записей/стр, попробуй 7 или 8 =)
		setList.setPageSize(PageSize); // количество записей/страница
		return setList;
	} set;
}[/quote]
[code]public ApexPages.StandardSetController setList {
	get {
		if (setList == null) { // проверка на наличие экземпляра контроллера
			setList = new ApexPages.StandardSetController(Database.query(query));
		}
		if (this.PageSize == null) PageSize = 10; // дефаултное значение записей/стр, попробуй 7 или 8 =)
		setList.setPageSize(PageSize); // количество записей/страница
		return setList;
	} set;
}[/code]

Как в тесте "запустить" setList, чтобы тест зашел в него?