CKEditor a nefunkční JS validace

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

Zdravím :)!

Pořeboval bych poradit. Chci používat CKEditor ve svých formulářích, ale nefunguje mi dobře validace a nevím co s tím.

Po pravdě, když dobře nefunguje JS validace a vypnul bych ji, tak si pak nejsem jistý, zda by dobře fungovala i PHP validace. Měla by, ale rád bych 100% potvrzení :).

Mám takto vytvořený formulář v presenteru:

	protected function createComponentNewsForm()
	{
		$form = new AppForm;

		$renderer = $form->getRenderer();
		$renderer->wrappers['label']['container'] = NULL;

		$form->addTextArea('content')
			->addRule(Form::FILLED, 'Vyplňte aktualitu prosím.');

		$form->addSubmit('save', 'Uložit')->getControlPrototype()->class('default');
		$form->addSubmit('cancel', 'Zrušit')->setValidationScope(NULL);
		$form->onSubmit[] = callback($this, 'newsFormSubmitted');


		$form->addProtection('Odešlete formulář znovu prosím (bezpečtnostní token vypršel).');
		return $form;
	}

A takto vytvořenou šablonu pro tento formulář:

{block content}

<script type="text/javascript" src="{$basePath}/js/jquery-1.4.min.js"></script>
<script type="text/javascript" src="{$basePath}/js/ckeditor/ckeditor.js"></script>
<script type="text/javascript">
/* <![CDATA[ */
    jQuery(document).ready(function() {
        CKEDITOR.replace('content')
    });
/* ]]> */
</script>

