Live Validace formulářů

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
redhead
Člen | 1313
+
0
-

Kód zde je zastaralý, viz extras/live-form-validation

Zdravím,

zviditelňuji tímto tady můj LiveClientScript, který upravuje chování JS validace formulářů, zobrazováním chyb ‚za běhu‘ namísto po submitování formu. Není to úplně tak zjednodušené jak bych chtěl, ale funguje to zatím dobře.

Ukázka zde

Je to převážně stejný kód jako InstantClientScript, pouze byla upravena funkčnost JavaScriptu.

S nepozměněným kódem pro zobrazení/schování chyby je bohužel nutné do stránek/šablon includovat .js soubor, který v případě chyby:

  • zobrazuje textovou chybu v tagu ‚span‘ s CSS třídou ‚form-error-message‘ vedle chybného controlu
  • přidá chybnému controlu CSS třídu ‚form-control-error‘

Při odstranění chyby změny zmizí.

PHP classa

final class LiveClientScript extends Object
{
	/** @var string  JavaScript code */
	public $doAlert = 'addError(element, message)';

	/** @var string  JavaScript code */
	public $doRemove = 'removeError(element)';

	/** @var string  JavaScript code */
	public $doToggle = 'if (element) element.style.display = visible ? "" : "none";';

	/** @var string  JavaScript event handler name for validating form on submit */
	public $validateFunction;

	/** @var string  JavaScript event handler name for validating controls on fly */
	public $validateControlFunction;

	/** @var string  JavaScript event handler name */
	public $toggleFunction;

	/** @var string */
	public $validateScript;

	/** @var string */
	public $toggleScript;

	/** @var bool */
	private $central;

	/** @var Form */
	private $form;

	public function __construct(Form $form)
	{
		$this->form = $form;
		$name = ucfirst($form->getName()); //ucfirst(strtr($form->getUniqueId(), Form::NAME_SEPARATOR, '_'));
		$this->validateFunction = 'validate' . $name;
        $this->validateControlFunction = 'validate' . $name . 'Control';
		$this->toggleFunction = 'toggle' . $name;
	}

	public function enable()
	{
		$this->validateScript = '';
		$this->toggleScript = '';
		$this->central = TRUE;

		foreach ($this->form->getControls() as $control) {
			$script = $this->getValidateScript($control->getRules());
			if ($script) {
                $id = $control->htmlId;
                $setId = $control->getControlPrototype()->id;
                if($setId)
                    $id = $setId;
                $this->validateScript .=  "if(sender.id=='".$id."' ";
                    foreach($control->rules as $rule)
                        if ($rule->type === Rule::CONDITION) { // this is condition
                            $this->validateScript .= "|| sender.id=='".$rule->control->htmlId."' ";
                        }
                $this->validateScript .= ") {\n\t\t";
                $this->validateScript .=  "\n\t\tvar validated_element = document.getElementById('$id')\n\t";
				$this->validateScript .= "\n\t\t$script \n\t";
                $this->validateScript .= "} \n\t";
			}
			$this->toggleScript .= $this->getToggleScript($control->getRules());

			if ($control instanceof ISubmitterControl && $control->getValidationScope() !== TRUE) {
				$this->central = FALSE;
			}
		}

		if ($this->validateScript || $this->toggleScript)
        {
            foreach ($this->form->getComponents(TRUE, 'Nette\Forms\IFormControl') as $control) {
                $control->getControlPrototype()->onchange("$this->validateControlFunction(this);", TRUE);
                $control->getControlPrototype()->onblur("$this->validateControlFunction(this);", TRUE);
                if($control instanceof TextBase)
                    $control->getControlPrototype()->onkeyup("$this->validateControlFunction(this);", TRUE);;
            }
            foreach ($this->form->getComponents(TRUE, 'Nette\Forms\ISubmitterControl') as $control) {
                if ($control->getValidationScope()) {
                    $control->getControlPrototype()->onclick("return $this->validateFunction()", TRUE);
				}
            }
		}
	}

