HeaderControl – konec trápení s HTML hlavičkou!
- Ondřej Mirtes
- Člen | 1536
Diskuse ke stránce HeaderControl
Už mě nebavilo vždy někde shánět, kopírovat a přepisovat údaje v HTML hlavičce, tak jsem si na to napsal komponentu :)
Co to umí?
- nastavovat DOCTYPE (HTML 4.01, HTML 5 a XHTML 1.0) všech tří úrovní (Strict, Transitional, Frameset) a posílat i kontroverzní (jediný správný a také značně problémový) Content-Type pro XHTML – application/xhtml+xml.
- nastavovat jazyk dokumentu
- hrátky s
<title>
– v konstruktoru předáte výchozí (např. Zdroják.cz) a pak ho můžete přepisovat/přidávat kaskádovitě další (např. kategorii a jméno článku – title pak bude mít podobuNadpis článku - Kategorie - Název webu
(pořadí a oddělovač jsou volitelné) - favikonu (kontroluje, zda soubor existuje)
- RSS kanály
- libovolné meta tagy (obsahuje zvláštní metody pro ty
nejčastější – author, description, keywords, robots)
- keywords lze přidávat kaskádovitě podobně jako title
- elegantní volání setterů pomocí fluent rozhraní
- propojení s WebLoaderem Honzy Marka – využívá ho pro přidávání CSS a JS skriptů, včetně widgetového volání
Příklad továrničky
protected function createComponentHeader() {
$header = new HeaderControl(HeaderControl::HTML_5, 'cs', 'Example title');
$header->setTitleSeparator(' | ')
->setTitlesReverseOrder(true)
->addKeywords('one')
->addKeywords(array('two', 'three'))
->setDescription('Our example site')
->setRobots('index,follow') //of course ;o)
->addRssChannel('News', 'Rss:')
->addRssChannel('Comments', 'Rss:comments');
//CssLoader
$css = $header['css'];
$css->sourcePath = APP_DIR . '/templates/WebModule/css';
$css->sourceUri = Environment::getVariable('baseUri') . 'temp';
$css->tempUri = $css->sourceUri;
$css->tempPath = WWW_DIR . '/temp';
//JavascriptLoader
$js = $header['js'];
$js->tempUri = Environment::getVariable('baseUri') . 'temp';
$js->tempPath = WWW_DIR . '/temp';
$js->sourcePath = APP_DIR . '/templates/WebModule/js';
return $header;
}
Příklad šablony
{widget header}
Příklad šablony s widgetovým přidáváním RSS kanálů, CSS stylů a JS skriptů
{widget header:begin}
{widget header:rss 'News' => 'Rss:', 'Comments' => 'Rss:comments'}
{widget header:css 'reset.css', 'default.css', 'screen.css'}
{widget header:js 'jquery.js', 'jquery.nette.js', 'jquery.antispam.js', 'web.js'}
{widget header:end}
Výstup
<!DOCTYPE html>
<html>
<head>
<meta content="cs" http-equiv="Content-Language">
<meta content="text/html" http-equiv="Content-Type">
<title>Example title</title>
<link rel="shortcut icon" href="/favicon.ico">
<meta name="keywords" content="key,words,nette">
<meta name="description" content="Site description">
<meta name="robots" content="index,follow">
<link rel="stylesheet" type="text/css" href="/temp/cssloader-649fdaaa20fcd9f828ad9c25ac8d1596.css">
<script type="text/javascript" src="/temp/jsloader-367b76d07fd0a00b4316125595e1635d.js"></script>
<link rel="alternate" type="application/rss+xml" title="Sample RSS Channel" href="/rss/sample">
</head>
Díky za každou reakci!
Editoval Ondřej Mirtes (25. 1. 2010 23:28)
- Inza
- Člen | 330
Vypadá nádherně! Prosím prosím, přidáš ji i do Nette/Extras? – https://componette.org/search/?q=cs Děkuji…
- Honza Marek
- Člen | 1664
Dobré… Může tam být i více těch WebLoaderů? Třeba styly pro tisk, komprimované a nekomprimovatelné js a tak.
- Ondřej Mirtes
- Člen | 1536
Odpovídám :) Není problém to tam dodat, ale nefunguje to (zatím) dynamicky. Každopádně to beru jako feature request a nějak se o to pokusím :)
Až tam tohle přidám (a možná ještě nějaké widgetové volání pro title) + phpDocy, tak to dám do extras. Možná bych se pokusil i o unit testy, abych věděl, že vše funguje jak má.
- Honza Marek
- Člen | 1664
Odhodlal jsem se lehce prozkoumat zdroják a měl bych pár nápadů.
Možná by nebylo problém, kdyby tato komponenta s WebLoaderem nebyla integrovaná vůbec. O „integraci“ by se postaral komponentový model Nette.
Továrnička by vypadala třeba takto:
public function createComponentHeader() {
$header = new HeaderControl;
// nastavení HeaderControlu
$header->...
$header["css"] = new CssLoader;
// nastavení CssLoaderu
$header["printCss"] = clone $header["css"]; // snad funguje klonování komponent, nezkoušel jsem
$header["printCss"]->media = "print";
$header["js"] = new JavaScriptLoader;
// nastavení JavaScriptLoaderu
return $header;
}
Akorát by se musely pořešit metody renderCss, renderPrintCss atd. kvůli widgetům. Mohlo by to jít přes __call. Taky se mi nelíbí, že konstruktor této komponenty kompletně přepisuje konstruktor rodiče, takže tahle komponenta se chová jinak než všechny ostatní v Nette.
Pak bych řek, že je asi zbytečné mít na headerEnd vlastní šablonu :-D
Neber to jako kritiku, já jsem prostě náročnej. Pokud mi něco nevyhovuje na 100%, tak si to napíšu znova, pokud to jde. Ulehčit tvorbu hlavičky je ale výborný nápad!
- Ondřej Mirtes
- Člen | 1536
Jojo, dobrý nápad, že by to nemuselo být závislé na WebLoaderu (i když, co jiného by tam uživatel přidával?), ale nevím, jestli zápis s $header[‚css‘] = new CssLoader bude fungovat. Ale s addComponent asi jo. Pokud ne, tak přidám metodu addLoader, která bude přijímat instance typu WebLoader a pořeším ty render metody. Taky jsem nad nimi přemýšlel a došel ke stejnému závěru :) Akorát nevím, jak bych měl pořešit převod mezi camelCaps a PascalCaps, jestli stačí kapitalizovat první písmeno, nebo tam může vzniknout nějaký problém. Podívám se do zdrojáků Nette.
Šablona pro end – jak bych to měl řešit,
echo </head>
přímo v kódu třídy? Jsem radši pro
systémové řešení, i když je možná zbytečné :)
Stále se bojím, že je tam nějaký příšerný bug a že mě pak zastřelíte, holt to chce ty unit testy :)
- Honza Marek
- Člen | 1664
LastHunter napsal(a):
Jojo, dobrý nápad, že by to nemuselo být závislé na WebLoaderu
Především to umožní větší variabilitu, člověk tam bude mít kolik WebLoaderů, kolik bude chtít a pokud si vymyslí nějakou vlastní zázračnou komponentu, tak ji tam může přidat taky.
Šablona pro end – jak bych to měl řešit,
echo </head>
přímo v kódu třídy? Jsem radši pro systémové řešení, i když je možná zbytečné :)
Ano :-D Myslim, že šablony by měly být jen pomocníkem. Ve WebLoaderu taky šablony nejsou, protože by věc jen komplikovaly.
Stále se bojím, že je tam nějaký příšerný bug a že mě pak zastřelíte, holt to chce ty unit testy :)
Předpokládám i bez unit testů, že tvoje metoda renderJs se chová jinak než metoda render u JavaScriptLoaderu :-D
P.S.: Ještě bych prosil vyjádření k tomu konstruktoru… Jednak je tam ta výhrada, kterou už jsem přednesl a druhak mi ani ty parametry nesedí. Jazyk já obvykle třeba vůbec nenastavuju (doufám, že odpovědí nebude, že jsem dobytek…)
Další věc, přišlo by mi přirozenější nastavovat doctype jako celek. XHTML1_STRICT a tak.
- bazo
- Člen | 620
zdravim,
da sa s tymto urobit takato vec?
v hlavnej sablone layout budem mat napriklad
{control header:begin}
{control header:css 'reset.css', 'default.css'}
{control header:js 'jquery.js', 'jquery.nette.js'}
{control header:end}
a v sablone pre nejaky konkretny prezenter bude
{control header:begin}
{control header:css 'dalsie.css'}
{control header:js 'dalsie.js'}
{control header:end}
a vysledok na stranke prezenteru bude
<head>
<link rel="stylesheet" type="text/css" href="reset.css" />
<link rel="stylesheet" type="text/css" href="default.css" />
<link rel="stylesheet" type="text/css" href="dalsie.css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.nette.js"></script>
<script type="text/javascript" src="dalsie.js"></script>
</head>
- Honza Marek
- Člen | 1664
Jednak nedá a druhak header:css a header:js dělají něco trochu sofistikovanějšího
- Ondřej Mirtes
- Člen | 1536
No, imho by to přes dědičnost bloků a volání {include #parent} (dělá se to takhle?) šlo.
layout:
...
{block #css}{control header:css 'screen.css'}{/block}
...
šablona:
{block #css}{include #parent}{control header:css 'dalsi.css'}{/block}
- bazo
- Člen | 620
kedze sa to neda tak je tato komponenta dost zbytocna pretoze ten spakovany js a css si tam mozem vlozit len pomocou toho webloaderu. bolo by fajn keby to vedelo aj takuto vec, lebo potom skript, ktory chcem pouzit napr. iba na jednom formulari sa musi tahat pri vsetkych strankach
LastHunter: presne toto som chcel dosiahnut, funguje to. dik!
Editoval bazo (30. 9. 2009 22:01)
- Honza Marek
- Člen | 1664
Ani pomocí WebLoaderu nemusíš skript pro jeden formulář tahat po všech stránkách. Buď je tam možnost vypnout spojování souborů anebo
{control js 'jquery.js', 'nette.js'}
{control js 'form.js'}
vloží ty skripty dva, jeden se zbalenýma prvníma dvěma souborama a druhý s tím třetím souborem.
// EDIT: LastHunterův příklad s dědičností bloků vlastně taky zobrazí dva WebLoadery pod sebou.
Editoval Honza M. (30. 9. 2009 22:03)
- bazo
- Člen | 620
ale toto ten skript nevlozi medzi <head></head> ale vypise to na miesto kde sa to widgetove volanie nachadza.
mne ide o to, ze v @layout mam napevno nalinkovane napr jquery a na jednej stranke chcem zobrazit napriklad editor. alebo nemat css pre vsetky presentery natahane naraz v jednom subore ale kazdy presenter, action si stiahne vlastny css a prida to medzi <head></head>
- Ondřej Mirtes
- Člen | 1536
bazo napsal(a):
ale toto ten skript nevlozi medzi <head></head> ale vypise to na miesto kde sa to widgetove volanie nachadza.
mne ide o to, ze v @layout mam napevno nalinkovane napr jquery a na jednej stranke chcem zobrazit napriklad editor. alebo nemat css pre vsetky presentery natahane naraz v jednom subore ale kazdy presenter, action si stiahne vlastny css a prida to medzi <head></head>
Ano, to ti ty bloky zařídí. Stačí mít ten {block #css} v layoutu v <head>. A v šabloně to volat:
{block #css}{include #parent}{control header:css 'dalsi.css'}{/block}
{block #content}
text dané šablony...
Editoval LastHunter (30. 9. 2009 22:24)
- Honza Kuchař
- Člen | 1662
Ahoj, je nějaká jednoduchá možnost, jak to co se mi vypíše mezi <title> získat jako string?
- Ondřej Mirtes
- Člen | 1536
honzakuchar napsal(a):
Ahoj, je nějaká jednoduchá možnost, jak to co se mi vypíše mezi <title> získat jako string?
Máš to tam :)
Jinak se omlouvám, že jsem zatím neudělal ty slíbená todo, nebyl zatím čas :)
- Honza Kuchař
- Člen | 1662
Jinak se omlouvám, že jsem zatím neudělal ty slíbená todo, nebyl zatím čas :)
Myslím, že se nemáš za co omlouvat.
Máš to tam :)
Díky ;)
- Ondřej Mirtes
- Člen | 1536
Tak jsem dnes v komponentě provedl divoké změny – podle požadavku jsem spojil doctype + level doctype a také jsem přepsal renderování (na mix vypisování Html objektů a čistého textu) a založil ji stránku v addonech.
Co pro HeaderControl chystám do budoucna?
- dopsání phpDoců (udělal bych to dnes, ale bohužel mě zlobí Netbeans a vůbec mi při psaní komentářů nepomáhají :))
- zvážit návrh Honzy Marka na univerzální připojování komponent
- zvážit přepsání renderování na čisté vypisování stringů, přecijen při zakládání X Html objektů se PHP může zbytečně zadýchat
- Honza Kuchař
- Člen | 1662
Paráda! A až bude tohle:
zvážit návrh Honzy Marka na univerzální připojování komponent
Tak to teda bude opravdu super!
- Ondřej Mirtes
- Člen | 1536
Přemýšlel jsem nad tím a šlo by to zpracovat jako univerzální předek, něco jako RenderableComponentContainer :))
- p3s
- Člen | 8
Caute, s Nette zacinam, pomaly prenikam do tajov controlov, tovarniciek a pod. Chcem pouzit v mojom prvom projekte HeaderControl. Potrebujem nakopnut ako doplnit dalsiu cast title. Postupoval som podla prikladu, vsetko funguje tak ako ma, teraz chcem ale v dalsom presentri ktori dedi od BasePresenter doplnit nieco do title aby vyzeral nasledovne: WebTitle | PageTitle. Zatial mam len ten WebTitle. Diky.
BasePreseneter obsahuje metodu
protected function createComponentHeader()
{
$header = new HeaderControl(HeaderControl::XHTML_1_STRICT, 'sk', Environment::getConfig('web')->title);
$header->setTitleSeparator(' | ')
->setTitlesReverseOrder(FALSE)
->setRobots('index,follow'); //of course ;o)
....
return $header;
}
v sablone @layout.phtml mam
{widget header:begin}
{widget header:css 'screen.css', 'print.css'}
{widget header:end}
- Ondřej Mirtes
- Člen | 1536
Ahoj,
kdekoli v Presenteru můžeš zavolat
$this['header']->addTitle('Moje stránka');
:)
- p3s
- Člen | 8
Diky, to je ono, zasa som sa posunul dalej a naucil nieco nove. Mam este jednu otazku. Ako dostat do meta tagu s Content-type aj charset, aby vyzeral napr takto
<meta http-equiv="Content-Type" content="text/html; charset=windows-1250" />
Charset sa negeneruje.
Ako som si vsimol, vzdy sa posiela header pre utf-8 kodovanie, nie vzdy vsak
stranky musia byt v UTF-8.
- Ondřej Mirtes
- Člen | 1536
V Nette platí, že by stránky v UTF-8 měly být (proto v této komponentě ani jinou možnost nedávám), už kvůli práci s formuláři. Nevidím důvod používat nějaké jiné kódování, UTF-8 je standard několika posledních let.
- Honza Kuchař
- Člen | 1662
Ahoj, jak to vypadá s tou registrací komponent?
//EDIT: Nijak na to nespěchám. Jen se ptám.
Editoval honzakuchar (18. 2. 2010 0:19)
- Ondřej Mirtes
- Člen | 1536
Ahoj,
hmatatelně zatím nijak, ale udělám to jako univerzálního předka
RenderableContainer, do kterého si budeš moci přidávat vlastní komponenty a
ty pak renderovat přes renderNazevKomponenty (udělané přes __call).
V přístích dnech na to snad kouknu :)
- Ondřej Mirtes
- Člen | 1536
HeaderControl pracuje ‚uvnitř‘ s WebLoaderem, tohle nastavení jde bez problému realizovat.
V továrničce:
$header = new HeaderControl(HeaderControl::HTML_5, HeaderControl::CZECH, 'Example title');
...
$webConfig = Environment::getConfig('web');
$css = $header['css'];
if ($webConfig->strip->css) {
$css->filters[] = array('MyTools', 'minifyCss');
}
- Ondřej Mirtes
- Člen | 1536
Nemyslím si, že by to takhle hezky fungovalo. Nejsem si úplně jistý, v jakém pořadí by Nette takhle zapsané kódy volalo.
Dokonce si myslím, že volat $this[‚header‘]->addTitle() v render metodě je praktičtější, nemusíš si tu proměnnou předávat do šablony. A v šabloně (layoutu) můžeš do místa nadpisu dát tento kód:
<h1>{$presenter['header']->getTitle()}</h1>
{* metoda getTitle může jako parametr přijímat index,
podle kterého se dostaneš i k dříve přidaným titlům, nejen k tomu poslednímu *}
- LiborM
- Člen | 15
Zdravím,
jsem absolutní začátečník s PHP i Nette. Použil jsem HeaderControl
komponentu (moc pěkná práce), ale když chci pak v HomePresenteru (úvodní
stránka webu – použit standardní skeleton) použít např.
echo "Text";
, tak dostanu hlášku z laděnky:
InvalidStateException – Cannot send header after HTTP headers have
been sent.
HeaderControl mám zapracovaný do BasePresenteru :
abstract class BasePresenter extends Presenter
{
//public $oldLayoutMode = FALSE;
//private $skinName="blue";
private $skinName;
public function beforeRender(){
$skin = Environment::getConfig('skin');
$this->skinName=$skin['skin'];
}
protected function createComponentHeader() {
$header = new HeaderControl(HeaderControl::XHTML_1_STRICT, 'cs', 'Nette - CMS');
$header->setTitleSeparator(' | ')
->setTitlesReverseOrder(true)
->addKeywords('one')
->addKeywords(array('two', 'three'))
->setDescription('Our example site')
->setRobots('index,follow') //of course ;o)
->addRssChannel('News', 'Rss:')
->addRssChannel('Comments', 'Rss:comments');
$css = $header['css'];
if ($this->skinName==""){
$css->sourcePath = WWW_DIR . '/css';
}
else {
$cesta='/skin/'.$this->skinName.'/css';
$css->sourcePath = WWW_DIR . $cesta;
}
$css->sourceUri = Environment::getVariable('baseUri') . 'temp';
$css->tempUri = $css->sourceUri;
$css->tempPath = WWW_DIR . '/temp';
return $header;
}
}
V @layout.phtml mám :
{widget header:begin}
{widget header:css 'style.css', 'paginator.css'}
{widget header:end}
Dělám něco špatně nebo jak použít např. echo "Text";
v HomePresenteru ?
Díky
LiborM
Editoval LiborM (15. 3. 2010 15:57)
- Ondřej Mirtes
- Člen | 1536
Ahoj,
pokud nepoužiješ HeaderControl, dělá ti to taky nebo ne? Sice bys před
jejím vykreslením neměl nic vypisovat, aby se provedl správně tento řádek,
ale i přesto jsem nikdy s dumpováním z Presenteru neměl problém :)
BTW: V tvém kódu vidím problém. Pokud bys náhodou instanci HeaderControl vytvořil ještě před render fází (čemuž továrnička nijak bránit nebude), tak se ti nastaví cesta ještě podle nenaplněné hodnoty $this->skinName, protože to bude ještě před provedením metody beforeRender().
Editoval Ondřej Mirtes (15. 3. 2010 16:57)
- LiborM
- Člen | 15
Ahoj,
pokud nedám widget do @layout.phtml , tak to funguje normálně :-(
Jěště mne napadlo, že mám poslední dev verzi…může to být
tím ?
Zkoušel jsem to v stable 0.9.3 pro PHP 5.2 a chová se to stejně
Děkuju za připomínku o $this->skinName…zkusím ji dát jinam
LiborM
Editoval LiborM (16. 3. 2010 15:24)
- Foowie
- Člen | 269
Nešla by odstranit pevná závislost na WebLoaderu?
<?php
např místo
protected function createComponentCss() {
return new CssLoader;
}
udělat něco jako
protected $cssLoaderClass = "CssLoader";
protected function createComponentCss() {
return new $cssLoaderClass;
}
public function setCssLoaderClass($className) {
$this->cssLoaderClass = $className;
}
nebo nějak podobně ...
?>
Vím, že můžu skrátka přepsat třídy v továrničce, ale tohle by bylo variabilnější řešení .)
- Honza Marek
- Člen | 1664
Foowie napsal(a):
Nešla by odstranit pevná závislost na WebLoaderu?
…
Vím, že můžu skrátka přepsat třídy v továrničce, ale tohle by bylo variabilnější řešení .)
- Ondřej Mirtes
- Člen | 1536
Trošku jsem si máknul a opravil pár broučků, co tuhle komponentku trápili.
Přidal jsem taky možnost zvenku
modifikovat <html>
tag, aby se do něj daly přidávat
definice namespace, které jsou potřeba např. při přihlašování přes
Facebook.
A konečně jsem vytvořil třídu RenderableContainer,
která slouží jako zkratka k volání
$component['subcomponent']->render()
pomocí
$component->renderSubcomponent();, což se hodí ve {widget} makru
v šablonách a HeaderControl tenhle mechanismus využívá :)
- dj.kure
- Člen | 70
Zkouším, zkouším a stále mám jeden problémek :D … nevíte náhodou, jestli se HeaderControl neperes ConfirmationDialogem ? Je tu totiž problémek, že se HEAD vypíše až za tímto Dialogem, takže mám příklad:
<div id="ConfirmDialog">
...
...
...
</div>
// a až zde začíná Head
<!DOCTYPE html>
<html lang="en" xmlns:fb="http://www.facebook.com/2008/fbml">
<head>
....
nesetkali jste se s tím už někdo ?
Edit: Nette verze 1.0 pro PHP5.3.
Editoval dj.kure (12. 7. 2010 23:49)
- dj.kure
- Člen | 70
assassik napsal(a):
Ani jednu komponentu sice nepoužívám, ale na první pohled mě napadá zavináčová magie u toho ConfirmationDialogu.
Zavináčová magie ? Pokud máš na mysli zavináče v layout.phtml, tak tam není ani jeden a po pravdě, ani při jeho vložení se nic nezmění.
{widget header:begin}
{widget header:end}
Takhle vypadá současný default.phtml
- maarlin
- Člen | 207
Nevím proč, ale nefunguje mi to:
Jako první argument konstruktoru je vyžadována instance
IComponentContainer
, což je už podle příkladu trochu
nereálné:
$header = new HeaderControl(HeaderControl::HTML_5, 'en', 'Example title');
Argument 1 passed to HeaderControl::__construct() must implement interface IComponentContainer,
string given, called in ...\app\presenters\BasePresenter.php on line 16 and defined
Kde dělám chybu?
Editoval maarlin (5. 9. 2010 9:34)
- Ondřej Mirtes
- Člen | 1536
Příklad na wiki je zastaralý. Konstruktor nepoužívej vůbec, použij settery.