Кастомизация
05
Июн
4

Мульти-пиклист

Итак! Вы наверно знаете, или не знаете, но догадываетесь, или не догадываетесь, но ощущаете каким-то шестым чувством (нет, не матрицу 🙂 ), что в CRM нет пиклистов с возможностью выбора нескольких значений! А все потому, что CRM не поддерживает хранение нескольких значений в одном поле, Но это можно исправить с помощью JavaScript’а.

Точнее саму модель данных никто править не будет, а будет вот что: создается два атрибута (ну или используются существующие), первый из которые это обычный пиклист, а второй обычное текстоое поле. Пиклист необходим для хранения возможных значений пиклиста и возможности добавлять их стандартными crm’ными средствами (т.е. без правки кода). А в текстовом поле будут хранится выбранные и сохраненные значения пиклиста для каждой записи (разделенные каким-нибудь служебным символом). Рассмотрим три разновидности скриптов с возможностью выбора нескольких значний:

Чекбокс стайл

Скрипт ниже преобразовывает стиль пиклиста в checkbox’овый список с возможностью выбора нескольких значений (которые берутся из списка самого пиклиста). Использование:

  • Создайте стандартный picklist с необходимыми значениями;
  • Создайте другой атрибут nvarchar, который будет хранить выбранные значений в этом мульти-пиклисте;
  • Ппоместите и пиклист и тектовый атрибут на форму. Откройте свойства тектового поля и снимите галку Отображать подпись в форме (в данном примере пиклист и тектовое поле называются new_picklist и new_picklistvalue, соответственно);
  • Поместите следующий скрипт на событи формы OnLoad (не забудьте только поменять названия пиклиста и текстового поля на Ваши).
/* 
Checkbox style Multi-Select Picklist 
author: Jim Wang @ January 2009 
http://jianwang.blogspot.com 
*/  
  
// PL - переменная содержащая ссылку на атрибут пиклиста; PLV - переменная ссылающаяся на атрибут хранящий значения пиклиста  
var PL = crmForm.all.new_picklist;  
var PLV = crmForm.all.new_picklistvalue;  
  
if( PL != null && PLV != null ) {  
	PL.style.display = "none";  
	PLV.style.display = "none";  
  
	// Создаем DIV контейнер
	var addDiv = document.createElement("<div style='overflow-y:auto; height:80px; border:1px #6699cc solid; background-color:#ffffff;' />");  
	PL.parentNode.appendChild(addDiv);  
  
	// Задаем элемент управления checkbox  
	for( var i = 1; i < PL.options.length; i++ ) {  
		var pOption = PL.options[i];  
		if(!IsChecked( pOption.text )) 
			var addInput = document.createElement("<input type='checkbox' style='border:none; width:25px; align:left;' />");  
		else  
			var addInput = document.createElement("<input type='checkbox' checked='checked' style='border:none; width:25px; align:left;' />");  
  
		var addLabel = document.createElement( "<label />");  
		addLabel.innerText = pOption.text;  
  
		var addBr = document.createElement("<br />"); //'br' флаг  
  
		PL.nextSibling.appendChild(addInput);  
		PL.nextSibling.appendChild(addLabel); 
		PL.nextSibling.appendChild(addBr);  
	}  
  
	// Check if it is selected  
	function IsChecked( pText ) {  
		if(PLV.value != "") {  
			var PLVT = PLV.value.split("||");  
			for( var i = 0; i < PLVT.length; i++ ) {  
				if( PLVT[i] == pText )  
					return true;  
			}  
		}  
		return false;  
	}  
 	
    // Сохраняем выделенный текст (это поле также может быть использовано при расширенном поиске)
	crmForm.attachEvent( "onsave" , OnSave);  
	function OnSave() {  
		PLV.value = "";  
		var getInput = PL.nextSibling.getElementsByTagName("input");  
  
		for( var i = 0; i < getInput.length; i++ ) {  
			if(getInput[i].checked) {  
				PLV.value += getInput[i].nextSibling.innerText + "||";  
			}  
		}  
	}  
}



В виде списка