	/**
	 * Generates the client side validation script.
	 * @return string
	 */
	public function renderClientScript()
	{
		$s = '';

		if ($this->validateScript) {
			$s .= "function $this->validateControlFunction(sender) {\n\t"
			. "var element, message, res;\n\t"
            . "var b = true;\n\t"
            //. "switch(sender.id) {"
			. $this->validateScript
			//. "}\n"
			. "return b;\n}\n"
            . "function $this->validateFunction() {\n\t var b = true;\n";
            $i=0;
            foreach ($this->form->getComponents(TRUE, 'Nette\Forms\IFormControl') as $control)
            {
                $id = $control->htmlId;
                $setId = $control->getControlPrototype()->id;
                if($setId)
                    $id = $setId;
                $s .= "\t b = $this->validateControlFunction(document.getElementById('$id')) && b; \n";
            }
			$s .= "\treturn b; \n}\n";
		}

		if ($this->toggleScript) {
			$s .= "function $this->toggleFunction(sender) {\n\t"
			. "var element, visible, res;\n\t"
			. $this->toggleScript
			. "\n}\n\n"
			. "$this->toggleFunction(null);\n";
		}

		if ($s) {
			return "<script type=\"text/javascript\">\n"
			. "/* <![CDATA[ */\n"
			. $s
			. "/* ]]> */\n"
			. "</script>";
		}
	}

	private function getValidateScript(Rules $rules, $onlyCheck = FALSE)
	{
		$res = '';
		foreach ($rules as $rule) {
			if (!is_string($rule->operation)) continue;

			if (strcasecmp($rule->operation, 'Nette\Forms\InstantClientScript::javascript') === 0) {
				$res .= "$rule->arg\n\t";
				continue;
			}

			$script = $this->getClientScript($rule->control, $rule->operation, $rule->arg);
			if (!$script) continue;

			if (!empty($rule->message)) { // this is rule
				if ($onlyCheck) {
					$res .= "$script\n\tif (" . ($rule->isNegative ? '' : '!') . "res) { return false; }\n\t";

				} else {
					$res .= "$script \n\t\t"
						. "if (" . ($rule->isNegative ? '' : '!') . "res) { \n\t\t\t"
						. "message = " . json_encode((string) vsprintf($rule->control->translate($rule->message), (array) $rule->arg)) . "; "
						. "$this->doAlert ; b = false;"
						. "\n\t\t} else {\n"
                        . "\t\t\t$this->doRemove ;"
                        . "}";
				}
			}

			if ($rule->type === Rule::CONDITION) { // this is condition
				$innerScript = $this->getValidateScript($rule->subRules, $onlyCheck);
				if ($innerScript) {
					$res .= "$script\n\tif (" . ($rule->isNegative ? '!' : '') . "res) {\n\t\t" . str_replace("\n\t\t", "\n\t\t\t", rtrim($innerScript)) . "\n\t\t} else { $this->doRemove }\n\t";
					if (!$onlyCheck && $rule->control instanceof ISubmitterControl) {
						$this->central = FALSE;
					}
				}
			}
		}
		return $res;
	}

	private function getToggleScript(Rules $rules, $cond = NULL)
	{
		$s = '';
		foreach ($rules->getToggles() as $id => $visible) {
			$s .= "visible = true; {$cond}element = document.getElementById('" . $id . "');\n\t"
				. ($visible ? '' : 'visible = !visible; ')
				. $this->doToggle
				. "\n\t";
		}
		foreach ($rules as $rule) {
			if ($rule->type === Rule::CONDITION && is_string($rule->operation)) {
				$script = $this->getClientScript($rule->control, $rule->operation, $rule->arg);
				if ($script) {
					$res = $this->getToggleScript($rule->subRules, $cond . "$script visible = visible && " . ($rule->isNegative ? '!' : '') . "res;\n\t");
					if ($res) {
						$el = $rule->control->getControlPrototype();
						if ($el->getName() === 'select') {
							$el->onchange("$this->toggleFunction(this)", TRUE);
						} else {
							$el->onclick("$this->toggleFunction(this)", TRUE);
							//$el->onkeyup("$this->toggleFunction(this)", TRUE);
						}
						$s .= $res;
					}
				}
			}
		}
		return $s;
	}

