CKEditor a nefunkční JS validace
- Endrju
- Člen | 147
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!
- Endrju
- Člen | 147
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
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
redhead napsal(a):
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
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)
- Endrju
- Člen | 147
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["frm-content"].updateElement()" name="frm-newsForm">
- Honza Kuchař
- Člen | 1662
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["frm-content"].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
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.
- Endrju
- Člen | 147
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
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
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
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!
- Endrju
- Člen | 147
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
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.
- Endrju
- Člen | 147
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["frm-content"].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["frm-content"].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["frm-content"].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
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.
- na1k
- Člen | 288
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
@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?