<div id="title">{include #title}</div>

{widget newsForm}

{/block}

Prpblém je, že když vložím text jakkoli dlouhý (či prázdný), tak mi pokaždé vyskočí JS validace, abych zadal text aktuality. Když validaci ->addRule(Form::FILLED, 'Vyplňte aktualitu prosím.') odstraním, tak se záznam v pořádku do databáze uloží. Validaci ale odtranit nechci.

Dokázali by jste mi prosím poradit co jsem kde udělal špatně nebo v čem je chyba? Děkuji moc!

Honza Kuchař
Člen | 1662
+
0
-

Pokud se to odešle, tak PHP validace fungovat bude.

Endrju
Člen | 147
+
0
-

honzakuchar napsal(a):

Pokud se to odešle, tak PHP validace fungovat bude.

jj, diky, myslel jsem si že ano. Nicméně mě zaráží, že nefunguje ta JS validace. Mám pocit, že tu snad bylo nějaké takové podobné vlákno, ale nemohl jsem to najít. Mám dojem, že se tam psal něco o tom, že si to ten CKEditor nějak cachuje (jinými slovy, že do odeslání nenaplňuje fyzicky tu textarea). Tedy by se to snad měl dát nějak zjistit obsah CKEditoru nějakou jeho funkcí a ten obsah pak předat našmu JScriptu? Zdá se mi to ale celé nějaké složité a raději bych, aby to fungovalo standartním způsobem..

Nevíte někdo jak to provést?

Editoval Endrju (5. 3. 2010 15:07)

Tharos
Člen | 1030
+
0
-

V továrničce můžeš ke konci definice formuláře s editorem přidat:

<?php
$form->getElementPrototype()->onsubmit('CKEDITOR.instances["'.$form['content']->getHtmlId().'"].updateElement()');
?>

kde $form[‚content‘] je nějaká textarea. Dělá to přesně to, co je zapotřebí – ještě před JS validací to uloží obsah editoru do textarea, které nahrazuje. Pak JS validace funguje. Předpokládá to vytváření instance CKEditoru nahrazením textarea podle jejího id.

Endrju
Člen | 147
+
0
-

redhead napsal(a):

https://forum.nette.org/…a-je-prazdny

Dival jsem se co vse tam psali, ale v podstate tam neni napsano to vysledne reseni. Nebo ho tam nevidim. Pochopil jsem, ze by bylo vhodne pouizit neco jako:

$html = $form->getElementPrototype();
$html->onsubmit = "naplnTextAreaHodnotouZCKEditoru();";

Coz by bylo idelani resni, ale nevim presne jak..

Tharos napsal(a):

V továrničce můžeš ke konci definice formuláře s editorem přidat:

<?php
$form->getElementPrototype()->onsubmit('CKEDITOR.instances["'.$form['content']->getHtmlId().'"].updateElement()');
?>

kde $form[‚content‘] je nějaká textarea. Dělá to přesně to, co je zapotřebí – ještě před JS validací to uloží obsah editoru do textarea, které nahrazuje. Pak JS validace funguje. Předpokládá to vytváření instance CKEditoru nahrazením textarea podle jejího id.

Takhle jsem doufal, ze to nejak bude. Zkopiroval jsem to do kodu, ale nefunguje to. Zrejmne je potreba neco upravit. Nevim jak dostat text toho CKEditoru.. Zkousel jsem hledat v dokumentaci, jestli nemaji nejakou funkci, kterou bych ten text vytahl, ale bud hledam spatne nebo jsem to prehledl.

Kdyz jsem se dival pres Firebug, tak zdroj obsahuje toto:

Moje TextArea (schovana CKEditorem):

<textarea id="frm-content" name="content" rows="10" cols="40" style="visibility: hidden; display: none;"></textarea>

A tady je kod CKEditoru, ktery nahrazuje moji TextArea (mozna takto nejsnaze najdete to co bych potreboval):

http://www.pastebin.cz/3d1d97d1d2d491 (nechtěl jsem vkládat celý zdroj tady, kvůli přehlednoti)

Jeste pridavam dva odkazy na CKEditor dokomentaci.. treba by to slo nejak pouzit (bohuzel tam nemaji nejake priklady..):

  • getHtml()
  • getText() (tohle by bylo lepsi, protože me zajima obsah jako text a ne to, že tam jsou nejake HTML znacky bez obsahu – tedy nejakeho textu)

Editoval Endrju (5. 3. 2010 16:05)

Endrju
Člen | 147
+
0
-

Trochu jsem jeste patral v samples dodanych u CKEditoru a zjistil jsem nasledujici:

V souboru /ckeditor/_samples/api.html je nasledujici JS funkce, ktera vraci obsah editoru:

function GetContents()
{
	// Get the editor instance that we want to interact with.
	var oEditor = CKEDITOR.instances.editor1; // editor1 predstavuje ID textarea - tedy muj 'content'

	// Get the editor contents
	alert( oEditor.getData() );
}

Kdyz tuto funkci upravim pouzitim funkce getText(), tak bude vracet inner text tech HTML prvku.

Ovsem v popisu metody mimo jine sotji: Only in IE (which uses innerText) nebude to pusobit potize v jinych prohlizecich? (ve Firefoxu mi to jde)

Kod pak bude vypadat takto:

function GetContents()
{
	// Get the editor instance that we want to interact with.
	var oEditor = CKEDITOR.instances.editor1;

	// Get the editor contents
	var html = oEditor.getData();

	if  (html == '')
	{
  		alert( 'Editor neobsahuje zadny text (zadejte text)' );
	}
	else
	{
	    var element = CKEDITOR.dom.element.createFromHtml( oEditor.getData() );
		alert( element.getText() );  // "Sample text."
	}
}

Ted ale jak tohle zpracovat (upravit), abych to mohl v tovarnicce na formular pouzit nejak takto:

$form->onSubmit[] = array($this, 'GetContents');

Dekuji za napady

Editoval Endrju (5. 3. 2010 16:43)

Tharos
Člen | 1030
+
0
-

To je opravdu zvláštní, zkus debugovat, proč u tebe ta metoda updateElement() nepracuje podle předpokladů. Já ji ve formulářích používám bez problémů a její použití mi připadá jako takové nejhezčí řešení. Kdyžtak koukni ještě třeba na api.

Editoval Tharos (5. 3. 2010 16:48)

Endrju
Člen | 147
+
0
-

No a nemuze byt spatne ten zapis:

$form->getElementPrototype()->onsubmit('CKEDITOR.instances["'.$form['content']->getHtmlId().'"].updateElement()');

protoze, kdyz se podivam do HTML zdroje, tak pak mam ve formulari vygenerovano toto:

<form ... onsubmit="CKEDITOR.instances[&quot;frm-content&quot;].updateElement()" name="frm-newsForm">
Tharos
Člen | 1030
+
0
-

Zkus to ještě bez těch uvozovek (tj. jenom s těmi apostrofy), nedošlo mi, že to je kontext, ve kterém Nette v šablonách escapuje.

Honza Kuchař
Člen | 1662
+
0
-

Endrju napsal(a):

No a nemuze byt spatne ten zapis:

$form->getElementPrototype()->onsubmit('CKEDITOR.instances["'.$form['content']->getHtmlId().'"].updateElement()');

protoze, kdyz se podivam do HTML zdroje, tak pak mam ve formulari vygenerovano toto:

<form ... onsubmit="CKEDITOR.instances[&quot;frm-content&quot;].updateElement()" name="frm-newsForm">

použij '. Tj.

$form->getElementPrototype()->onsubmit('CKEDITOR.instances[\''.$form['content']->getHtmlId().'\'].updateElement()');

Editoval honzakuchar (5. 3. 2010 17:10)

Endrju
Člen | 147
+
0
-

Napsal jsem to tedy takto:

$form->getElementPrototype()->onsubmit('CKEDITOR.instances['.$form['content']->getHtmlId().'].updateElement()');

Vygenerovalo to:

<form ... onsubmit="CKEDITOR.instances[frm-content].updateElement()" name="frm-newsForm">

A stale to nefuguje – sposuti se JS validace, ktera mi rika, ze jsem nezadal zadny text.

Neudelal jsem neco spatne? Neni v Nette neco jinak?

Kdyz jsi psal, ze jsi to takto stejne resil, tak se podivat jak presne to mas, zda to odpovida to co pises tady.

Honza Kuchař
Člen | 1662
+
0
-

Updatoval jsem příspěvek #11

Tharos
Člen | 1030
+
0
-

Endrju napsal(a):
Kdyz jsi psal, ze jsi to takto stejne resil, tak se podivat jak presne to mas, zda to odpovida to co pises tady.

Pošlu Ti skeleton i s CKčkem, kde to funguje. Dej mi jenom vteřinku. :)