	private function getValueScript(IFormControl $control)
	{
		$tmp = "element = document.getElementById(" . json_encode($control->getHtmlId()) . ");\n\t\t";
		switch (TRUE) {
		case $control instanceof Checkbox:
			return $tmp . "var val = element.checked;\n\t\t";

		case $control instanceof RadioList:
			return "for (var val=null, i=0; i<" . count($control->getItems()) . "; i++) {\n\t\t\t"
			. "element = document.getElementById(" . json_encode($control->getHtmlId() . '-') . "+i);\n\t\t\t"
			. "if (element.checked) { val = element.value; break; }\n\t\t"
			. "}\n\t";

		default:
			return $tmp . "var val = element.value.replace(/^\\s+|\\s+\$/g, '');\n\t\t";
		}
	}

	private function getClientScript(IFormControl $control, $operation, $arg)
	{
		$operation = strtolower($operation);
		switch (TRUE) {
		case $control instanceof HiddenField || $control->isDisabled():
			return NULL;

		case $operation === ':filled' && $control instanceof RadioList:
			return $this->getValueScript($control) . "res = val !== null;";

		case $operation === ':submitted' && $control instanceof SubmitButton:
			return "element=null; res=sender && sender.name==" . json_encode($control->getHtmlName()) . ";";

		case $operation === ':equal' && $control instanceof MultiSelectBox:
			$tmp = array();
			foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
				$tmp[] = "element.options[i].value==" . json_encode((string) $item);
			}
			$first = $control->isFirstSkipped() ? 1 : 0;
			return "element = document.getElementById(" . json_encode($control->getHtmlId()) . ");\n\tres = false;\n\t\t"
				. "for (var i=$first;i<element.options.length;i++)\n\t\t\t"
				. "if (element.options[i].selected && (" . implode(' || ', $tmp) . ")) { res = true; break; }";

		case $operation === ':filled' && $control instanceof SelectBox:
			return "element = document.getElementById(" . json_encode($control->getHtmlId()) . ");\n\t\t"
				. "res = element.selectedIndex >= " . ($control->isFirstSkipped() ? 1 : 0) . ";";

		case $operation === ':filled' && $control instanceof TextInput:
			return $this->getValueScript($control) . "res = val!='' && val!=" . json_encode((string) $control->getEmptyValue()) . ";";

		case $operation === ':minlength' && $control instanceof TextBase:
			return $this->getValueScript($control) . "res = val.length>=" . (int) $arg . ";";

		case $operation === ':maxlength' && $control instanceof TextBase:
			return $this->getValueScript($control) . "res = val.length<=" . (int) $arg . ";";

		case $operation === ':length' && $control instanceof TextBase:
			if (!is_array($arg)) {
				$arg = array($arg, $arg);
			}
			return $this->getValueScript($control) . "res = " . ($arg[0] === NULL ? "true" : "val.length>=" . (int) $arg[0]) . " && "
				. ($arg[1] === NULL ? "true" : "val.length<=" . (int) $arg[1]) . ";";

		case $operation === ':email' && $control instanceof TextBase:
			return $this->getValueScript($control) . 'res = /^[^@]+@[^@]+\.[a-z]{2,6}$/i.test(val);';

		case $operation === ':url' && $control instanceof TextBase:
			return $this->getValueScript($control) . 'res = /^.+\.[a-z]{2,6}(\\/.*)?$/i.test(val);';

		case $operation === ':regexp' && $control instanceof TextBase:
			if (strncmp($arg, '/', 1)) {
				throw new InvalidStateException("Regular expression '$arg' must be JavaScript compatible.");
			}
			return $this->getValueScript($control) . "res = $arg.test(val);";

		case $operation === ':integer' && $control instanceof TextBase:
			return $this->getValueScript($control) . "res = /^-?[0-9]+$/.test(val);";

		case $operation === ':float' && $control instanceof TextBase:
			return $this->getValueScript($control) . "res = /^-?[0-9]*[.,]?[0-9]+$/.test(val);";

		case $operation === ':range' && $control instanceof TextBase:
			return $this->getValueScript($control) . "res = " . ($arg[0] === NULL ? "true" : "parseFloat(val)>=" . json_encode((float) $arg[0])) . " && "
				. ($arg[1] === NULL ? "true" : "parseFloat(val)<=" . json_encode((float) $arg[1])) . ";";

		case $operation === ':filled' && $control instanceof FormControl:
			return $this->getValueScript($control) . "res = val!='';";

		case $operation === ':valid' && $control instanceof FormControl:
			return $this->getValueScript($control) . "res = function(){\n\t" . $this->getValidateScript($control->getRules(), TRUE) . "return true; }();";

		case $operation === ':equal' && $control instanceof FormControl:
			if ($control instanceof Checkbox) $arg = (bool) $arg;
			$tmp = array();
			foreach ((is_array($arg) ? $arg : array($arg)) as $item) {
				if ($item instanceof IFormControl) { // compare with another form control?
					$tmp[] = "val==function(){var element;" . $this->getValueScript($item). "return val;}()";
				} else {
					$tmp[] = "val==" . json_encode($item);
				}
			}
			return $this->getValueScript($control) . "res = (" . implode(' || ', $tmp) . ");";
		}
	}

	public static function javascript()
	{
		return TRUE;
	}

}

