Кастомизация
15
Ноя
12

Представление и форма в одном «флаконе»

Несколько дней назад тут был пост про компонент CRM 4.0 Reading Pane, который предоставляет возможность просматривать почту и другие действия, так как это реализовано в Outlook’е. Вещь в принципе хорошая, но в CRM предостаточно «своих» средств для создания похожего функционала. А именно есть Представления, для отображения списка записей и есть Формы для подробного отображения каждой записи.

А сейчас посмотрим, как это все свести воедино…

  • Первым делом откройте форму Бизнес-партнера (или какую Вы хотите использовать). Нажмите Ctrl + N – откроется новое окно, содержащее ту же самую форму, но уже с адресной строкой. Затем перейдите к какому-либо связанному представлению (я буду подключать связанное представление Действий) и введите адресной строке:
    javascript:alert(document.frames[1].location)

    Появится окно содержащее примерно следующий текст:

    http://crm2008/superfirma/sfa/accts/areas.aspx?oId={BBFF77D3-94C5-DE11-A985-000C298F544B}&oType=1&security=852023&tabSet=areaActivities

    Сохраните куда-нибудь значение последней переменной (в данном случаи это «areaActivities») – оно нам впоследствии понадобится;

  • Откройте форму настройки объекта Бизнес-партнер. Добавьте новую вкладку, а на нее новый раздел;
  • Затем на этой вкладке создайте два iFrame’а со следующими параметрами:
    • Первому дайте имя «list», а второму «forma»;
    • Обоим в качестве URL пропишите «about:blank»;
    • Снимите галки «Ограничить использование сценариев между кадрами»;
    • На вкладке Форматирование для первого задайте какое-нибудь ограниченное количество строк (например, 10), а для второго поставьте галку «Автоматически развертывать для заполнения доступной области окна»;
    • Для обоих уберите прокрутку;
    • И для первого отключите отображение границы.
  • На онлоад повесьте такой скрипт (переименуйте в нем iFrame’ы если они у Вас отличаются и измените название связанного представления, которое мы передаем при вызове функции GetFrameSource(«areaActivities»)):
    function GetFrameSource(tabSet) {
    	if (crmForm.ObjectId != null) {
    
    		var oId = crmForm.ObjectId;
    		var oType = crmForm.ObjectTypeCode;
    		var security = crmFormSubmit.crmFormSubmitSecurity.value;
    
    		return "areas.aspx?oId=" + oId + "&oType=" + oType + "&security=" + security + "&tabSet=" + tabSet;
    
    	} else {
    
    		return "about:blank";
    
    	}
    }
    
    // загружем связанное представление (в качестве параметра передаем название связанного представлеия)
    crmForm.all.IFRAME_list.src = GetFrameSource("areaActivities");
    
    
    var bFired = false;
    
    GridClick = function() { 
    	
    	// проверяем что что событие произошло впервые
        if(bFired == false) { 
    		
    		// получаем массив выделенных записей 
    		var frameDoc = document.getElementById("IFRAME_list").contentWindow.document; 
    		var a = frameDoc.all['crmGrid'].InnerGrid.SelectedRecords; 
    
    		// проверяем что выделена только одна запись
            if (a.length == 1) {
    			
    			// в зависимости от типа записи выделенной в первом айфрейме, меняем URL втого айфрейма
                switch (a[0][1]) {
    				case "4202":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/email/edit.aspx?id=" + a[0][0];
    				break;
    				case "4212":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/task/edit.aspx?id=" + a[0][0];
    				break;
    				case "4214":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/ServiceAppointment/edit.aspx?id=" + a[0][0];
    				break;
                    case "4210":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/phone/edit.aspx?id=" + a[0][0];
    				break;
                    case "4207":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/letter/edit.aspx?id=" + a[0][0];
    				break;
                    case "4204":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/fax/edit.aspx?id=" + a[0][0];
    				break;
                    case "4201":
    					crmForm.all.IFRAME_forma.src = "http://crm2008/superfirma/activities/appointment/edit.aspx?id=" + a[0][0];
    				break;
    				default:
    					crmForm.all.IFRAME_forma.src = "about:blank";
    			}
                
            } else {
    
    			crmForm.all.IFRAME_forma.src = "about:blank";
    
    		}
    
            bFired = true; 
    	
        } else {
        
            // переключаем флаг обратно
    		bFired = false;
            
        } 
    }
    
    // после загрузки грида добавляем ему по событию щелчка вызов функции GridClick
    AttachGridEvent = function() {
    	// проверяем что iFrame'е полностью загружен
    	if(crmForm.all.IFRAME_list.readyState == "complete") { 
    		var frameDoc = document.getElementById("IFRAME_list").contentWindow.document; 
    		frameDoc.all['crmGrid'].InnerGrid.attachEvent("onselectionchange", GridClick); 
            
            // скрываем у представления строку с фильтром и убираем лишние поля
            frameDoc.all['AppGridFilterContainer'].parentElement.parentElement.style.display='none';
    		frameDoc.body.childNodes[0].style.padding = 0;
    	} 
    }
    
    // при изменении статуса загрузки преставления вызываем функцию AttachGridEvent
    crmForm.all.IFRAME_list.onreadystatechange = AttachGridEvent;
    
    
    // данная функция в загруженной форме скрывает строку с большой иконкой
    AttachFormaEvent = function() {
    	// проверяем что iFrame'е полностью загружен
    	if(crmForm.all.IFRAME_forma.readyState == "complete" && crmForm.all.IFRAME_forma.src != "about:blank") {
    		var frameDoc2 = document.getElementById("IFRAME_forma").contentWindow.document; 
            frameDoc2.all['crmMenuBar'].parentElement.parentElement.height = 50;
    
    	} 
    }
    
    // при изменении статуса загрузки формы вызываем функцию AttachFormaEvent
    crmForm.all.IFRAME_forma.onreadystatechange = AttachFormaEvent;
    

    Этот скрипт делает следующие вещи:

    • При загрузке связанного представления прицепляет вызов функции GridClick при щелчке на нем. Эта функция по полученному типу записи и GUID’у меняет URL второго айфрейма, таким образом, чтобы отобразить в нем форму этой записи. Поэтому если Вы собираетесь использовать другие типы записей Вам необходимо будет подправит код (в частности изменить URL);
    • При загрузке обоих фреймов, с помощью JavaScript у них скрываются не нужные элементы, такие как фильтр у представления, пустые поля, слишком большие «шапки».
  • Тестируем: откройте какую-нибудь запись Бизнес-партнера и перейдите на вновь созданную вкладку. Тут же подгрузится связанное представление, щелкните по какой-либо строчке в нем – во втором фрейме откроется форма этой записи  тут же можете ее редактировать.




