Tobiasz Cudnik

Posts Tagged ‘templates’

QueryTemplates development progress

In QueryTemplates on 01.03.2009 at 1:06

QueryTemplates 1.0 Beta3 is out since a week or so and next release is around the corner, so i decided to do some wrap-up of latest facts in library development:

  • Extended data targets are no supported by all methods. This means each method can be re-targeted with jQuery insert methods like for example varsToSelectorBefore or even varsToSelectorAttr.
  • New codeTo method type. It simply injects code groups. Idea evolved from methodTo approach, which would inject method calls. I find codeTo amuch more flexible and useful, but it can be less readable.
  • New valuesToLoop method. In opposite to varsToLoop aka loop(), it will iterate existing data over DOM structure and commit it right away. This has less purpose on server side (but can be useful), but it’s essential on the client-side when you don’t want to make a server request.
  • New conditional chains methods – onlyJS() and onlyPHP(). They allow to highly customize template for certain language. For example, everything between onlyPHP() and endOnly() will be applied only to PHP template, even when the same chain is used to create JS template.
  • Light version for JavaScript is available. It contains all valuesTo methods which includes valuesToSelector, valuesToStack and valuesToLoop. Form methods and mutation events support will be next. It’s a jQuery plugin and demo is available.
  • formFromVars will generate full form templates from structure and variable informations. Such form supports error messages and default values. Of course input-wrapper template is customizable. As for today there are couple of things to be done in this area and formFromValues will also be available soon.
  • Syntax reference is complete. Each method has it’s own wiki page with 6-part example: markup, data, formula aka code, template, tree dump before and tree dump after. Also, new JavaScript Light version is included in this reference. User comment are allowed. Personally i think it’s the best part of project’s page ;)
  • Some API refactorings had taken place. Old names are still accessible, but marked as deprecated.
    • varsFrom type was created from such methods as saveTextAsVar, saveAsVar, valuesToVars.
    • loop, loopOne and loopSeparate now becomes varsToLoop, varsToLoopFirst and varsToLoopSeparate and are grouped in ToLoop type.
  • dumpTree is now more verbose and can be used both via HTML and plain text. Just do ->dumpTree() anywhere in the chain to see readable representation of selected structure. It’s used for syntax reference examples.

As you can see project is quite close to 1.0 final. Code seems stable and each method has pseudo-unit-test in form of example available in Syntax reference, which is generated as a real template. I just can’t wait to start implementing CSS interface i wrote before. Additionaly, as JavaScript version is out, there can be some use for jQueryServer.

Events in CSS selector-driven markup templates make them highly reusable

In Ideas, QueryTemplates on 25.01.2009 at 16:36

One of the biggest adventage of DOM oriented template engines such as QueryTemplates is events support. Using Event Delegation and some Mutation Events you can set global handlers for all templates. Purpose of such handlers may be varient. I’m presenting 6 of them as examples.

Let’s start from events table

Each event can easily have many handlers. One of them is a mutation event – DOMNodeInserted.

$globalHandlers = array(
  array('unload', 'everyTableHaveTbody'),
  array('load', 'insertNavigation'),
  array('DOMNodeInserted', 'newListsAlwaysOrdered'),
  array('unload', 'noAds'),
  array('unload', 'clearEmptyClasses'),
  array('load', 'stripTables'),
);

Here’s our global handlers

// global handlers
// can be functions, methods, create_function and closures (in PHP 5.3)

