Добрый день, хабрапользователи! Сегодня я бы хотел поделиться с проблемами, которые обычно отталкивают добропорядочных программистов от использования фреймворка — Ext JS. Опишу конкретную ситуацию: в один прекрасный день после некоторого времени потраченного на просмотр возможностей, которые предлагает Ext JS, возникает непреодолимое желание попробовать его «в деле». Качается дистрибутив, ставится на локальный сервер и запускаются красивые примеры гридов, форм и, даже, готового рабочего стола! Разработчик меняет пару «фишечек», все вроде легко и просто. Резонно в голове бо��-скаута возникает идея сделать некий коммерческий проект на Ext JS (чаще всего это бывают различного рода CMS, админки, CRM). И тут начинается самое интересное…

После недели работы с Ext JS голова бой-скаута начинает полуавтоматически генерировать мысли о его собственной ничтожности и невообразимой сложности Ext JS. И, обиженный на Ext JS, разработчик развевает по ветру пепел желания работы на этом фреймворке. А жаль…

Поэтому в этой статье я хочу описать проблемы, с которыми я столкнулся при разработке системы управления контентом и решения этих проблем. Все примеры проверены на Ext JS версии 3.4.

1. Автоматическая подгонка размеров содержимого окна.
Задача: встроить в Ext.Window форму (Ext.form.FormPanel). Первое, что приходит в голову:

Ext.onReady(function(){

   var win = new Ext.Window({
		title: "title",
		border: false,
		width: 400,
		height: 400,
		items: [{
			width: 390,
			height: 380,
			items: [new Ext.form.FormPanel({
				 frame: true,
				 items: {xtype: 'textfield'}
			})]
		}]
   });

 win.show();
});

В данном случае разработчику требуется «подогнать» размеры формы под размеры окна. Если подогнано неверно — снизу появляется раздражающая белая полоска:

image

Для этой ситуации разработчики Ext JS предусмотрели параметр layout в конфигурации Ext.Window. Конкретно для этого случая используется layout: 'fit':

Ext.onReady(function(){

   var win = new Ext.Window({
		title: "title",
		border: false,
		width: 400,
		height: 400,
		layout:'fit',
		items: [
			new Ext.form.FormPanel({
				frame: true,
				items: {xtype: 'textfield'}
			})
		]
 });

 win.show();
});

image

2. Разделение полей формы на 2 и более колонок (или размещение нескольких полей формы на одной строке).
Если просто прописать в items формы несколько полей формы, то они выстроятся ровно друг под дружкой, примерно вот так:

Ext.onReady(function(){

   var win = new Ext.Window({
	title: "title",
	border: false,
	width: 400,
	height: 200,
	layout:'fit',
	items: [
		new Ext.form.FormPanel({
			frame: true,
			items: [{
				xtype: 'textfield',
				fieldLabel: 'field1'
			},{
				xtype: 'textfield',
				fieldLabel: 'field2'
			},{
				xtype: 'textfield',
				fieldLabel: 'field3'
			},{
				xtype: 'textfield',
				fieldLabel: 'field4'
			}]
		})
	]
 });

 win.show();
 
});

image

Нам же требуется, чтобы было примерно так:

image

Делается это так:
(Внимание! Для понимания следующего куска кода требуется немного логического мышления)
Ext.onReady(function(){

   var win = new Ext.Window({
		title: "title",
		border: false,
		width: 400,
		height: 200,
		layout:'fit',
		items: [
			new Ext.form.FormPanel({
				frame: true,
				// Нас интересует код от этого места
				layout: 'column',
				defaults: {
					xtype: 'form',
					columnWidth:0.5,
					labelAlign: 'top',
					anchor: '100%'
				},
				// до этого
				// 1
				items: [{
					// 2, столбец 1
					items:[{
							xtype: 'textfield',
							fieldLabel: 'field1'
						},{
							xtype: 'textfield',
							fieldLabel: 'field2'
						}]
				},{
					// 3, столбец 2
					items:[{
							xtype: 'textfield',
							fieldLabel: 'field3'
						},{
							xtype: 'textfield',
							fieldLabel: 'field4'
						}]
				}]
			})
		]
 });

 win.show();
 
});


