| 269 | | <p>TODO</p> |
|---|
| | 277 | <p>Kid templates can be any XML document with namespaced attributes that tells Kid how to process the template. In practice, your templates will be XHTML documents that will be processed and transformed into valid HTML documents.</p> |
|---|
| | 278 | |
|---|
| | 279 | <p>This example (straight from Kid's documentation) shows what Kid is like:</p> |
|---|
| | 280 | |
|---|
| | 281 | <textarea class="xhtml" name="code"><![CDATA[ |
|---|
| | 282 | <?python |
|---|
| | 283 | title = "A Kid Test Document" |
|---|
| | 284 | fruits = ["apple", "orange", "kiwi", "M&M"] |
|---|
| | 285 | from platform import system |
|---|
| | 286 | ?> |
|---|
| | 287 | <html xmlns:py="http://purl.org/kid/ns#"> |
|---|
| | 288 | <head> |
|---|
| | 289 | <title py:content="title">This is replaced.</title> |
|---|
| | 290 | </head> |
|---|
| | 291 | <body> |
|---|
| | 292 | <p>These are some of my favorite fruits:</p> |
|---|
| | 293 | <ul> |
|---|
| | 294 | <li py:for="fruit in fruits"> |
|---|
| | 295 | I like $${fruit}s |
|---|
| | 296 | </li> |
|---|
| | 297 | </ul> |
|---|
| | 298 | <p py:if="system() == 'Linux'"> |
|---|
| | 299 | Good for you! |
|---|
| | 300 | </p> |
|---|
| | 301 | </body> |
|---|
| | 302 | </html> |
|---|
| | 303 | ]]> |
|---|
| | 304 | </textarea> |
|---|
| | 305 | |
|---|
| | 306 | <p>Important points about Kid templates:</p> |
|---|
| | 307 | |
|---|
| | 308 | <ul> |
|---|
| | 309 | <li>Don't forget to define the py XML namespace (as is done in the html tag above). That's key to having your template understood as valid XML.</li> |
|---|
| | 310 | <li>$${} lets you easily substitute in a Python expression anywhere in your document.</li> |
|---|
| | 311 | <li>$$foo lets you substitute in foo, but it's not as safe as using the {} because it can be harder to detect where you substitution is supposed to end</li> |
|---|
| | 312 | <li>This is XHTML, so you need to close <em>all</em> of your tags. For example, in HTML you'd write <br> to put a line break. In XHTML, you need to write <br/>. This will get converted properly to HTML on the way out.</li> |
|---|
| | 313 | </ul> |
|---|
| | 314 | |
|---|
| | 315 | <p>One of the great things about Kid is that everything you know about Python applies here. Kid templates get compiled to Python code this makes it easier to predict how things will behave. For example, that py:for="fruit in fruits" behaves just like "for fruit in fruits:" in Python.</p> |
|---|
| | 316 | |
|---|
| | 317 | <p>The variables that you defined in your dictionary are all available for your use. In any of the "py" attributes, just use the variable from the dictionary as you would a local variable in Python. In the py:for="fruit in fruits" example, "fruits" would be some kind of iterable object passed in via the dictionary.</p> |
|---|
| | 318 | |
|---|
| | 319 | <p>When variables are dropped in to your template, Kid will automatically escape them. You don't even need to think about running into problems with values that contain <, for example. The time when you do need to care is if you actually have XHTML itself to drop in place. If you do, wrap your substitution value in XML(). For example, let's say we had an XHTML fragment called "header". You could just say $${XML(header)}, and the header would be dropped in without being escaped.</p> |
|---|
| | 320 | |
|---|
| | 321 | <p>Rather than reproduce it here, a quick read that is well worth it is the <a href="http://kid.lesscode.org/language.html#attribute-language" target="_blank">reference to Kid's attribute language</a>.</p> |
|---|
| | 322 | |
|---|
| | 323 | <h2>Being Designer-Friendly</h2> |
|---|
| | 324 | |
|---|
| | 325 | <p>Since they are standard XHTML, you can load up Firefox and open your Kid templates directly. If it's important or useful for you, you can make the templates look great all by themselves when loaded in the browser.</p> |
|---|
| | 326 | |
|---|
| | 327 | <p>What do you need to watch for? Stylesheets, JavaScript and variable substitutions.</p> |
|---|
| | 328 | |
|---|
| | 329 | <p>Sometimes, it's inconvenient or impossible to have a stylesheet href that works properly when viewing the template directly <em>and</em> after the template is rendered. To get around this, you can use href to handle viewing the template in your browser and py:attrs to handle the rendered page. For example:</p> |
|---|
| | 330 | |
|---|
| | 331 | <textarea name="code" class="xhtml"><![CDATA[ |
|---|
| | 332 | <link rel="stylesheet" type="text/css" href="/Path/To/File.css" py:attrs="href='/path/on/website'"/> |
|---|
| | 333 | ]]></textarea> |
|---|
| | 334 | |
|---|
| | 335 | <p>When you're viewing the template in your browser, your browser's only going to look at the href attribute, so your stylesheet will be loaded properly. When the template is rendered for the final view, Kid will substitute the value that's in py:attrs, so that the stylesheet works properly on the web. When dealing with JavaScript, this same approach will work for the src attribute on the script tag.</p> |
|---|
| | 336 | |
|---|
| | 337 | <h2>Application-wide headers and footers</h2> |
|---|
| | 338 | |
|---|
| | 339 | <p>Kid offers a number of ways to do application-wide headers and footers. Let's focus on a couple particular approaches: the cool approach and the fast approach.</p> |
|---|
| | 340 | |
|---|
| | 341 | <p>Kid has a really, really useful command called py:match. What makes it so useful is that you can write individual page templates that know nothing about the site-wide styling that will be applied. The quickstart command starts you off with this kind of setup.</p> |
|---|
| | 342 | |
|---|
| | 343 | <p>Let's start with a page template that we want to have headers/footers applied to (this is based on gs/templates/welcome.kid):</p> |
|---|
| | 344 | |
|---|
| | 345 | <textarea name="code" class="xhtml"><![CDATA[ |
|---|
| | 346 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|---|
| | 347 | <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" |
|---|
| | 348 | py:extends="'master.kid'"> |
|---|
| | 349 | |
|---|
| | 350 | <head> |
|---|
| | 351 | <meta content="text/html; charset=UTF-8" http-equiv="content-type" /> |
|---|
| | 352 | <title>Welcome to TurboGears</title> |
|---|
| | 353 | </head> |
|---|
| | 354 | |
|---|
| | 355 | <body> |
|---|
| | 356 | <h2>You're running TurboGears!</h2> |
|---|
| | 357 | |
|---|
| | 358 | <p>Your TurboGears server is running as of <span py:replace="now">now</span>.</p> |
|---|
| | 359 | <div py:replace="phraseOfTheDay()"/> |
|---|
| | 360 | </body> |
|---|
| | 361 | </html> |
|---|
| | 362 | ]]></textarea> |
|---|
| | 363 | |
|---|
| | 364 | <p>The <em>only</em> thing in this template that is necessary to get headers and footers applied is the py:extends up in the HTML tag. Kid allows one template to extend or subclass another template. When you do this, your template inherits the parent's py:match and py:def blocks. py:extends works with a string that looks for a file relative to the current template, or a template module object that you have imported.</p> |
|---|
| | 365 | |
|---|
| | 366 | <p>What does the master template look like?</p> |
|---|
| | 367 | |
|---|
| | 368 | <textarea name="code" class="xhtml"><![CDATA[ |
|---|
| | 369 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
|---|
| | 370 | <html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"> |
|---|
| | 371 | |
|---|
| | 372 | <head> |
|---|
| | 373 | <meta content="text/html; charset=UTF-8" http-equiv="content-type" /> |
|---|
| | 374 | <title>Your title goes here</title> |
|---|
| | 375 | </head> |
|---|
| | 376 | |
|---|
| | 377 | <body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'"> |
|---|
| | 378 | <h1>TurboGears is Running</h1> |
|---|
| | 379 | |
|---|
| | 380 | <div py:def="phraseOfTheDay()">There's no better time than the present!</div> |
|---|
| | 381 | |
|---|
| | 382 | <p>This text appears in the header provided by master.html.</p> |
|---|
| | 383 | |
|---|
| | 384 | <div py:if="turbogearsflash" class="flash" py:content="turbogearsflash"></div> |
|---|
| | 385 | |
|---|
| | 386 | <div py:replace="item[:]"/> |
|---|
| | 387 | </body> |
|---|
| | 388 | </html> |
|---|
| | 389 | ]]></textarea> |
|---|
| | 390 | |
|---|
| | 391 | <p>This master template can be viewed in a browser, just as individual page templates can. The py:match attribute on the body looks for the body of the child template (note the use of XML namespaces). In the py:match statement, "item" gives you access to the <a href="http://effbot.org/zone/pythondoc-elementtree-ElementTree.htm#elementtree.ElementTree.Element-function">element</a> at that point in the document. You can look at the tag or the attributes (through dictionary-style lookup on item.attrib) of the element to see if you're looking at one you care about. In this case, we're just going to take over the body element.</p> |
|---|
| | 392 | |
|---|
| | 393 | <p>When a match is found, the body of the child (individual page) template is <em>replaced</em> by the one in the master template. How do you get your content in there? At the bottom of this master template, there is a div that is replaced by "item[:]". In this particular example, that says to put everything <em>under</em> the body of the page template into this spot in the master template. Usually, you'll want all of the elements, so item[:] is what you'll use.</p> |
|---|
| | 394 | |
|---|
| | 395 | <p>py:match is not the fastest way to work, but premature optimization is the root of all evil. If you've got a fairly straightforward app without thousands of users, py:match may work just great for you. py:match has to look compare each element with any match expressions you have defined. If you have very large pages and tons of simultaneous users, you may want something faster.</p> |
|---|
| | 396 | |
|---|
| | 397 | <p>Kid offers a faster approach: template functions. These work just like normal template functions and they are inherited from the master template. There is a simple one in the example above: phraseOfTheDay.</p> |
|---|
| | 398 | |
|---|
| | 399 | <p>phraseOfTheDay is defined in the master by py:def on a div. In the page template, it shows up as a py:replace in a div. So, the div in the child page template will just get replaced by the div from the master. This lets you include headers, footers, search boxes and the like easily.</p> |
|---|
| | 400 | |
|---|
| | 401 | <p>Template functions can also take parameters just like normal Python functions. You could, for instance, have a title parameter that gets passed in from the child template in order to format a heading.</p> |
|---|
| | 402 | |
|---|
| | 403 | <p>Since template functions are normal Python functions and don't affect any other processing of the template, using this styling method is very fast.</p> |
|---|
| | 407 | <p>All templates that get rendered by TurboGears have access to some basic variables automatically. The variables are defined in the stdvars function in turbogears.view. They provide conveniences for identifying the user's browser, plus common template tasks. See the <a href="api/turbogears.view-module.html#stdvars">stdvars documentation</a> for the complete list.</p> |
|---|
| | 408 | |
|---|
| | 409 | <p>In order to avoid name collisions with your own template variables, all of the predefined variables are found in "std". For example, the user agent is found in std.useragent.</p> |
|---|
| | 410 | |
|---|
| | 411 | <p>One in particular that's worth noting is std.ipeek, which lets you look at the first item of an iterator without actually consuming it from the iterator. This comes in handy when you have an iterator that <em>might</em> be empty. If it is empty, you may not display a whole block from your template. Here's an example:</p> |
|---|
| | 412 | |
|---|
| | 413 | <textarea name="code" class="xhtml"><![CDATA[ |
|---|
| | 414 | <?python items=std.ipeek(shoppingcart) ?> |
|---|
| | 415 | <div py:if="items"> |
|---|
| | 416 | <p>Your cart contains:</p> |
|---|
| | 417 | <ul> |
|---|
| | 418 | <li py:for="item in items" py:content="item">Watermelon</li> |
|---|
| | 419 | </ul> |
|---|
| | 420 | </div> |
|---|
| | 421 | <div py:if="not items"> |
|---|
| | 422 | <p>Your cart is empty</p> |
|---|
| | 423 | </div> |
|---|
| | 424 | ]]></textarea> |
|---|
| | 425 | |
|---|
| | 426 | <p>When you have a list, you don't need to use std.ipeek. But, when all you have is an iterator, this is a handy tool.</p> |
|---|
| | 427 | |
|---|
| | 428 | <p>If you have site-wide variables that you would like available in your templates, you can add a callable to <a href="api/turbogears.view-module.html#variableProviders">turbogears.view.variableProviders</a>. This will be called with a dictionary that is already set up with the TurboGears standard variables. If you want the current time available in all of your templates, you can just add this to a module:</p> |
|---|
| | 429 | |
|---|
| | 430 | <textarea name="code" class="py"><![CDATA[ |
|---|
| | 431 | import time |
|---|
| | 432 | from turbogears import view |
|---|
| | 433 | |
|---|
| | 434 | def addtimevar(variables): |
|---|
| | 435 | variables["currenttime"] = time.ctime() |
|---|
| | 436 | |
|---|
| | 437 | view.variableProviders.append(addtimevar) |
|---|
| | 438 | ]]></textarea> |
|---|
| | 439 | |
|---|