Zend Framework 2: Touching headLink() twice on layout template
Friday, January 17. 2014
This one was one of the tricky ones. My CSS-inclusion was doubled for a very strange reason. My layout-template has:
{$this->headLink()
->prependStylesheet('/css/style.css')
->prependStylesheet('/css/jQuery/jquery.mobile.css')}
{$this->headLink([
'rel' => 'shortcut icon',
'type' => 'image/vnd.microsoft.icon',
'href' => '/images/favicon.ico'
])}
That would be pretty standard for any web application. Link a couple of CSS-definition files and declare the URL for favorite icon of the website. However, on ZF2 doing things like my above code does, makes things go bad. Rather surprisingly, the HTML gets rendered as:
<link href="/css/jQuery/jquery.mobile.css" media="screen" rel="stylesheet" type="text/css">
<link href="/css/style.css" media="screen" rel="stylesheet" type="text/css">
<link href="/css/jQuery/jquery.mobile.css" media="screen" rel="stylesheet" type="text/css">
<link href="/css/style.css" media="screen" rel="stylesheet" type="text/css">
<link href="/images/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">
It doesn't actually break anything to have the CSS linked twice, but it just makes the CSS-debugging bit weird. Lot of the declarations are twice in the list and browser has to determine which ones are effective and which ones are ignored in any particular case.
To find out what's going on, I swapped my template to contain:
{$this->headLink([
'rel' => 'shortcut icon',
'type' => 'image/vnd.microsoft.icon',
'href' => '/images/favicon.ico'
])}
{$this->headLink()
->prependStylesheet('/css/style.css')
->prependStylesheet('/css/jQuery/jquery.mobile.css')}
Whatta ... hell!? Now everything works as expected. First the favicon-link and then CSS-links. Without any unnecessary doubling.
After a nice long morning of debugging ZF2-view code revealed a solution:
{$this->headLink()
->prependStylesheet('/css/style.css')
->prependStylesheet('/css/jQuery/jquery.mobile.css')}
{$this->headLink()
->deleteContainer()}
{$this->headLink([
'rel' => 'shortcut icon',
'type' => 'image/vnd.microsoft.icon',
'href' => '/images/favicon.ico'
])}
Now everything renders nicely. No doubles, all in the order I wanted them to be. The key was to erase Zend\View\Helper\HeadLink's container after doing the stylesheets. The method is actually in the class Zend\View\Helper\Placeholder\Container\AbstractStandalone. Apparently headLink's container only adds up and any subsequent calls simply add to the existing storage. The mistake is to print the contents of the container in the middle. The final solution is not to touch headLink() twice:
{$this->headLink([
'rel' => 'shortcut icon',
'type' => 'image/vnd.microsoft.icon',
'href' => '/images/favicon.ico'
])
->prependStylesheet("/css/style.css")
->prependStylesheet("/css/jQuery/jquery.mobile.css")}
Now it works much better! The rendered HTML will have the items in appropriate order:
- /css/jQuery/jquery.mobile.css
- /css/style.css
- /images/favicon.ico
This was yet again one of the funny things that have changed since ZF1. I definitely would consider that as a bug, but don't want to bother sending Zend a report out of it. They'll yet again pull a Microsoft and declare it as a feature.