Итак. Приступим к разбору кода. Первое, что нужно для разделения формы на колонки параметр layout: 'column', который сообщает форме, что форму следует разделить на колонки. Далее вступает в дело параметр defaults, который сообщает настройки по умолчанию для нижестоящего items. Эти настройки передаются каждому объекту в этом массиве (объектам items 2, items 3).

Почему в defaults так много параметров и зачем они нужны, для простого разбиения формы на колонки? Отвечаю:
xtype: 'form' — без этого параметра, не отображались бы fieldLabel для каждого поля,
columnWidth — определяет ширину стобцов по умолчанию (в объектах 2 и 3 можно ширину столбца переопределять),
labelAlign — определяет положение fieldLabel (top — сверху),
anchor — ширина в процентах объектов 2 и 3.

Так же обратите внимание на то, что в отличие от предыдущего примера, в items объекта 1 добавляется еще один уровень вложенности.

3. Управление рендерингом записей в гриде.
Часто случается так, что хочется как-то изменить содержимое ячейки грида, в зависимости от будущего значения этого содержимого. На помощь приходит параметр грида — renderer.

Допустим у нас на старте есть грид:

image

Исходный код:

var data = {
   totalCount : 3,
   banners: [{"is_active": 0,"ID": 1},{"is_active": 0,"ID" : 3},{"is_active": 1,"ID": 4}]
}

var view = new Ext.Viewport({
	layout: 'fit',
	items: [{
		xtype: 'grid',
		columns: [
			{header: "ID", align : 'left', width: 30, sortable: true, dataIndex: 'ID'},
			{
				header: "Активность", align : 'left', width: 100, sortable: true,
				dataIndex: 'is_active'
			}
		],
		store: new Ext.data.GroupingStore({
			data: data,
			fields: [{name: 'ID'},{name: 'is_active'}],
			reader: new Ext.data.JsonReader({
					root: 'banners',
					totalProperty: 'totalCount',
					id: 'ID'
				},
				Ext.data.Record.create([
					{name: 'ID'},
					{name: 'is_active'}
				])
			)
		}),
		view: new Ext.grid.GridView({
			forceFit: false,
		}),
		listeners: {}
	}]

});

view.render(Ext.getBody());
});


Можно сделать так:
image

Исходный код:

var renderActivity = function(val) {
	if(val == 1) 
		return "<span style='color: green'>"+val+"</span>";
	else
		return "<span style='color: red'>"+val+"</span>";
}

var data = {
   totalCount : 3,
   banners: [{"is_active": 0,"ID": 1},{"is_active": 0,"ID": 3},{"is_active": 1,"ID": 4}]
}

var view = new Ext.Viewport({
	layout: 'fit',
	items: [{
		xtype: 'grid',
		columns: [
			{header: "ID", align : 'left', width: 30, sortable: true, dataIndex: 'ID'},
			{
				header: "Активность", align : 'left', width: 100, sortable: true,
				dataIndex: 'is_active', renderer: renderActivity
			}
		],
		store: new Ext.data.GroupingStore({
			data: data,
			fields: [{name: 'ID'},{name: 'is_active'}],
			reader: new Ext.data.JsonReader({
					root: 'banners',
					totalProperty: 'totalCount',
					id: 'ID'
					}, Ext.data.Record.create([
						{name: 'ID'},
						{name: 'is_active'}
					])
			)
		}),
		view: new Ext.grid.GridView({
			forceFit: false,
		}),
		listeners: {}
	}]

});

view.render(Ext.getBody());
});


Как видно, вначале была добавлена функция renderActivity, которая разукрашивает содержимое ячейки в зависимости от значения в зеленый или красный цвет. Далее эта функция вызывается в описании настроек колонки «Активность» — renderer: renderActivity. Таким образом содержимое ячеек грида можно легко и просто изменять под любые, даже, порой, не совсем пристойные, нужды.

Если посвятить изучению Ext JS побольше врем��ни, то действительно поражаешься, какие гибкие возможности предоставляет этот фреймворк. Многие говорят, что сайты на Ext JS сложно кастомизировать. Не верьте этому. Лично я считаю, что у фреймворка просто достаточно высокий порог вхождения.

Разрабатывая на Ext JS уже около года, я «съел не одну собаку». Таких мелочей у меня накопилось великое множество. Поэтому, если хабрасообщество пожелает и в дальнейшем читать мои статьи по Ext JS, я с радостью их буду писать.

На этом извольте закончить мою первую статью, надеюсь она вам понравилась.