Endrju
Člen | 147
+
0
-

honzakuchar napsal(a):

Updatoval jsem příspěvek #11

Jo, upravil jsem to tak a vysledne HTML je:

<form ... onsubmit="CKEDITOR.instances['frm-content'].updateElement()" name="frm-newsForm">

Ale porad to nefunguje.

Kdyz v API je u updateElement() napasno, ze by kod mel byt CKEDITOR.instances.editor1.updateElement();, tak nemelo by to vysledne HTML vypadat spise takto:

<form ... onsubmit="CKEDITOR.instances.frm-content.updateElement()" name="frm-newsForm">

Nicmene tohle jsem zkousel taky a nic.

Tak jsem zkusil takovy experiment a napsal do tovarnicky na formluar:

$form->getElementPrototype()->onsubmit('alert(\'ahoj\');');

coz vygeneruje:

<form ... onsubmit="alert('ahoj');" name="frm-newsForm">

ale po submit formulare to tento alert stejne nevypise. Neni neco spatne uplne nekde jinde? Nebo cela myslenka?

redhead
Člen | 1313
+
0
-

zkus to udělat přes jQuery, a to tak, že všem formulářům s CKEditorem, zaregistruješ event handler pro submit, ale (teď je to důležité:) musí být zaregistrován dříve, než ajaxová registrace, aby to šlo popořadě. Protože mám pocit, že ti to ten kód nespustí proto, že ajaxový handler volá na konci ‚return false‘, který zamezuje vykonání dalšího kódu tohoto eventu.

Tharos
Člen | 1030
+
0
-

Hmm, tak jsem zjistil, že ani má situace není ideální. S tím zápisem mi to pozná, že obsah toho elementu není prázdnej (asi teda nějak záhadně, budu muset prozkoumat, co se tam vlastně děje), ale složitější validační pravidla (jako třeba minimální délka textu) mi taky nefungují. Ještě se na to teda taky blíž podívám.

