Всем привет. Нужна помощь опытных программистов.
Задача (тестовое задание).
(удалено по просьбе правообладателя)
На текущем этапе, ПОЖАЛУЙСТА помогите разобраться, как правильно сделать пагинацию в кастомном контроллере?
У кого такое же задание или кто может помочь и сидит в ВК, пожалуйста пишите в личку vk.com/domovikx любой помощи пуду рад, спасибо заранее!
gitlab.com/domovikx/tx002
Да, классно уже народ подсказал, тут мои текущие коды, когда закончу ТЗ, выложу, что получилось на форуме в чистовом варианте.
Промежуточный этап. Пагинация + сортировка готовы и работают.
<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();
}
// Конец пагинации ----------------------------------------
} // конец контроллера =)
Текущий этап.
Как добавить столбец с кнопками - Edit Del
Как добавить поиск.
Возможно поможет этот код: https://developer.salesforce.com/forums/?id=9060G000000UVLGQA4
На вопрос "КАК СДЕЛАТЬ" сложно дать ответ. Существует 100500 способов сделать одну и ту же штуку.
Это основы! Советую сначала погуглить тему создания CRUD приложение для Salesforce.
Если уже не будет получаться, можно детальнее пообщаться на тему "ПОЧЕМУ".
К тому же тут на форуме присутствуют представители различных компаний и в том числе той куда ты планируешь попасть сделав тестовое задание. Возможность находить решения в интернете и адаптировать их под свои требования ценится не меньше чем наличие знаний ![]()
На форуме уже не раз обсуждались эти вопросы.
Можно "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.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 + '%')];Второй вариант предпочтительнее из-за правильного биндинга переменных в запрос.
В первом случае можно словить 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
Кто можешь подсказать ресурсы, которые почитать? Благодарю заранее!
Если хочется именно 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>
От млин уже за столько лет и позабыл про "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
Сейчас разбираю это. Спасибо за ссылки на инфу.
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;
}
}
Немнго оптимизации.
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;} // 5public 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; // Вначале добавляем аккаунт, чтобы потом взять его IDContact 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;
}
// -----------------------------------------------------------
Ура. Спасибо форуму, и в особенности Димке за источники.
Мое ТЗ готово. Остались Юнит тесты.
В результате получилось следующее.

Поздравляю с небольшой победой. Смотрится красиво!
Маленький совет для улучшения UI. Панель Errors обычно выводят НАД формой.
Помести ее между приветствием и формой и будет вообще супер!
Илья, при таком
<apex:inputText label="2. Last Name * " value="{!newLastName}" required="true" />Так и правильно выдает.
Last Name - Это поле поумолчанию стоит как обязательное, даже если required="false", оно надо.
А вот e-mail - делается обязательным полем.
Остальной код с правками на гите: https://gitlab.com/domovikx/tx002/tree/master.
Пока открыт, потом возможно попросят закрыть. Если недоступен, значит закрыл.
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, чтобы тест зашел в него?