Permalink
Browse files

Added FormMacros, DynamicContainer

Createc README
  • Loading branch information...
1 parent e87f5f4 commit fc9f2d9121f00a8df3ab3c802bb21a0fc30d490e @foowie committed Oct 7, 2010
Showing with 541 additions and 12 deletions.
  1. +211 −0 DynamicContainer.php
  2. +34 −12 DynamicContainerCore.php
  3. +236 −0 FormMacros.php
  4. +60 −0 README
View
@@ -0,0 +1,211 @@
+<?php
+
+/**
+ * Nette addon that allows dynamically add/remove set of items in Form (FormContainer)
+ *
+ * @author Daniel Robenek
+ * @license MIT
+ */
+
+namespace Addons\Forms;
+
+// <editor-fold defaultstate="collapsed" desc="use">
+
+use \InvalidArgumentException;
+use \InvalidStateException;
+use Nette\Callback;
+use Nette\Forms\FormContainer;
+use Nette\Application\Presenter;
+use \LogicException;
+use Nette\ComponentContainer;
+use Nette\IComponent;
+use Nette\Forms\SubmitButton;
+
+// </editor-fold>
+
+class DynamicContainer extends DynamicContainerCore {
+
+ // <editor-fold defaultstate="collapsed" desc="variables">
+
+ /** array(add_delete_button?, label, name) */
+ protected $deleteButtonOptions = array(true, "Odebrat", "deleteButton");
+ /** array(add_add_button?, label, name) */
+ protected $addButtonOptions = array(true, "Nový záznam", "addButton");
+ /** @var bool Add ajaxClass to buttons? */
+ protected $useAjax = true;
+ /** @var string Ajax html class */
+ protected $ajaxClass = "ajax";
+
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="constructor">
+
+ /**
+ * Create FormContainer
+ * @param IComponentContainer $parent
+ * @param string $name
+ */
+ public function __construct(IComponentContainer $parent = null, $name = null) {
+ parent::__construct($parent, $name, null);
+ }
+
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="getters / setters">
+
+ /**
+ * Set delete button options
+ * @param bool $enable Create delete button?
+ * @param string $label Button label
+ * @param string $name Button name
+ * @return DynamicContainer
+ */
+ public function setDeleteButton($enable = true, $label = "Odebrat", $name = "deleteButton") {
+ if($this->getRowCount() > 0)
+ throw new InvalidStateException("Button options have to be set before factory is set and form is attached !");
+ $this->deleteButtonOptions = array($enable, $label, $name);
+ return $this;
+ }
+
+ /**
+ * Set add button options
+ * @param bool $enable Create add button?
+ * @param string $label Button label
+ * @param string $name Button name
+ * @return DynamicContainer
+ */
+ public function setAddButton($enable = true, $label = "Nový záznam", $name = "addButton") {
+ if($this->getRowCount() > 0)
+ throw new InvalidStateException("Button options have to be set before factory is set and form is attached !");
+ $this->addButtonOptions = array($enable, $label, $name);
+ if ($enable) {
+ if(!$this->lookup("Nette\Forms\FormContainer", false) === null)
+ $this->attachAddButton();
+ }
+ return $this;
+ }
+
+ /**
+ * Use ajax / ajax class
+ * @param bool $useAjax
+ * @param string $ajaxClass
+ * @return DynamicContainer
+ */
+ public function setAjax($useAjax = true, $ajaxClass = "ajax") {
+ $this->useAjax = $useAjax;
+ $this->ajaxClass = $ajaxClass;
+ return $this;
+ }
+
+ /**
+ * Use ajax class to buttons?
+ * @param bool $useAjax
+ * @return DynamicContainer
+ */
+ public function setUseAjax($useAjax) {
+ $this->useAjax = $useAjax;
+ return $this;
+ }
+
+ /**
+ * Class for ajax buttons
+ * @param string $ajaxClass
+ * @return DynamicContainer
+ */
+ public function setAjaxClass($ajaxClass) {
+ $this->ajaxClass = $ajaxClass;
+ return $this;
+ }
+
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="other protected+ methods">
+
+ /**
+ * Create "Add row" button, if enabled
+ */
+ protected function attachAddButton() {
+ list($enableAddButton, $addButtonLabel, $addButtonName) = $this->addButtonOptions;
+ if ($enableAddButton) {
+ $container = $this->getParent();
+ if (isset($container[$addButtonName]))
+ return; // todo: exception? (add param attached)
+ $container->addComponent(new SubmitButton($addButtonLabel), $addButtonName, $this->getName());
+ $container[$addButtonName]->setValidationScope(false)->onClick[] = callback($this, "addRow");
+ if ($this->useAjax)
+ $container[$addButtonName]->getControlPrototype()->class($this->ajaxClass);
+ }
+ }
+
+ /**
+ * Overriden method createRow, added "Delete row" button
+ * @param string $name
+ */
+ protected function createRow($name = null) {
+ $innerContainer = parent::createRow($name);
+
+ list($enableDeleteButton, $deleteButtonLabel, $deleteButtonName) = $this->deleteButtonOptions;
+ if ($enableDeleteButton) {
+ $button = $innerContainer->addSubmit($deleteButtonName, $deleteButtonLabel)->setValidationScope(false);
+ $button->onClick[] = callback($this, "removeRowClickHandler");
+ if ($this->useAjax)
+ $button->getControlPrototype()->class($this->ajaxClass);
+ }
+ }
+
+ /**
+ * Look at parent ;)
+ */
+ protected function attached($obj) {
+ parent::attached($obj);
+ if ($obj instanceof Presenter) {
+ $this->attachAddButton();
+ }
+ }
+
+ /**
+ * Called when is clicked to "Remove row" button
+ * @param SubmitButton $button
+ */
+ public function removeRowClickHandler($button) {
+ $name = $button->getParent()->getName();
+ $this->removeRow($name);
+ }
+
+ /**
+ * Call this before rendering
+ */
+ public function beforeRender() {
+ list($enableDeleteButton, $deleteButtonLabel, $deleteButtonName) = $this->deleteButtonOptions;
+ if ($enableDeleteButton && $this->getRowCount() <= $this->minimumRows) {
+ foreach ($this->getRows() as $container) {
+ $button = $container[$deleteButtonName];
+ $button->setDisabled()->getControlPrototype()->style("display: none;");
+ }
+ }
+
+ list($enableAddButton, $addButtonLabel, $addButtonName) = $this->addButtonOptions;
+ if ($enableAddButton && $this->getRowCount() >= $this->maximumRows) {
+ $button = $this->parent[$addButtonName];
+ $button->setDisabled()->getControlPrototype()->style("display: none;");
+ }
+ }
+
+ // </editor-fold>
+
+ // <editor-fold defaultstate="collapsed" desc="register helpers">
+
+ public static function FormContainer_addDynamicContainer(FormContainer $_this, $name = null) {
+ return $_this[$name] = new DynamicContainer(null, $name);
+ }
+
+ public static function register($methodName = "addDynamicContainer") {
+ if(PHP_VERSION_ID >= 50300)
+ FormContainer::extensionMethod($methodName, "Addons\Forms\DynamicContainer::FormContainer_addDynamicContainer");
+ else
+ FormContainer::extensionMethod("FormContainer::$methodName", array("DynamicContainer", "FormContainer_addDynamicContainer"));
+ }
+
+ // </editor-fold>
+
+}
View
@@ -1,14 +1,25 @@
<?php
+/**
+ * Nette addon that allows dynamically add/remove set of items in Form (FormContainer)
+ *
+ * @author Daniel Robenek
+ * @license MIT
+ */
+
namespace Addons\Forms;
+// <editor-fold defaultstate="collapsed" desc="use">
+
use Nette\IComponentContainer;
use Nette\IComponent;
use Nette\Forms\FormContainer;
use Nette\Application\Presenter;
use \InvalidStateException;
use \LogicException;
+// </editor-fold>
+
class DynamicContainerCore extends FormContainer {
// <editor-fold defaultstate="collapsed" desc="variables">
@@ -18,7 +29,7 @@ class DynamicContainerCore extends FormContainer {
/** Restriction for rows count */
protected $defaultRows = 1;
- protected $minimumRows = 0;
+ protected $minimumRows = 2;
protected $maximumRows = 100;
@@ -53,6 +64,14 @@ public function getRowCount() {
}
/**
+ * Get all dynamic rows
+ * @return Iterator of FormContainer
+ */
+ public function getRows() {
+ return $this->getComponents();
+ }
+
+ /**
* Set minimum number of container rows
* @param int $min
* @return $this
@@ -85,7 +104,7 @@ public function setDefaultCount($count) {
/**
* Set callback which content add items into container
- * @param Callback $callback
+ * @param Callback $callback function($container, $dynamicContainer, $form)
* @return $this
*/
public function setFactory($callback) {
@@ -102,7 +121,7 @@ public function setFactory($callback) {
* @return bool Was inserting successfull? (false = max rows reached)
*/
public function addRow() {
- if(count($this->getComponents()) >= $this->maximumRows)
+ if($this->getRowCount() >= $this->maximumRows)
return false;
$this->createRow();
$this->fireOnChange();
@@ -114,6 +133,7 @@ public function addRow() {
* Fires onChange
* @param int|string $name
* @return bool true = ok false = minimum limit exceeded
+ * @throws InvalidArgumentException
*/
public function removeRow($name) {
if(!isset($this[$name]))
@@ -149,7 +169,7 @@ public function addComponent(IComponent $component, $name) {
// <editor-fold defaultstate="collapsed" desc="other protected+ methods">
/**
- * Called when component is attached
+ * Called when component is attached to monitored object
* @param Presenter $obj
*/
protected function attached($obj) {
@@ -161,8 +181,8 @@ protected function attached($obj) {
/**
* Remove all subcontainers
*/
- protected function clear() {
- foreach($this->getComponents() as $name => $component)
+ protected function clearRows() {
+ foreach($this->getRows() as $name => $component)
unset($this[$name]);
}
@@ -171,7 +191,7 @@ protected function clear() {
*/
protected function rebuild() {
if($this->getForm()->isSubmitted()) {
- $this->clear();
+ $this->clearRows();
$containerPath = explode("-", $this->lookupPath("Nette\Forms\Form"));
$containerData = $this->getForm()->getHttpData();
foreach($containerPath as $step) { // goes through structure and extract neccesery data
@@ -191,7 +211,7 @@ protected function rebuild() {
* Create minimum count of rows if neccessery
*/
protected function fixMinRows() {
- $count = count($this->getComponents());
+ $count = $this->getRowCount();
if($count < $this->minimumRows) {
for($i = $count; $i < $this->minimumRows; $i++)
$this->createRow();
@@ -202,7 +222,7 @@ protected function fixMinRows() {
* Create default count of rows if neccessery
*/
protected function fixDefaultRows() {
- $count = count($this->getComponents());
+ $count = $this->getRowCount();
if($count < $this->defaultRows) {
for($i = $count; $i < $this->defaultRows; $i++)
$this->createRow();
@@ -212,17 +232,19 @@ protected function fixDefaultRows() {
/**
* Creates row of given name
* @param string|int $name
+ * @return FormContainer created container
*/
protected function createRow($name = null) {
- $items = $this->getComponents();
+ $items = $this->getRows();
$itemCount = count($items);
if($name === null) {
- $name = $itemCount == 0 ? "0" : (string)(1 + (int)end($items)->getName());
+ $name = $itemCount == 0 ? "0" : (string)(1 + (int)(end($items)->getName()));
}
$innerContainer = $this->addPrivateContainer($name);
call_user_func($this->factoryCallback, $innerContainer, $this, $this->getForm());
+ return $innerContainer;
}
/**
@@ -231,7 +253,7 @@ protected function createRow($name = null) {
* @return FormContainer added contanier
*/
private function addPrivateContainer($name) {
- $control = new FormContainer;
+ $control = new FormContainer();
$control->currentGroup = $this->currentGroup;
parent::addComponent($control, $name);
return $this[$name];
Oops, something went wrong.

0 comments on commit fc9f2d9

Please sign in to comment.