Комментарии (12)
  • a33ik 15.11.2009

    Интересный код, но по нему есть замечания:
    1. Хардкод названия сервера и названия организации. Неуниверсально.
    2. Оперирование в айфреймах не относительными ссылками а абсолютными, что может явиться причиной проблемы с аутентификации в айфреймах.
    3. В качестве заглушки для айфреймов не рекомендуется использоваться about:blank. Рекомендуется /_static/blank.html.

  • slivka_83 15.11.2009

    Я не верю в «универсальный» код 🙂 так что рассматривайте это просто как пример и не более того 🙂

    По поводу «бланка» http://blog.customereffective.com/blog/2009/04/setting-blank-iframe-sources.html

  • a33ik 15.11.2009

    Даже копипастинг должен быть осмысленным ИМХО.

  • Stanislav 15.11.2009

    Помогло.
    /_static/blank.html

  • scint 15.11.2009

    скрипт отрабатывает до
    frameDoc.all[‘crmGrid’].InnerGrid.attachEvent(«onselectionchange», GridClick);
    после чего выдает ошибку:
    Message: ‘all.crmGrid.InnerGrid’ is null or not an object

    у меня даже нет идеи, что и делать (

  • slivka_83 15.11.2009

    А Вы подгоняли его под свои потребности или полностью повторили пример? Если первое, то вереоятно Вы не переименовали все необходимые поля…

  • scint 15.11.2009

    поля поменял (название сервера и организации). в остальном мне необходимо было все то же самое, что и в примере. У меня возникает ощущение, что несмотря на проверку загруженности i’frame==»complete», у меня не успевают загрузиться данные ячеек (InnerGrid). В результате процедура attachEvent(«onselectionchange», GridClick); не отрабатывает.

  • scint 15.11.2009

    Вообщем проблему решил.
    Итак, проверки i’frame==complete мало. i’frame получает статус complete раньше нежели элементы были подгружены во фрейм (можно проверить простой вставкой alert’a перед frameDoc.all[‘crmGrid’].InnerGrid.attachEvent). В результате пришлось делать задержку на выполнение функции AttachGridEvent, используя SetTimeOut.
    Сам код очень помог. Огромное спасибо.

  • slivka_83 15.11.2009

    Извините, что сразу не ответил — проблемы с компом — негде провести эксперимент…
    Как то давно я тоже воевал с отслеживанием загрузки контента в айфрейм и тогда обнаружил что статус complete айфрем получает три!!! раза 🙂 не знаю с чем это было связано, возможно сейчас это не так… но тогда я решил проблему установлением счетчика, т.е. проверял статус и если счетчик переваливал за 3 продолжал выполнение кода 🙂

  • Илья 15.11.2009

    При осуществлении каких-то действий со списком Действий (в фрейме list) в т.ч. нажатие кнопки «Обновить список» приводит к неработоспособности фрейма forma. Как это можно исправить?

  • slivka_83 15.11.2009

    Добрый день 🙂 Извините что долго не отвечал — был занят 🙂

    А в чем выражается неработоспособность? 🙂

  • varelka 15.11.2009

    У Вас очень интересные разработки, и время от времени я к Вам заглядываю, если вдруг появляется какая-нибудь новая задача. Тут вдруг меня попросили сделать чро-ро похожее на то что описано в «Представление и форма в одном «флаконе»
    , но для «Notes».
    Бьюсь над crmForm.all.IFRAME_list.src = GetFrameSource ????? не подскажете?
    Заранее благодарен

*

code