В отличие от предыдущего примера, этот пиклист имеет более классический вид, но все так же менее ниспадающий 🙂 . Т.е. все опции видны сразу на форме! А аля выбра несколько значений необходимо удерживайте клавишу Ctrl и щелкайте по ним!

Установка: опять создаем пиклист (имеет название picklist new_visa) и поле хранящее выбранные значения (в примере new_visa_s). Вешаем на онлоад нижеследующий скрипт (не забудьте поменять название полей на Ваши).

Очень важно: обычный размер picklist’а равен одной строке, также как и обычное текстовое поле. Данный код изменяет структуру формы, таким образом, что picklist не отодвигает ниже следующие элементы формы. Поэтому, чтобы скрипт корректно работал, под пиклистом должно быть достаточное количество пустого простанства, чтобы вместить все значения. Поэтому для примера я справа от пиклиста разместил несколько текстоых полей, с тем чтобы под самим пиклистом образовалось пустое пространство!

function OnCrmPageLoad() {
	var multiPicklist = new MultiPicklist("new_visa","new_visa_s");
	multiPicklist.Size = 3;
	multiPicklist.Transform();
} 

function MultiPicklist( picklistId , storageId ) {
	var Instance = this;
	var getElem  = document.getElementById;
	Instance.Picklist = getElem(picklistId);
	Instance.Storage = getElem(storageId);
	Instance.Size = 1;
 
	Instance.Transform = function() {
		if( !Instance.Picklist )
			return alert( "Picklist " + picklistId + " is undefined" );
 
		if( !Instance.Storage )
			return alert( "Picklist Storage " + storageId + " is undefined" );
  
		//Скрываем поле хранящее значения
		hideStorage();
		//правим макет так, чтобы пиклист не будет наезжать на следующую строку снизу
		fixLayout();
		//делаем в пиклист мульти выбор
		Instance.Picklist.multiple = true;
		Instance.Picklist.size = Instance.Size * 2;
		//Обрабатываем событие сохранения
		crmForm.attachEvent( "onsave" , Instance.OnFormSave );
  
		//разбор опций пиклиста 
		var optionList;
		if((optionList = getStorageOptions()) == null) 
		return;
		//повторный выбор опций пиклиста
		for( var i = optionList.length - 1 ; i >= 0  ; i-- ) {
			for( var k = Instance.Picklist.options.length - 1 ; k >= 0 ; k-- ) { 
				var option = Instance.Picklist.options[k];
				if( option.value == optionList[i] )
					option.selected = true;
			}
		}
	}
 
	Instance.OnFormSave = function() {
        //слияний выбранных опций
        var optionSummery = "";
        for( var k = 0 ; k < Instance.Picklist.options.length ; k++ ) { 
            var option = Instance.Picklist.options[k];
            if( option.selected )
                optionSummery += option.value + ","; 
        }
        //удаляем плавующую запятую
        Instance.Storage.DataValue = optionSummery.replace(/,$/gi,"");
	}

	function fixLayout() {
	/*
	Идея заключается в том, чтобы найти позици ячеек текущего листа в строке
	Понизить количество строк как определено в Size Property
	Удалить ячейки ниже пиклиста и изменить атрибут rowspan ячеек пиклиста
	*/
	var countTD    = 0;
	var picklist_d = getElem( picklistId + "_d" );
	var picklist_c = getElem( picklistId + "_c" );
	var currentTR  = picklist_d.parentElement;
	var currentTD  = picklist_c.previousSibling;
  
	while( currentTD && currentTD.tagName == "TD" ) {
        countTD++;
        currentTD = currentTD.previousSibling;
	}
  
	for( var i = 1 ; i < Instance.Size ; i++ ) {
		currentTR = currentTR.nextSibling;
		if( currentTR ) {
			currentTR.deleteCell(countTD); 
			currentTR.deleteCell(countTD);
		}
	}
      
        picklist_d.rowSpan = Instance.Size;
        picklist_c.rowSpan = Instance.Size;
	}
 
	function getStorageOptions() {
		if( Instance.Storage.DataValue == null )
			return null;
		return Instance.Storage.DataValue.split(',');
	}
 
	function hideStorage() {
		getElem( storageId + "_d" ).style.visibility = "hidden";
		getElem( storageId + "_c" ).style.visibility = "hidden";
		Instance.Storage.style.visibility = "hidden";
	}
}