JS funkce

js vložte do .js souboru a includujte do šablon:

<script>
function hasClass(ele, cls) {
    var classes = ele.className.split(" ");
    for (var i=0;i<classes.length;i++)
        if (classes[i].indexOf(cls) == 0)
            return true;
    return false;
}
function addClass(ele,cls) {
	if (!this.hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
	if (hasClass(ele,cls)) {
		var classes = ele.className.split(" ");
        ele.className = '';
        i = 0;
        for (var i=0;i<classes.length;i++)
            if (classes[i].indexOf(cls) != 0)
            {
                if(i==0) ele.className += classes[i];
                else ele.className += ' '+classes[i];
                i++;
            }
	}
}
function addError(sender, message)
{
    addClass(sender, 'form-control-error');
    var id = sender.id+'_message';
    var el = document.getElementById(id);
    if(!el)
    {
        el = document.createElement('span');
        el.className = 'form-error-message';
        el.innerHTML = message;
        el.id = id;
        var parent = sender.parentNode;

        if(parent.lastchild == sender) {
            parent.appendChild(el);
        } else {
            parent.insertBefore(el, sender.nextSibling);
        }
    }
    else
    {
        el.style.display = 'inline';
        el.innerHTML = message;
    }
}
function removeError(sender)
{
    removeClass(sender, 'form-control-error');
    var el = document.getElementById(sender.id+'_message');
    if(el)
        el.style.display = 'none';
}
</script>

CSS třídy se upravte podle svého, třeba s nějakou hezkou error ikonkou a červeným písmem ;)

Použití

Nejdůležitější částí je zprovoznění:

//vytvoříme form:
$this->form = new AppForm($this, 'test');

// ... přidáme controls a validační pravidla ...

//a vytvoříme nový Client Script a v Rendereru ho nasetujeme (tím se přepíše původní Instant)
$this->form->getRenderer()->setClientScript(new LiveClientScript($this->form));

Pozn.: na pořadí tvorby controls a pravidel a client scriptu nezáleží.

Můžete si samozřejmě upravit chování zobrazení/schování chyby podle svého přepsáním doAlert a doRemove proměnných s vaší funkčností (případně použitím nějaké JS libky jako jQuery nebo Prototype), například:

//po nasetování LiveClientScriptu
$script = $this->form->getRenderer()->getClientScript();

$script->doAlert = '$(element).addClassName("control_error").highlight();';
$script->doRemove = '$(element).removeClassName("control_error");';

//kde můžete použít js proměnné 'element' jako DOM objekt controlu, který chybu vyvolal
//a string 'message' - validační chybová hláška

Editoval redhead (2. 9. 2009 18:34)

romansklenar
Člen | 655
+
0
-

Vypadá to pěkně, dobrá práce! Byla by škoda aby to tu zapadlo ve fóru, nechceš to přidat do extras?

Honza Marek
Člen | 1664
+
0
-

