Posted by Pavel Podlipensky on September 07 8:08 PM
Рано или поздно наступает момент, когда возможностей фреймворка вам недостаточно. "Ах, как же так, забыли такую полезную кнопочку/контрол/приложение сделать..." - сетуете вы. Вот тогда и приходит в голову идея написать нехватающий функционал самому. Точнее дописать. И любой уважающий себя фреймворк предоставляет механизмы для собственного расширения/изменения. ExtJs не стал исключением.<p>По сути расширение это класс, наследованный от уже существующего в библиотеке и реализующий <strong>дополнительный функционал</strong>. Представим себе, что нам необходимо реализовать контрол, весьма похожий по своему назначению и функционалу на Ext.Panel, но с небольшим отличием - новая панель всегда должна быть квадратной формы (т.е. ширина всегда должна быть равна ее высоте). </p> <div id="codeSnippetWrapper" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; width: 97.5%; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; border-width: 1px; border-color: silver; border-style: solid; padding: 4px"> <div id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px"> <pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 1:</span> SquarePanel = Ext.extend(Ext.Panel, {</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 2:</span> <span style="color: #008000">//Устанавливаем размер панели по умолчанию</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 3:</span> width: 100,</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 4:</span> height: 100,</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 5:</span> onResize: <span style="color: #0000ff">function</span>(width, height) {</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 6:</span> <span style="color: #008000">//Если размеры нашей панели были изменены, причем непропорционально - вернуть ей квадратную форму</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 7:</span> <span style="color: #0000ff">if</span> (width != height) {</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 8:</span> <span style="color: #0000ff">this</span>.body.setWidth(height);</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 9:</span> }</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 10:</span> <span style="color: #008000">//Вызываем базовый метод класса-родителя, т.е. - класса Panel.</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 11:</span> SquarePanel.superclass.onResize.call(<span style="color: #0000ff">this</span>, height, height);</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 12:</span> },</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 13:</span> <span style="color: #008000">//Добавляем метод который позволит нам изменять размеры нашей панели, указав лишь размер одной ее стороны</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 14:</span> setSquareSize(length){</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 15:</span> <span style="color: #0000ff">this</span>.body.setWidth(length);</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 16:</span> <span style="color: #0000ff">this</span>.body.setHeight(length);</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 17:</span> }</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 18:</span> });</pre><!--CRLF--></div></div><p>Вот такая нехитрая конструкция Ext.extend позволяет создать новый класс, с <strong>дополнительными возможностями</strong>, а именно – новое поведение панели при изменении ее размеров и новый метод setSquareSize позволяющий установить новые размеры панели.<strong> </strong>Я акцентировал внимание на дополнительных возможностях, потому что все свойства, методы и события базового класса (Ext.Panel) теперь присущи и нашему классу. Если заглянуть <a href="https://www.extjs.com/deploy/dev/docs/source/Ext.html#method-Ext-extend">внутрь реализации</a> метода extend, то можно увидеть, что </p><div id="codeSnippetWrapper" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; width: 97.5%; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; border-width: 1px; border-color: silver; border-style: solid; padding: 4px"><div id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px"><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 1:</span> <span style="color: #008000">//создаем пустую функцию-класс F</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 2:</span> <span style="color: #0000ff">var</span> F = <span style="color: #0000ff">function</span>(){},</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 3:</span> <span style="color: #008000">//сюда будет записана ссылка на прототип дочернего класса (т.е. нашего собственного)</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 4:</span> sbp, </pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 5:</span> <span style="color: #008000">//сюда будет записана ссылка на прототип родительского класса</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 6:</span> spp = sp.prototype;</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 7:</span> <span style="color: #008000">//прототипом класса F становится прототип родительского класса</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 8:</span> F.prototype = spp;</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 9:</span> <span style="color: #008000">//прототипом нашего же класса становится класс F</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 10:</span> sbp = sb.prototype = <span style="color: #0000ff">new</span> F();</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 11:</span> <span style="color: #008000">//далее идет переопределение конструктора и назначение суперкласса</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 12:</span> sbp.constructor=sb;</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 13:</span> sb.superclass=spp;</pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 14:</span> //...много непонятных буков</pre><!--CRLF--></div></div><p>Методы и свойства, которые мы <strong>переопределили</strong> (onResize, width и height) попадут в prototype нашего класса вот таким образом:</p><div id="codeSnippetWrapper" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; width: 97.5%; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; border-width: 1px; border-color: silver; border-style: solid; padding: 4px"><div id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px"><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 1:</span> <span style="color: #008000">//sb - это наш класс SquarePanel, а overrrides - объект содержащий дополнения к нашему классу</span></pre><!--CRLF--><pre style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 2:</span> Ext.<span style="color: #0000ff">override</span>(sb, overrides);</pre><!--CRLF--></div></div><p>О функции override, я расскажу в другой раз. Пока ограничусь лишь кратким описанием – функция Ext.override позволяет <strong>замещать</strong> функционал уже существующих классов, на ваши собственные реализации.  </p><p>Далее мы можем расширять нашу панель точно также как и Ext.Panel</p><div id="codeSnippetWrapper" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; margin-top: 20px; margin-right: 0px; margin-bottom: 10px; margin-left: 0px; width: 97.5%; font-family: 'Courier New', courier, monospace; direction: ltr; max-height: 200px; font-size: 8pt; overflow-x: auto; overflow-y: auto; cursor: text; border-width: 1px; border-color: silver; border-style: solid; padding: 4px"><div id="codeSnippet" style="text-align: left; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px"><pre style="text-align: left; line-height: 12pt; background-color: white; width: 100%; font-family: 'Courier New', courier, monospace; direction: ltr; color: black; font-size: 8pt; overflow-x: visible; overflow-y: visible; border-style: none; padding: 0px; margin: 0em"><span style="color: #606060"> 1:</span> DifferentSquarePanel = Ext.extend(SquarePanel, {<span style="color: #008000">/* some additional logic here */</span>})</pre><!--CRLF--></div></div><p>Что ж, довольно просто и элегантно. Впрочем, как и должно быть в любом уважающем себя фреймворке.</p>
Рано или поздно наступает момент, когда возможностей фреймворка вам недостаточно. "Ах, как же так, забыли такую полезную кнопочку/контрол/приложение сделать..." - сетуете вы. Вот тогда и приходит в голову идея написать нехватающий функционал самому. Точнее дописать. И любой уважающий себя фреймворк предоставляет механизмы для собственного расширения/изменения. ExtJs не стал исключением.

По сути расширение это класс, наследованный от уже существующего в библиотеке и реализующий дополнительный функционал. Представим себе, что нам необходимо реализовать контрол, весьма похожий по своему назначению и функционалу на Ext.Panel, но с небольшим отличием - новая панель всегда должна быть квадратной формы (т.е. ширина всегда должна быть равна ее высоте).

   1: SquarePanel = Ext.extend(Ext.Panel, {
   2:     //Устанавливаем размер панели по умолчанию
   3:     width: 100,
   4:     height: 100,
   5:     onResize: function(width, height) {
   6:         //Если размеры нашей панели были изменены, причем непропорционально - вернуть ей квадратную форму
   7:         if (width != height) {
   8:             this.body.setWidth(height);
   9:         }
  10:         //Вызываем базовый метод класса-родителя, т.е. - класса Panel.
  11:         SquarePanel.superclass.onResize.call(this, height, height);
  12:     },
  13:     //Добавляем метод который позволит нам изменять размеры нашей панели, указав лишь размер одной ее стороны
  14:     setSquareSize(length){
  15:         this.body.setWidth(length);
  16:         this.body.setHeight(length);
  17:     }
  18: });

Вот такая нехитрая конструкция Ext.extend позволяет создать новый класс, с дополнительными возможностями, а именно – новое поведение панели при изменении ее размеров и новый метод setSquareSize позволяющий установить новые размеры панели. Я акцентировал внимание на дополнительных возможностях, потому что все свойства, методы и события базового класса (Ext.Panel) теперь присущи и нашему классу. Если заглянуть внутрь реализации метода extend, то можно увидеть, что

   1: //создаем пустую функцию-класс F
   2: var F = function(){},
   3:     //сюда будет записана ссылка на прототип дочернего класса (т.е. нашего собственного)
   4:     sbp, 
   5:     //сюда будет записана ссылка на прототип родительского класса
   6:     spp = sp.prototype;
   7: //прототипом класса F становится прототип родительского класса
   8: F.prototype = spp;
   9: //прототипом нашего же класса становится класс F
  10: sbp = sb.prototype = new F();
  11: //далее идет переопределение конструктора и назначение суперкласса
  12: sbp.constructor=sb;
  13: sb.superclass=spp;
  14: //...много непонятных буков

Методы и свойства, которые мы переопределили (onResize, width и height) попадут в prototype нашего класса вот таким образом:

   1: //sb - это наш класс SquarePanel, а overrrides - объект содержащий дополнения к нашему классу
   2: Ext.override(sb, overrides);

О функции override, я расскажу в другой раз. Пока ограничусь лишь кратким описанием – функция Ext.override позволяет замещать функционал уже существующих классов, на ваши собственные реализации. 

Далее мы можем расширять нашу панель точно также как и Ext.Panel

   1: DifferentSquarePanel = Ext.extend(SquarePanel, {/* some additional logic here */})

Что ж, довольно просто и элегантно. Впрочем, как и должно быть в любом уважающем себя фреймворке.

blog comments powered by Disqus