OnCrmPageLoad();



«Классика»

Самая интересная (на мой взгляд) реализация среди представленных и более остальных смахивает на то, что называется ниспадающий список. В отличии от предыдущих, в данном примере скрывается не текстовое поле, а пиклист. Но что еще более интересно, в тектовом поле выбранные значания отображаются через запятую и только щелкнув по этому полю появится ниспадающий список, где вы можете выбрать нужные значения (удерживая клавишу Ctrl).

Для установки делаем все то же самое что и в предыдущих примерах: создаем пиклист (в примере называется new_hobbieslist) и текстовое поле (в примере new_hobbies). Вешаем их на форму, а на онлоад вешаем скрипт:

function makeMultiSelectPicklist(txt, pkl){
	txt.associatedPicklist = pkl;
	pkl.associatedTextbox = txt;
	txt.readonly = true;
	// скрываем пиклист за ненадобностью
	crmForm.all.new_hobbieslist_c.style.display = "none";
	crmForm.all.new_hobbieslist_d.style.display = "none";

	// установка значений для мульти-пиклиста
	var container = document.createElement("span");
	container.style.position = "relative";
	container.style.top = "0px";
	container.style.left = "0px";
	container.style.width = "0px";
	container.style.height = "0px";
	txt.parentNode.insertBefore(container, txt);
	container.insertBefore(pkl, null);
	pkl.style.display = "none";
	pkl.style.position = "absolute";
	pkl.style.top = "0px";
	pkl.style.left = "0px";
	pkl.multiple = "true";

	txt.onfocus = function(e) {
		var txt = this;
		var pkl = txt.associatedPicklist;
		var values = "|" + txt.value.replace(/; /g, "|") + "|";
		pkl.style.display = "block";
		pkl.style.width = txt.offsetWidth + "px";
		for(var i=0; i < pkl.options.length; i++){
			pkl.options[i].selected = new RegExp("\\|" + pkl.options[i].text + "\\|").test(values);
		}
		pkl.focus();
	}
	pkl.onblur = function(e) {
		var pkl = this;
		var txt = pkl.associatedTextbox;

		var s = [];
		for(var i=0; i < pkl.options.length; i++){
			if(pkl.options[i].text && pkl.options[i].selected) {
				s.push(pkl.options[i].text);
			}
		}
		txt.value = s.join("; ");
		pkl.style.display = "none";
	}

	// разрываем связь - необходимо для 6 осла
	txt = null;
	pkl = null;
}
//поменяйте названия полей на Ваши
makeMultiSelectPicklist(document.all.new_hobbies, document.all.new_hobbieslist);



Комментарии (4)
  • Дмитрий 05.06.2009

    Привет!

    отличная подборка материала по срм! супер!

    подскажи пожалуйста, как можно добавлять (удалять) значения из стандартного пиклиста на лету (например в зависимости от выбора значения другого пик листа)

    например есть пиклист1 и пиклист2

    в пиклист1 добавлены значения: состояние1 и состояние2.
    при выборе значение — состояние1, в пиклисте2 должны заполняться значения: состояние1.1, состояние1.2
    а при выборе значение состояние2: в пиклисте2 должны появляться: состояние2.1, состояние 2.2

    За ранее признателен за помощь!

  • slivka_83 05.06.2009

    Добрый день 🙂

    Вам сюда http://mmcrm.ru/?p=708

  • Dmitry 05.06.2009

    А если надо сделать что бы поле выбора было обязательным? Проще говоря в первом варианте надо что бы хотя бы одно значение было выбрано.

  • slivka_83 05.06.2009

    На онсейве делаете проверку: проходитесь итерацией по списку и проверяете, что хотя бы одна галка установлена. Если нет, то отменяете сохранение (http://mmcrm.ru/?p=694) и выдаете сообщение юзверу 🙂

*

code