Endrju
Člen | 147
+
0
-

to redhead: omlouvam se, ale nejsem tak sbehly co se techot veci tyka, mohl by jsi napat kusy kodu na kterych bych dokazal vycist jake presne upravy mam udelat, aby to bylo tak jak rikas?

to Tharos: Dobre, byl bych moc rad, kdyby se ti to podarilo vyresit a napsal pak sem reseni.

A pro vsechny ostatni: Kdyby jste nahodou vedeli, jak to udelat, mohli by jste sem napsat veskery kod potrebnych uprav?

Dekuji!

redhead
Člen | 1313
+
0
-

nějak jsem si to spojil s jiným příspěvkem, takže jsem do toho zamíchal i ajax.. takže co sem napsal je asi blbost. Nicméně se zeptám, nepoužíváš ajaxové formuláře? Pokud ne, je opravdu divné, že se ti nespustí ani ten alert v onsubmit.

Endrju
Člen | 147
+
0
-

to redhead: Zatim jestse ani nevim jak se ajaxove formulare zapinaji. Vychazim z ukazkove aplikace modules-usage z distribuce NetteFramework-0.9.3-PHP5.2\examples\modules-usage, takze si myslim, ze ajaxove formulare nepouzivam.

Zkuste vzit tuto ukazku, udelat si tovarnicku na formular s text areou. Na ni pak v sablone namapovat CKEditor (mam verzi 3.1, ve verzi 3.2 zlobilo vykreslování dialogů) a rozchodit JS validaci. Bez CKEditoru to funguje v poradku.

Neslo by sem zavolat Davida, aby se podival v cem by mohl byt problem?

Etch
Člen | 403
+
0
-

Endrju :

Zkoušel sem to na Nette 1.0dev s CKEditorem 3.2 a funguje to naprosto bez problému.

Tharos napsal(a):
Hmm, tak jsem zjistil, že ani má situace není ideální. S tím zápisem mi to pozná, že obsah toho elementu není prázdnej (asi teda nějak záhadně, budu muset prozkoumat, co se tam vlastně děje), ale složitější validační pravidla (jako třeba minimální délka textu) mi taky nefungují. Ještě se na to teda taky blíž podívám.

To že ti nefunguje minimální délka textu a další věci je naprosto normální a není na tom moc co zkoumat. Pokud napíšeš v CKEditoru jen jedné písmeno „A“ tak se validace bude chovat následovně.

Min 3 znaky - Projde
Max 5 znaků - Neprojde
Min 8 znaků - Projde
Min 9 znaků - Neprojde

K validaci přijdou data už jako html tedy A = <p>A</p> v tom je celá nesrovnalost.

Tharos
Člen | 1030
+
+1
-

Etch: No jo, vždyť jsem vů… To je tak, když člověk něco zkouší narychlo a mozek má někde o projekt vedle.

Takže to samozřejmě funguje, zde je kompletní příklad s CKEditorem a fungující JS validací.

Editoval Tharos (6. 3. 2010 10:36)

Endrju
Člen | 147
+
0
-

Trochu jsem porovnaval vygenerovane HTML zdrojaky a pak zkousel upravovat tovarnicku na formular. Zjistil jsem, co dela problem (coz vola po dalsim reseni – viz. tucny text na konci prispevku..).

Vsimete si v kodu toho, co jsem zakomentoval (->setValidationScope(NULL)):

	protected function createComponentNewsForm()
	{
		$form = new AppForm;

		$renderer = $form->getRenderer();
		$renderer->wrappers['label']['container'] = NULL;

		$form->addTextArea('content')
			->addRule(Form::FILLED, 'Vyplňte aktualitu prosím.');

		$form->addSubmit('save', 'Uložit')->getControlPrototype()->class('default');
		$form->addSubmit('cancel', 'Zrušit');//->setValidationScope(NULL);

		$this->template->textareaId = $form['content']->getHtmlId();

		$form->getElementPrototype()->onsubmit('CKEDITOR.instances["'.$this->template->textareaId.'"].updateElement()');

		$form->onSubmit[] = callback($this, 'newsFormSubmitted');


		$form->addProtection('Odešlete formulář znovu prosím (bezpečtnostní token vypršel).');
		return $form;
	}

