This tutorial presents a way of separating JS code from views and passing to it values from PHP.
Yii provides two helpful ways of keeping JavaScript code close to the widgets and other elements they interact with:
- strings prefixed with 'js:'
- CClientScript.registerScript method
Quickly, small snippets of JavaScript code turn into big ugly strings filled with PHP variables and without proper syntax highlighting.
This tutorial shows a method of organizing JavaScript code and integrating it with a PHP backend.
jQuery plugin template ¶
This template for building JavaScript plugins is proposed by jQuery:
(function( yourPluginName, $, undefined ) {
// public method
yourPluginName.someCallback = function() {
};
}( window.yourPluginName = window.yourPluginName || {}, jQuery ));
This defines a function that is immediately called and passed two arguments:
- a reference to window.yourPluginName
- the jQuery object
This allows you to extend the global yourPluginName object by adding methods and properties to it, keeping them all in one scope.
Now you can place your bulky JS code from your views inside this plugin and use them as:
yourPluginName.someCallback();
Put that template in a .js file, for example protected/components/assets/yourPluginName.js.
Then register it in your action or view:
$path = Yii::app()->assetManager->publish(Yii::getPathOfAlias('application.components.assets'));
Yii::app()->clientScript->registerScriptFile($path.'/yourPluginName.js');
Namespaced code ¶
By putting all your code in this plugin you keep them in a limited scope and thus create a namespace for it. That helps avoiding name conflicts and keeps your code cleaner.
Another important feature is that this plugin can be registered many times. Sometimes this can happen when you load an action through AJAX on a page that has already registered the script.
This also allows extending if further, adding more functions.
Also, since there actually is a limited scope, strict mode can be enabled:
(function( yourPluginName, $, undefined ) {
"use strict";
// ... more code
}( window.yourPluginName = window.yourPluginName || {}, jQuery ));
This helps to detect browser-specific issues early in the development that is done using the developer's favourite browser, not the ones that clients are using.
Passing variables from PHP ¶
Very often JavaScript code needs data from PHP, like URLs, ids, translated labels and other configuration.
They can be stored inside the plugin by adding a private variable and an init() function:
(function( yourPluginName, $, undefined ) {
// guard to detect browser-specific issues early in development
"use strict";
// private var
var _settings;
// public var
yourPluginName.someProperty = 'default value';
// public method
yourPluginName.init = function(settings) {
_settings = $.extend({}, settings);
}
}( window.yourPluginName = window.yourPluginName || {}, jQuery ));
Now after registering the script file add a call to the init() function on document load:
$options = CJavaScript::encode(array(
'someUrl' => $this->createUrl('someUrl'),
'someLabel' => Yii::t('app', 'someLabel'),
));
Yii::app()->clientScript->registerScript(__CLASS__.'#yourPluginName', "yourPluginName.init($options);", CClientScript::POS_READY);
The '_settings' var is private and can be only referenced inside functions defined in yourPluginName;
Now your PHP and JS code is:
- separated and cleaner
- safer
- easier to read
- more extensible (no more hardcoded labels and urls in JS!)
@nineinchnick
good work. we will try it :-)
JS code examples pls
Hi, good work. But I miss js examples. I think the beginners will make wrong codes from this schema at the first times; and we know that debugging a js code is not easy :)
examples
What do you mean you miss examples? There are examples.
Very good practice
It's a very good practica to keep everything separated.
Recommended.
As @blaces says: A complete example will help beginners to misunderstand what you mean, including the simple example of a view and a JS file.
Or even easier. You can create a github repo with a simple application using an example.
Nice. Very helpful for JS plugin development. Here is a simple example of a plugin
<script> (function( animatemenu, $, undefined ) { // public method animatemenu.animate = function(id,options) { var defaults = { animatePadding: 60, defaultPadding: 10, evenColor: '#ccc', oddColor: '#eee', }; $(id).each(function() { var o =options; var obj = $(this); var items = $("li", obj); $("li:even", obj).css('background-color', o.evenColor); $("li:odd", obj).css('background-color', o.oddColor); items.mouseover(function() { $(this).animate({paddingLeft: o.animatePadding}, 300); }).mouseout(function() { $(this).animate({paddingLeft: o.defaultPadding}, 300); }); }); }; }( window.animatemenu = window.animatemenu || {}, jQuery )); </script> <script type="text/javascript"> $(document).ready(function() { animatemenu.animate('#menu',{animatePadding: 30, defaultPadding:10}) }); </script> <style> body {font-family:arial;font-style:bold} a {color:#666; text-decoration:none} #menu {list-style:none;width:160px;padding-left:10px;} #menu li {margin:0;padding:5px;cursor:hand;cursor:pointer} </style> </head> <body> <ul id="menu"> <li>Home</li> <li>Posts</li> <li>About</li> <li>Contact</li> </ul>
Excellent work!, I would like to share another example of a plugin.
<script> !function(module, $, window, document, undefined) { 'use strict'; var NamePlugin = function() { console.log('NamePlugin'); /*! * Private variables, properties and methods */ // variables var pluginName = 'NamePlugin'; var publicApi = {}; // public api to expose // properties var properties = {defOpt1: 'defOpt1', defOpt2: 'defOpt2'}; // methods var constructor = (function() { console.log(pluginName + ':constructor'); })(); var privateMethod1 = function(message) { console.log(pluginName + ':privateMethod1' + ' ' + (message ? message : '')); }; var privateMethod2 = function() { console.log(pluginName + ':privateMethod2'); }; /*! * Public properties and methods */ // properties publicApi.property1 = 'Publi Property 1'; publicApi.property2 = 'Publi Property 2'; publicApi.defaults = {defProp1: 'defProp1'}; publicApi.settings = {}; // methods publicApi.method1 = function() { console.log(pluginName + ':publicApi.method1'); }; publicApi.method2 = function() { console.log(pluginName + ':publicApi.method2'); }; publicApi.method3 = function() { console.log(pluginName + ':publicApi.method3:expose:privateMethod1'); privateMethod1(); }; publicApi.method4 = function() { console.log(pluginName + ':publicApi.method4:expose:privateMethod2'); privateMethod2(); }; //publicApi.method5 = privateMethod1('from publicApi.method5'); publicApi.init = function(options) { console.log(pluginName + ':publicApi.init'); publicApi.settings = $.extend({}, publicApi.defaults, options); privateMethod1('defaults' + JSON.stringify(publicApi.defaults)); privateMethod1('options' + JSON.stringify(options)); privateMethod1('settings' + JSON.stringify(publicApi.settings)); return publicApi; }; // return the public API (Plugin) to expose return publicApi; }; module.NamePlugin = NamePlugin; }(this, jQuery, window, document); </script> // Passing variables #1 (from view) <script> var myRoute = "<?php echo $this->createUrl('someUrl'); ?>"; var myNamePlugin = new NamePlugin; myNamePlugin.init({name: 'my name', lastName: 'my last name', route: myRoute}); myNamePlugin.method1(); myNamePlugin.method2(); myNamePlugin.method3(); myNamePlugin.method4(); console.log('Get Public Prop1: ' + myNamePlugin.property1); console.log('Get Public Prop2: ' + myNamePlugin.property2); console.log('Get Private Prop: ' + myNamePlugin.pluginName); // undefined because of pluginName is private <script> // Passing variables #2 <?php $options = CJavaScript::encode(array( 'someUrl' => $this->createUrl('someUrl'), 'someLabel' => Yii::t('app', 'someLabel'), )); Yii::app()->clientScript->registerScript(__CLASS__.'#yourPluginName', "var myNamePlugin = new NamePlugin; myNamePlugin.init($options);", CClientScript::POS_READY); ?>
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.