Zkoušim něco podobného, ale úplně jinak. Vytvořit clientScript, který by fungoval s pluginem jQuery Validation. Zatím mám takový koncept, kde snad funguje $formControl->addRule(Form::FILLED).

class jQueryValidationClientScript {

	/** @var Form */
	protected $form;

	public function __construct(Form & $form) {
		// kokotské (lepší by to bylo jako parametr renderClientScript)
		$this->form = $form;
	}

	public function enable() {
		// kokotské, ale ConventionalRenderer to vyžaduje
	}

	public function renderClientScript() {
		$rules = array();

		foreach ($this->form->getControls() as $control) {
			foreach	($control->getRules() as $rule) {
				if ($rule->operation === ":filled") {
					$rules[$control->getName()]["required"] = true;
				}
			}
		}

		if (!$this->form->getElementPrototype()->id) {
			$this->form->getElementPrototype()->id = $this->form->getName();
		}

		return "<script type=\"text/javascript\">\n"
			. "/* <![CDATA[ */\n"
			. 'jQuery("#' . $this->form->getElementPrototype()->id . '").validate('
			. json_encode($rules)
			. ');'
			. "/* ]]> */\n"
			. "</script>";
	}

}

Uvidím ještě kolik tam bude zádrhelů, třeba to časem vzdám, co já vim… .)

reflex
Člen | 28
+
0
-

Oprava chybky, odebira se spatna css trida.

<script>
function removeError(sender)
{
    removeClass(sender, 'form-error-message'); // bylo zde form_error
    var el = document.getElementById(sender.id+'_message');
    if(el)
        el.style.display = 'none';
}
</script>
redhead
Člen | 1313
+
0
-

reflex: pravda, opraveno (to je tak když člověk kopíruje a změní to tady místo aby to ještě vyzkoušel na localu :D )

btw: ta odebíraná třída měla být ‚form-control-error‘, neboť se odebírá od toho controlu a ne od té error zprávy.

Do extras to nejspíš dám.

Editoval redhead (14. 7. 2009 9:15)

redhead
Člen | 1313
+
0
-

přidáno do extras

Editoval redhead (14. 7. 2009 21:34)

smarma
Člen | 5
+
0
-

Perfektní věc. Jde to nějak globálně registrovat, aby se změna rendereru nemusela nastavovat u každého formuláře zvlášť?

Honza Marek
Člen | 1664
+
0
-

Vyrobit třídu BaseForm (poděděný AppForm) a nastavit to v konstruktoru ;)

// EDIT: Hm… tak to asi fungovat nebude. Leda by redhead hodil obsah metody enable do metody renderClientScript, pak by to mohlo jít.

Editoval Honza M. (18. 7. 2009 18:29)

redhead
Člen | 1313
+
0
-

Vyrobit třídu BaseForm (poděděný AppForm) a nastavit to v konstruktoru ;)

Vyzkoušel jsem to a jde to, nevím kde by to mohlo haprovat, Nette neznám tolik do hloubky, takže jestli je tam nějaká ultra mega über vychytávka na to tohle, tak možná. Lepší by bylo zeptat se na toto Davida.

Tento kód pro mě u dema fungoval.

class BaseForm extends AppForm
{
    public function  __construct(IComponentContainer $parent = null, $name = null)
    {
        parent::__construct($parent, $name);
        $this->form->getRenderer()->setClientScript(new LiveClientScript($this));
	//pripadne upraveni JS funkci pres doAlert a doRemove...
    }
}

posloucháš radiohead?

Radiohead ani ne, i když mi to last.fm furt cpe pod nos, jsem mimo jiné spíš metalhead :D

redhead
Člen | 1313
+
0
-

malý bug fix:

pro získání DOM objektu controlu, který chybu vyvolal, používejte proměnnou ‚validated_element‘ místo ‚element‘

$script->doAlert = 'mojeZobrazChybuFunkce(validated_element, message)';
$script->doRemove = 'mojeSkryjChybuFunkce(validated_element)';

EDIT: ehm, má někdo tušení, jak nahradit archiv v extras archivem novým (s novou verzí)?? Poslání stejnojmenného souboru nefunguje.