To, ze je na tlacitko „Zrusit“ nastaveno setValidationScope(NULL); zpusobi, validacni JS je mapovan na submit tlacitko „Pridat“ na udalost onClick. HTML pak kod vypada takto:

<form action="/SK_Celadna/document_root/admin/news/add/?do=newsForm-submit" method="post" onsubmit="CKEDITOR.instances[&quot;frm-content&quot;].updateElement()" name="frm-newsForm">
	<table>
		<tr class="required">
			<label class="required" for="frm-content">
			</label>
			<td>
				<textarea cols="40" rows="10" name="content" id="frm-content"></textarea>
				<!-- Zde na tomto miste je kod CKEditoru ... ten k tomu co chci ukazat nepotrebujem -->
			</td>
		</tr>
		<tr>
			<td>
				<input type="submit" class="default button" onclick="return nette.validateForm(this)" name="save" id="frmnewsForm-save" value="Přidat" />
				<input type="submit" class="button" name="cancel" id="frmnewsForm-cancel" value="Zrušit" />
			</td>
		</tr>
	</table>
	<div>
		<input type="hidden" name="_token_" id="frmnewsForm-_token_" value="102e4bf43d9635e3abfbcd136a6c6842" />
	</div>
</form>

No a v tom je, zda se, kamen urazu.

Kdyz totiz v tovarnicce zakomentuju setValidationScope(NULL); na tlacitku „Zrusit“, jak je uvedeno vyse, tak validacni JS, ktery byl mapovan na submit tlacitko „Pridat“ na udalost onClick se presune na element „form“ do udalosti onSubmit a pak to „funguje“. HTML kod pak vypada takto:

<form name="frm-newsForm" onsubmit="CKEDITOR.instances[&quot;frm-content&quot;].updateElement();return nette.validateForm(this)" method="post" action="/SK_Celadna/document_root/admin/news/add/?do=newsForm-submit">
	<label for="frm-content" class="required">
	</label>
	<table>
		<tbody>
			<tr class="required">
				<td>
					<textarea id="frm-content" name="content" rows="10" cols="40" style="visibility: hidden; display: none;"></textarea>
					<!-- Zde na tomto miste je kod CKEditoru ... ten k tomu co chci ukazat nepotrebujem -->
				</td>
			</tr>
			<tr>
				<td>
					<input type="submit" value="Přidat" id="frmnewsForm-save" name="save" class="default button">
					<input type="submit" value="Zrušit" id="frmnewsForm-cancel" name="cancel" class="button">
				</td>
			</tr>
		</tbody>
	</table>
	<div>
		<input type="hidden" value="102e4bf43d9635e3abfbcd136a6c6842" id="frmnewsForm-_token_" name="_token_">
	</div>
</form>

Schvalne jsem napsal do uvozovek, ze to funguje, protoze to porad neni dobre reseni.

Kdyz ted stisknu tlacitko „Zrusit“, tak se stale provadi JS validace. Validace je totiz nyni mapovana na cely formular (v udalosi onSubmit ve formulari) a protoze tlacitko „Zrusit“ je take typu sumbmit, tak vyvolava JS validaci.

Ted by me zajimalo, zda je dobry napad, aby bylo ve formulari vice submit tlacitek nez jen to jedine, ktere slouzi pro odeslani formulare?

Paklize ano (muze byt takova situace, kdy to tak budu chtit), tak by JS validace by mela byt mapovana prave a jen na stisknuti submit tlacitka „Pridat“ tak, jak tomu je ve vychozim reseni z distribuce NetteFramework-0.9.3-PHP5.2\examples\CD-collection.

Tedy otazka k reseni zni: Jak presunout JS validaci jen na submit tlacitko „Pridat“ tak, aby vysledne HTML vypadalo nasledovne (jen dedukuju, ze takto by to asi melo vypadat)?

