TIP: Use Markdown or, <pre> for multi line code blocks / <code> for inline code.
Including multiple JS and CSS in your template
  • Hi,

    I am still using Kohana 2.3.x and I am building my websites with the use of template controller. I have a template I use for the layout. In the template I include my JS and CSS files.

    Some sub-pages of my website(s) can include additional JS/CSS files. I would like to avoid including all JS/CSS files on all pages of my website, because they are not needed everywhere. How do you approach this?

    Thanks
    Jume
  • I have a layout controller where I define an array of css paths and an array of JS paths and pass them to the template view.
    Then in the template view I check for the css and js arrays and just echo and echo the paths.

    This way you can define a base array for the general css and js files that has to be included on every page and you can define per controller the css and js files.
  • I do it in almost the same way - seems to be a Hungarian habit :P :)

    I have got a core controller (descendant of Controller_Template) and all other controllers are extended from it. In the core controller I store the path of the css/js files in two arrays, and the application controllers can add items to them. Plus, in the after method I look for some defaults: if there is a /res/css/<controller-name>/<action-name>.css then I add it to the css array (and I do the same for the js files). After this process I render the ouput.
  • Similar to above, I have a Website_Controller that extends the base Template_Controller. The Website_Controller has a $page array property that stores most of the basic page information along with defaults for elements such as css, js, meta tags, etc. I then have a few public methods in the Website_Controller such as "add_js()", "add_css()" that can be called by the various controllers that extend the Website_Controller to add per page js and css.
  • I have the same exact add_css and add_js methods too :)
    The only thing that differs is I have a layout.php in the config folder which holds the page basic information, and paths to css and js files with keywords.

    So for ex. I have this in config/layout.php

    $js => array('jquery' => ASSETS_JS . 'jquery-1.4.2.min.js');

    Then in the controller I just call:

    $this->add_js('jquery');

    This way if a path changes, for ex. I want to use jquery from Google's repo. then I just only have to change the array in config/layout.php



  • I also hold all paths in a config file, the same as @feketegy.

    However, I offload all view processing to an extended View class, and never use a template controller.

    That way you can load up a primary view with a bunch of add_whatever() methods, and even extend when you need views with even more specific functionality, have an RSS_View, XML_View, etc.

    $this->view = new SuperView; // SuperView extends view, constructor takes care of core CSS, JS, metatags, titling, etc
    $this->view->add_css('custom.css') // add anything else you like


    Why clog up controllers with something that's not controller-related?
  • @DaveStewart: Your approach sounds very interesting. I've always focused so much on the template controller for rendering and never really looked into additional view classes.
  • @neovive - In regards to Dave's comment 'why clog up controllers with something that's not controller-related', you might find the following articles interesting:

    Why the view has access to the model
    Using View Helpers to increase view reusability

    along with some discussions here on the forum, they got me interested in playing around with zombor's KOstache module and getting some nice, thin and reusable controllers.
  • @LordZicon: thanks for the links ... very interesting.
  • @LordZicon: Those links are very interesting indeed.

    I've found a way to make reusable views and controllers. However, I do read GET and POST, validation, etc... on the controller.

    In other note, I personally try to avoid non standards like Mustache, LESS, etc.
  • @Kaltar: I'm curious as to what you mean by non-standards.. do you mean because they are not widely used?
  • @LordZicon quite interesting articles, thank you for sharing
  • @DaveStewart that sounds interesting.

    Does that SuperView class automatically load some sort of template file that contains the page structure?

    Eg:


    views/templates/layout.php
    <html>
    <head>
    // create title, meta, css, and js tags etc.
    </head>
    <body>
    <div class="header">
    Header Stuff
    </div>
    <div class="content">
    <?php echo $content ?>
    </div>
    <div class="footer">
    Footer Stuff
    </div>
    </body>
    </html>



    class Controller_Welcome extends Controller
    {
    public function action_index()
    {
    $view = new SuperView('templates/layout');
    $view->content = View::factory('pages/welcome');
    echo $view;
    }
    }


    Can you share your extended view class?
  • You're halfway there.

    If you want to use a factory, create the static method on your SuperView Class. Essentially you're just moving any view-related code to the extended view class.

    So instead of your 3 view lines, you could only have one:

    echo SuperView::factory('optional/path/to/file.php')->add_css('an/optional/css/file.css')->render();


    I'd only bother passing through a (optional) path to the template files when you really want customisation - otherwise, your View file defaults should really take care of that.

    You'll need to override the render() method to correctly output the head and body code. It's not voodoo by any stretch of the imagination, it's just more logically placing view related code where it belongs. Have courage! Forget what you know about "dumb templates" and embrace a certain amount of logic in your views, and accept that a Master View class is a good thing.

    Here's a Template View class from one of my projects. I haven't looked at it in 9 months though - so if you find any glaring errors, do let me know!


    <?php<br />
    /**
    * Template View
    *
    * The Template View class sets up a variety of core page elements, such as
    * page title, adding scripts, stylesheets, and groups of assets from the
    * "assets" configuration file.
    *
    * The variables $title, $css, and $scripts are assumed to be set in the template.
    *
    */
    class Template_View extends View
    {
    // --------------------------------------------------------------------------------------------------
    // VARIABLES
    // --------------------------------------------------------------------------------------------------

    // template and auto-render
    protected $template = 'template';
    protected $auto_render = TRUE;

    // default page variables
    public $title = NULL;

    // scripts and css arrays (these are converted to strings upon rendering)
    public $scripts = array();
    public $css = array();

    // --------------------------------------------------------------------------------------------------
    // INITIALIZE
    // --------------------------------------------------------------------------------------------------

    /**
    * Template_View constructor
    */
    public function __construct($template = NULL, $data = NULL)
    {
    // default path
    if($template == NULL)
    {
    $template = $this->template;
    }

    // default title
    if($data == NULL)
    {
    $data = $this->title;
    }
    if(is_string($data))
    {
    $data = array('title' => $data);
    }

    // constructor
    parent::__construct($template, $data);

    // auto-render
    if ($this->auto_render == TRUE)
    {
    Event::add('system.post_controller', array($this, '_render'));
    }

    }

    /**
    * Chainable method to create an instance of Template_View
    */
    public static function factory($template = NULL, $data = NULL)
    {
    return new Template_View($template, $data);
    }

    // --------------------------------------------------------------------------------------------------
    // CORE PROPERTIES
    // --------------------------------------------------------------------------------------------------

    /**
    * Chainable method to change the view template file
    */
    public function set_template($template)
    {
    $this->set_filename($template);
    return $this;
    }

    /**
    * Chainable method to set the title variable of the view
    */
    public function set_title($value)
    {
    $this->title = $value;
    return $this;
    }

    /**
    * Load single or multiple stylesheets, searching in the paths set up in the assets configuration file
    */
    // --------------------------------------------------------------------------------------------------
    // ASSETS
    // --------------------------------------------------------------------------------------------------

    public function css($file)
    {
    if(is_array($file))self::css($file);
    else $this->css[$file] = html::stylesheet(Kohana::config('assets.paths.css') . $file);
    return $this;
    }

    /**
    * Load single or multiple scripts, searching in the paths set up in the assets configuration file
    */
    public function script($file)
    {
    if(is_array($file))self::script($file);
    else $this->scripts[$file] = html::script(Kohana::config('assets.paths.scripts') . $file);
    return $this;
    }

    /**
    * Load groups of assets specified by values set in the assets configuration file
    */
    public function assets($key)
    {
    $assets = Kohana::config('assets.assets.' . $key);
    if($assets != NULL)
    {
    if(array_key_exists('css', $assets))
    {
    foreach($assets['css'] as $file)
    {
    $this->css($file);
    }
    }
    if(array_key_exists('scripts', $assets))
    {
    foreach($assets['scripts'] as $file)
    {
    $this->script($file);
    }
    }
    }
    return $this;
    }

    // --------------------------------------------------------------------------------------------------
    // CORE
    // --------------------------------------------------------------------------------------------------

    /**
    * Set all stored variables in the view and render
    */
    public function render($print = FALSE)
    {
    // set variables
    $this
    ->set('title', htmlentities($this->title))
    ->set('css', implode('', $this->css) . "\n")
    ->set('scripts', implode('', $this->scripts) . "\n");

    // render
    parent::render($print);
    }

    /**
    * Callback for auto-rendering
    */
    public function _render()
    {
    if ($this->auto_render == TRUE)
    {
    $this->render(TRUE);
    }
    }

    /**
    * Override parent class to magically set view variables
    *
    * @param string variable key
    * @param string variable value
    * @return void
    */
    public function __set($key, $value)
    {
    $protected = array('template', 'auto_render');
    if(!in_array($key, $protected))
    {
    $this->kohana_local_data[$key] = $value;
    }
    }



    }

    ?>
  • And here's the PHP template:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title><?php echo $title; ?></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

    <?php
    echo $css;
    echo $scripts;
    echo $editable;
    ?>
    </head>
    <body style="margin-bottom:100px; clear:both; background-color:#bbd1e5;">
    <div id="body" style="margin-bottom:100px; clear:both"">
    <div id="header">
    <?php
    echo $header;
    ?>
    </div>

    <div id="nav-container" style="height:50px;">
    <?php
    $nav->set('class', 'menu horizontal interactive dropdown')->render('top-nav', TRUE);
    ?>
    </div>

    <div id="content" class="<?php echo count(Router::$segments) ? Router::$segments[0] : 'home';?>">

    <?php
    echo $content;

    if(isset($sidebar))
    {
    $sidebar->render(TRUE);
    }
    ?>
    </div>
    <div class="clear"></div>
    <div id="footer">
    <p style="float:right">© Copyright Pacific <?php echo date('Y'); ?>. All rights reserved</p>
    <?php
    $nav->set('class', 'horizontal')->render('foot-nav', TRUE);
    ?>
    </div>
    </div>
    </body>
    </html>
  • Thanks for sharing that. It's very helpful.
  • @LordZicon: Yes. For example, I could use LESS for CSS, but every time I add some new technology, my code has more dependencies. I try to keep it with the less dependencies as possible.

    @Dave Stewart: Thanks for sharing! Very interesting indeed!
  • Hey guys thanks for so many responses. It turned out a great discussion and I'll be able to fetch out the best model for static includes!