Editoval redhead (23. 7. 2009 23:37)

marek.dusek
Člen | 99
+
0
-

trosku jsem kod upravil tak, aby:

  1. korektne validoval i vice podminek na jednom elementu zaroven (puvodne pokud A bylo spatne a B dobre, B smazalo zpravy o A – jestli mi rozumite ;)
  2. na required hodnoty zacal upozornovat az po pokusu odeslat formular (po vzoru jquery.validate – osobne me rozciluje, kdyz jen kliknu do policka a uz na me vyskoci hlaska)

Snippety:

final class LiveClientScript extends Object
{
...
	/** @var string  JavaScript variable name indicating whether was the current form submitted */
	public $validateCalled;

...

	public function __construct(Form $form)
	{
		...
		$this->validateCalled = 'validate' . $name . 'Called';
	}

...

	public function renderClientScript()
	{
		$s = '';

		if ($this->validateScript) {
			$s .= "function $this->validateControlFunction(sender) {\n\t"
			. "var element, message, res;\n\t"
            . "var b = true;\n\t"
            //. "switch(sender.id) {"
			. $this->validateScript
			//. "}\n"
			. "return b;\n}\n"
			. "var $this->validateCalled = false;\n"
            . "function $this->validateFunction() {\n\t var b = true;\n";
            $s .= "\t $this->validateCalled = true;\n";
            ...
	}

...
	private function getValidateScript(Rules $rules, $onlyCheck = FALSE)
	{
		...
				} else {
					$res .= "$script \n\t\t"
						. "if (" . ($rule->isNegative ? '' : '!') . "res) { \n\t\t\t"
						. "message = " . json_encode((string) vsprintf($rule->control->translate($rule->message), (array) $rule->arg)) . "; "
						. "$this->doAlert ; \n\t\t\t"
						. "bb = !($this->validateCalled || "
							.intval(strtolower($rule->operation) != ':filled').");"
						. "\n\t\t}";
		....

		if ($res) {
			$res = <<<EOT
var bb = true;
		$res
		if (bb) {
			$this->doRemove;
		} else {
			b = false;
		}
EOT;
		}
		return $res;
	}
redhead
Člen | 1313
+
0
-

Pravda, pravdoucí. Díky Marku! Přidám to tam (až zjistím, jak nahrát nový archiv)

Jinak to s těma required controlama je myslím subjektivní, navíc mě se po kliknutí control nevaliduje, možná tak po stisku TAB, což vím že je nežádoucí, to by možná šlo ošetřit :)
Ale jinak s tím nemám problém..
A nebo udělat kompromis a udělat na to nějaký option, čili by se rozhodl až programátor zda validovat nebo nevalidovat až po submitu.
Nebo mě teď můžete všichni virtuálně ukamenovat a já pak uznám svou chybu :D

Jinak ví někdo jak nahrát novej archiv do extras???

Díky

Honza Marek
Člen | 1664
+
0
-

Teoreticky by mohlo stačit nahrát soubor se stejným jménem. Ale kdysi tam byla nějaká chyba, tak to třeba nebude fungovat.

redhead
Člen | 1313
+
0
-

to jsem ve čtvrtek zkoušel, ale nešlo, zůstaval starý archiv, teď zrovna nejsem u sebe na kompu, takže se na to nemůžu podívat

redhead
Člen | 1313
+
0
-

super, už je to tam, Marku, jestli dovolíš uvedu tě jako spolu autora ;)

Jan Jakeš
Člen | 177
+
0
-

Nepřidal bys ještě na stránku extras odkaz na tu živou ukázku? Máš jí přece někde na webu, ne? Akorát by to pak vždycky chtělo, aby ta ukázka byla k aktuální verzi…

redhead
Člen | 1313
+
0
-

je to tam :)

Jan Jakeš
Člen | 177
+
0
-

Díky, celkově výborná práce!

o5
Člen | 416
+
0
-

redhead na wiki v extras napsal:

Validuje se totiž control po controlu ‚za běhu‘ při vyplňování formuláře, nikoli až při odeslání formuláře

Co je u tebe ‚za běhu‘?