function everyTableHaveTbody($e) {
  pq($e->target->ownerDocument)
    ->find('table')
      // nested closure should be here
      ->each('everyTableHaveTbodyCallback');
}
function everyTableHaveTbodyCallback($table) {
  if (pq('> tbody', $table)->length == 0)
    pq($table)->contents()->wrapAll('
<tbody>');
}
function noAds($e) {
  pq($e->target->ownerDocument)
    ->find('.ads')->remove();
}
function newListsAlwaysOrdered($e) {
  if (pq($e->target)->is('ul'))
    pq($e->target)->replaceWith(
      pq('
<ol>')->append(
        pq($e->target)->contents()
      )
    );
}
function insertNavigation($e) {
  $nav = pq($e->target->ownerDocument)
    ->find('.navigation')
      ->empty()
      // nested closure should be here
      ->each('insertNavigationCallback');
}
function insertNavigationCallback($nav) {
  pq($nav)->append(file_get_contents('navigation.html'));
}
function stripTables($e) {
  pq($e->target)->find('table.strip-me')
    ->removeClass('strip-me')
    ->find('> tr:odd, > tbody > tr:odd')
      ->addClass('odd');
}
function clearEmptyClasses($e) {
  $nav = pq($e->target->ownerDocument)
    ->find('*[class=""]')
      ->removeAttr('class');
}

Read the rest of this entry »

QueryTemplates – template’s logic and markup in one file

In QueryTemplates, phpQuery on 25.01.2009 at 15:41

Using latest phpQuery revisions you can easily keep template’s markup and logic in one place with 2 extra lines of code. This heavily breaks the general concept, but it may be useful in some cases. Idea is based on support for callbacks as template sources. First you start dumping markup with 1 line.

<?php ob_start(); ?>

After that the markup goes

<html>
  <body>
<div>Hello world</div>
</body>
</html>

And at the end there is whole logic. $markup is dumped using new CallbackReturnValue, which takes a variable as parameter. When callback is called it just returns it.

<?php
require('../src/QueryTemplates.php');
$markup = new CallbackReturnValue(ob_get_clean());
require template('test')->parse($markup)
	->find('div')
		->text('Hello template!')
;

This approach could also be used for getting markup with requestAction in CakePHP views.

toReference() inside chains

In QueryTemplates, phpQuery on 25.01.2009 at 15:26

There is one extremely useful and not-so widely known toReference() method. It saves actually matched elements inside variable, by reference (as the name says). It can be used to target such cases as:

1. Preserving chain

// declare our variables (this is VERY important)
$deepClass = $section = null;
$template['div.main div.otherclass .deep-class:first']
  ->toReference($deepClass)
//  ->...  // do something
  ->find('.section')
    ->toReference($section)
//  ->...  // do something
  ->end()
  ->next()
    // highlighter but; only one empty()
    ->empty()
    ->append($deepClass->contents())
    ->eq(0)->add($section)->eq(1)
//      ->...  // do something, stack is [$section]
    ->end()->end()->end()
//      ->...  // stack is same as after ->next()
;

2. Splitting chains

// declare our variables (this is VERY important)
$row = $titleBody = null;
$template
  ->find('ul:first > li')
    ->loopOne('posts', 'postNum', 'r')
      // add dynamic class
      ->addClassPHP('if (! $postNum) print "first"')
      ->find('> .title, > .body')
        ->varsToStack('r["Post"]', $postFields)
        // save our $titleBody variable
        ->toReference($titleBody)
      ->end()
      // save our $row variable
      ->toReference($row)
//    ->...  // lots of code ;)
;
// just continue your work
$row->find('h3:first, .comments')
// ->...
;
// anywhere...
doSomethingOnFields($titleBody);

In SVN version of QueryTemplates there is a fix for cache issue. The workaround for beta2 is to do “if ($ref)” before, or turning off the cache with “$cacheTimeout = -1″.

Extending QueryTemplates with closures

In QueryTemplates, phpQuery on 25.01.2009 at 14:24

Lastest changes in phpQuery allows us to extend it much more easily. One of the ways to do this is directly same as in jQuery, using phpQuery::extend() method. When you combine this with upcoming closures in PHP 5.3, you will feel like-almost-in-browser. Take a look at this:

$source = dirname(__FILE__).'/table.html';
$rows = array(
	array(
		'field-1' => 'foo1',
		'field-2' => 'foo2',
	),
	array(
		'field-1' => 'bar1',
		'field-2' => 'bar2',
	),
);
// this will work in PHP 5.3
// this code will evaluate on onLoad event, thus is cache firendly
$onload = function() {
  $tableFirstRowClass = function($self, $var, $classname = 'first') {
    return $self->addClassPHP("
      if ($$var == 0)
        print '$classname';
    ");
  };
  // here we pass new closure into extend method, just like in jQuery
  // compact() creates array for us
  phpQuery::extend('phpQueryObject', compact('tableFirstRowClass'));
};
require template('table')->parse($source)
  // just bind it
  ->bind('onload', $onload)
  // just fire it up ;)
  ->trigger('onload')
  ->find('table > tr, table > * > tr')
    ->loopOne('rows', 'i', 'row')
      ->varsToSelector('row', $rows[0])
      // now you can use this just-like-this
      ->tableFirstRowClass('i')
;

But for now to extend phpQuery on-the-fly we have to use create_function or delegate existing one.

We can also use new Scripts plugin feature, which does almost the same. Just like all Scripts’ plugin script files, newly attached function will have all available variables. Example below works in PHP < 5.3:

function onload() {
  // in PHP 5.3, this can be done simply like this
//  phpQuery::script(...
  // but for now we have to use fake phpQuery::$plugins namespace
  phpQuery::$plugins->script('tableFirstRowClass', create_function(
    '$self, $params, &$return, $config', '
    $className = ! $params[1] ? "first" : $params[1];
    $self->addClassPHP("
      if ($$params[0] == 0) print '$className';
    ");
  '));
};
$onload = 'onload';

One major difference between extending Scripts and phpQuery itself is way of using new functions. With our new script, we have to small change to main code.

// using phpQuery::extend()
//->tableFirstRowClass('i')
// using Scripts extend
->script('tableFirstRowClass', 'i')

QueryTemplates finally released

In QueryTemplates, plainTemplates on 03.12.2008 at 16:47

I’ve finally managed to release QueryTemplates, a pure HTML templating engine i’ve been working past months. There’re extensive examples which should allow anyone to easily understand the idea. Previously posted Pure HTML templates theory sums up some thoughts about this templating pattern. You can read more about new library on the wiki and there’s also an official blog. Feel free to post feedback.

Pure HTML templates theory

In Ideas on 19.11.2008 at 23:51

In this post i would like to sum up my thought about using pure HTML files as fully functional templates. I’ve examined this pattern for about a year and i have to say it creates real order in application structure and lifecycle.

What means “pure” ?

  • Pure like plain, nothing else than HTML
  • Fully validable
  • Readable in any browser
  • Editable in any HTML editor

Read the rest of this entry »

plainTemplates – CSS driven templating engine

In Ideas, plainTemplates on 30.07.2007 at 11:22

This post is deprecated and informations below are outdated. Please follow up to QueryTemplates and Pure HTML templates theory.

Introduction

plainTemplates is a PHP and JS library that aims to divide template into 2 separate layers:

  1. CSS selectors driven data attaching mechanism
  2. Plain HTML

It’s simply creates your templates from plain HTML files on-the-fly, filling it with data and fetching parts you like.

It’s basen on phpQuery – a jQuery port to PHP.

Pros

  • True separation of data and visual layer
  • HTML can be filled with example data
  • High reusability (several templates from one HTML, one template from several HTMLs)
  • Framework independed template organisation
  • Transparent refreshes (including PHP code changes!)
  • phpQuery integration
  • No need to lear new, template-only language
  • Saves a lot of time ;)

Cons

  • You have to know CSS selectors (XPath will also help)
  • In most cases you have to know jQuery/phpQuery
  • You have to format data outside template (eg date).

Here is simple example, which inserts data from $data array into template in template.htm:

// get the class
require_once('../../plainTemplates/plainTemplates.php');
// always end dir with a slash
plainTemplates::$cacheDir = '../../plainTemplates/cache/';
$data = array(
	array(
		'title' => 'News 1 title',
		'body'	=> 'News 1 body',
	),
	array(
		'title' => 'News 2 title',
		'body'	=> 'News 2 body',
	),
	array(
		'title' => 'News 3',
		'body'	=> 'News 3 body',
	),
);
// include to fully preserve scope
include(
	plainTemplates::createTemplate(
		// first the template source
		'template.htm',
		// second the var info
		array(
			// var name in scope
			'$data',
			// and var value
			$data,
			// finally the selectors (of course there are defaults, that is UL and LI)
			array(
				'container' => '.someclass ul',
				'row'		=> 'li'
			)
		)
	)
);

Here is template.htm itself:

<div class='someclass'>
	<p>UL below will be container, and every LI will be searched agains classes with field names.</p>
	<ul>
		<li>

			<p>Only tags with field classes are replaced.</p>
			<p class='title'>This will be replaces by the title</p>
			<p class='body'>And this by the body...</p>
		</li>

		<li>
			Only first row will be in the result and this wont be considered by any chance... so You can use as many row examples as You want.
		</li>
	</ul>
</div>

Now the result’s source:

<ul><?php foreach( $data as $dataRow ): ?>

			<li>
			<p>Only tags with field classes are replaced.</p>
			<p class="title"><?php print $dataRow['title']; ?></p>
			<p class="body"><?php print $dataRow['body']; ?></p>

		</li><?php endforeach; ?></ul>

Those are the basics, it can do a lot more, but i think its good that it can do simple things in a simple way… Read the rest of this entry »