<form action="/SK_Celadna/document_root/admin/news/add/?do=newsForm-submit" method="post" name="frm-newsForm">
	<table>
		<tr class="required">
			<label class="required" for="frm-content">
			</label>
			<td>
				<textarea cols="40" rows="10" name="content" id="frm-content"></textarea>
				<!-- Zde na tomto miste je kod CKEditoru ... ten k tomu co chci ukazat nepotrebujem -->
			</td>
		</tr>
		<tr>
			<td>
				<input type="submit" class="default button" onclick="CKEDITOR.instances[&quot;frm-content&quot;].updateElement();return nette.validateForm(this)" name="save" id="frmnewsForm-save" value="Přidat" />
				<input type="submit" class="button" name="cancel" id="frmnewsForm-cancel" value="Zrušit" />
			</td>
		</tr>
	</table>
	<div>
		<input type="hidden" name="_token_" id="frmnewsForm-_token_" value="102e4bf43d9635e3abfbcd136a6c6842" />
	</div>
</form>

Editoval Endrju (6. 3. 2010 18:09)

Etch
Člen | 403
+
0
-

ale v tom ti přece nic nebrání to tak udělat

protected function createComponentNewsForm()
{
        $form = new AppForm;

        $renderer = $form->getRenderer();
        $renderer->wrappers['label']['container'] = NULL;

        $form	->addTextArea('content')
		->addRule(Form::FILLED, 'Vyplňte aktualitu prosím.');

        $form	->addSubmit('save', 'Uložit')
		->getControlPrototype()
		->onclick('CKEDITOR.instances["'.$form['content']->getHtmlId().'"].updateElement()')
		->class('default');

        $form	->addSubmit('cancel', 'Zrušit')->setValidationScope(NULL);

        $form	->addProtection('Odešlete formulář znovu prosím (bezpečtnostní token vypršel).');

        $form	->onSubmit[] = callback($this, 'newsFormSubmitted');

        return $form;
}

je to naprosto stejné jako předtím jen se to navěsí na onClick událost tlačítka Uložit.

Endrju
Člen | 147
+
0
-

Etch: moc diky, nevedel jsem, ze to jde primo takto krasne a jednoduse :)!

Editoval Endrju (7. 3. 2010 4:18)

na1k
Člen | 288
+
0
-

Doplním své řešení – nelíbí se mi vazba na odesílací tlačítko, tak vše řeším v configu (za předpokladu že používáte jQuery adapter). Před odesláním formuláře se u všech CKEditorů ve stránce updatne formulářový element. Dalo by se to ještě vylepšit, „hlídat“ jen formuláře které obsahují editor a updatovat jen ten jeden konkrétní, ale stačilo mi to takhle. JS validace samozřejmě funguje a v definici formu není nutné nic víc, než nanejvýš nastavit css třídu pro textarea :)

<script>
$(function(){
   $('textarea.editor').ckeditor(
      function() {
         $('form').submit(function(){
            for (inst in CKEDITOR.instances)
               CKEDITOR.instances[inst].updateElement();
         });
      },
      {
         language : 'cs',
         resize_dir : 'vertical',
	 ...
      }
   );
});
</script>
duke
Člen | 650
+
0
-

@na1k Toto řešení by se mi zamlouvalo, ale bohužel mi nefunguje. Problém je v tom, že netteForms.js si onsubmit event zaregistrují jiným způsobem než jquery, což pak ve výsledku vede k tomu, že se validace v netteForms.js provede před voláním updateElement(), bez ohledu na to, v jakém pořadí načtu tyto javascripty (z toho, co jsem k tomu našel, je toto pořadí relevantní jen v případě použití jquery).

Co funguje, je inline zápis onsubmit u tagu form (který má, jak se zdá, přednost před jinak zaregistrovanými event handlery), ale dal bych přednost unobtrusive javascriptu a tam vidím cestu buď v přepisu netteForms.js tak, aby používalo pro registraci eventů jquery (existuje už taková varianta netteForms.js?), nebo přidáním callbacku onBeforeValidate do netteForms.js.

Nebo existuje lepší řešení, které jsem přehlédl?