hodim si tam demo a vyplnuju:

  1. Username zadam samotne a
  2. Pokud stisknu TAB tak na me hned vyskoci Počet znaků musí být větší než 5 – ovsem nic jsem zatim nezadal, podle meho by se mela objevit informace, ze jsem zadal spatne prvni policko (to se objevi az po stisknuti Login)
redhead
Člen | 1313
+
0
-

no za běhu, během plnění formuláře a ne až při jeho odeslání.. opravdu nevím jak bych Live Validation řekl česky aby to vypovědělo co to dělá, jesli máš nějaký výraz proto, tak sem s ním. Prostě to na tebe neřve JS alertový okýnka – pro každou jednotlivou chybu, takže opravím jednu, klik zase chyba, opravím, klik, zase chyba, aaaa … Ale chyby se objeví najednou podle nějaké (upravitelné funkce)

to s tím TAB je hodně špatné.. taky mě to štve. Jde o to, že ta validace se provádí na (mimo jiné) i onKeyUp event, do nějž TAB samozřejmě patří. Možná by šlo nějak získat stisknutý tlačítko, ale řekl bych že to nebude cross-browser všude stejné. Ale podívám se na to, jesli by to šlo udělat.

A to s tím zobrazením až po kliknutí na Login: přečti si příspěvek od Marka Duška o pár postů výš (konkrétně bod 2)

redhead
Člen | 1313
+
0
-

tak to vypadá, že zas takový problém s tím TABem nebude :)

romansklenar
Člen | 655
+
0
-

Napadá někoho jak na ajaxovém formuláři zajistit odeslání až po úspěšné validaci pomocí této komponenty?

redhead
Člen | 1313
+
0
-

no, nevím, nic mě nenapadá, ajax. formuláře jsem totiž nikdy nepoužíval. Ale zkusím se na to podívat a najít nějaké řešení.

redhead
Člen | 1313
+
0
-

no řešení jsem našel, ale trochu prasácké. Při volání jQuery eventu click nad submitem se (ještě jednou, tedy celkem 2× při kliknutí) provede validace a pokud vrátí true volá se ajaxSubmit:

<script>
$("form :submit").click(function () {
	var fce = eval($(this).attr('onclick'));
	if(fce())
        	$(this).ajaxSubmit();
	return false;
});
</script>
romansklenar
Člen | 655
+
0
-

Já zkusil použít myšlenku Honzy Kuchaře, která tu někde na fóru zazněla: „nejdřív provést validaci, poté všechny eventy kromě odeslání a nakonec odeslat“, musí se ale sáhnout do pluginu na ajaxové odesílání formulářů od Honzy Marka, ještě to ale není ideální.

22
Člen | 1478
+
0
-

dotaz k live validaci..funguje to pouze nad appForm nebo i nad samostatnym Form?
Protože následující kod se nejak nema k činnosti :-(

<?php
$form1 	= new Form();
$form1	->addGroup();
$form1	->addText('mail','e-mail:')
	->setEmptyValue('@')
	->addRule(Form::FILLED,'E-mailova adresa musí být vyplněna.')
	->addRule(Form::EMAIL,'Neplatná e-mailova adresa.');
$form1	->addText('subject','Předmět:')
	->addRule(Form::FILLED,'Zadejte důvod Vaší zprávy.',50);
$form1	->addTextArea('text','Text:')
	->addRule(Form::FILLED,'Zadejte text Vaší zprávy. ')
	->addRule(Form::MAX_LENGTH,'Zpráva je příliš dlouhá',1024);
$form1	->addGroup();
$form1	->addSubmit('send','Odeslat');
$form1	->getRenderer()->setClientScript(new LiveClientScript($form1));
$form1	->render();
?>
redhead
Člen | 1313
+
0
-

Přiznám se, že jsem to nezkoušel, ale zkus se podívat co ti to vybleje ve zdrojáku stránky – pod formulářem by měl být <script> tag s tou validací.. (btw máš na stránce includovanej ten .js soubor co je v zipu?)

22
Člen | 1478
+
0
-

script includovanej, ve zdrojovem kodu vidim jen defaultni validaci z Nette podle myho nazoru.