Pure javascript in views

Now there is no standard good way to embed javascript snippets into views.

Usually these snippets are code that does job specific to each page, like register event handlers.

Yii supports this via CHtml helpers, for example CHtml::ajaxLink, but I never liked this approach, because very soon your view became a mix of HTML and php arrays.

From the first sight helpers seems to be easier way to do ajax requests, but when something doesn’t work as expected it can be really hard to find the reason and solve problem

I think having pure JS code which does the same job is much better and for some time I used something like this:




//here view code, mostly HTML with php snippets to insert variables

//...

//...

//block with JS code

<?php

<?php

Yii::app()->clientScript->registerScript('settings-script', <<<EOD

    initForms('myform');

    $('.sel-image').live('click',function() {

        $('#preview img').attr('src', $(this).find('img').attr('src'));

        $(this).parent().addClass('active');;

        $(this).parent().siblings().removeClass('active');

        $('#SettingsData_stdImg').attr('value', $(this).attr('rel'));

        return false;

    });

EOD

);

?>



But this way JS code is embedded into the php string and you can forget about code highlight and completions.

So I started to search for better solutions.

One of approaches is to have separate folder where we have JS files for controllers / actions (similar structure to the ‘views’ folder) - http://weavora.com/blog/2011/11/24/yii-framework-an-easy-was-to-split-javascript-from-html/ and (in russian) - http://sergebezborodov.com/yii/loading-js-files-in-yii-framework.html.

I do not like this because js code now is too far from the view and what I want is just to be able to put js code into view (but at the same time I do not want it to be rendered in-place, but it should go into page head or end or jQuery ‘ready’ section).

I looked for solutions for Rails and found many advice like in this question.

For me this Rails solution can be implemented in Yii using clips. In the view file we do:




<?php //$this->beginClip('js-page-end'); ?>

<script type="text/javascript">

    alert('my script');

</script>

<?php //$this->endClip(); ?>



And then we render this clip in the layout:




//layout HTML

//...

//render clip with js, if there is no such clip then it will return empty string

<?php $this->renderClip('js-page-end'); ?>



Easy and good solution, but it will not work for the ajax requests where we use renderPartial and in this case layout is not rendered.

And, finally, the solution suggested by Tsunu.

He added a beginScript() and endScript() to the client script component, so code looks like this:




<?php $clientScript = Yii::app()->clientScript; ?>

<?php $clientScript->beginScript('my-script'); ?>

<script type="text/javascript">

    alert('my script');

</script>

<?php $clientScript->endScript(); ?>



There is a little hack here - CClientScript automatically wraps script into <script> tags, so in the endScript() Tsunu uses strip_tags($script) before register it using parent registerScript() method.

In conclusion - I think the last solution is close to ideal and it would be good to have support for this in the core CClientScript class.

Hello, there’s something I’m missing here. I just jumped on to the thread you mention, and I still can’t get it.

If you want just to embed JS code into your views, what prevents you from doing like this


<?php

    // some PHP code

    …

?>

<script type="text/javascript">

    alert('my script');

</script>

<?php

    // other PHP code

    …

?>

On the other hand, the following syntax allows specifying where to embed the JS (head, at the end of html elements, or on document ready):


<?php Yii::app()->clientScript->registerScript('script', <<<JS

    alert('my script');

JS

, CClientScript::POS_READY);?>

So imho, if you don’t care about JS placement, just embed it straight like in the first case, otherwise, use Yii’s registerScript method, whether you’re waiting for document ready (DOM manipulation for instance), or just not to hold on the HTML parsing, or even for easier debugging (all JS code at the same places).

So, what am I missing? :unsure:

[quote=“seb”]

I think having pure JS code which does the same job is much better and for some time I used something like this:




//here view code, mostly HTML with php snippets to insert variables

//...

//...

//block with JS code

<?php

<?php

Yii::app()->clientScript->registerScript('settings-script', <<<EOD

    initForms('myform');

    $('.sel-image').live('click',function() {

        $('#preview img').attr('src', $(this).find('img').attr('src'));

        $(this).parent().addClass('active');;

        $(this).parent().siblings().removeClass('active');

        $('#SettingsData_stdImg').attr('value', $(this).attr('rel'));

        return false;

    });

EOD

);

?>



[b][u]But this way JS code is embedded into the php string and you can forget about code highlight and completions.

So I started to search for better solutions.[/u][/b]

so thi is why it is not acceptable for him…

Yes, this works, but this way it will be hard to find pieces of JS inside the document. Also usually I wan to include script into the jQuery ready block (or other specific place), but not just anywhere.

Yes, StasuSS is right - as I mentioned above that I used heredoc strings before, but this way you have php string instead of JS code and editors / IDEs can not recognize it.

Yes, that’s the downside, and I generally don’t do that.

Yes in fact with the present way of doing it, we can only have either advantage: code completion or correct JS/jQuery handling :lol:

For the moment, I’m just happy that my IDE matches braces inside heredoc strings :D

Bottom line: +1 for the feature request :)

PS I guess I’ve read your message way too fast, you haven’t used coreScriptPosition property, so I thought that you didn’t care about putting your script in $(document).ready(), while at the same time you said it in your explanation

FYI, PhpStorm works fine with JavaScript in PHP strings/heredoc.

Yes, but others don’t. I have vim for editing and Netbeans for debugging. Maybe vim can be tuned to highlight js in strings, but Netbeans not.

Also it is not difficult to write own CClientScript subclass to support this.

But I believe that Yii core should support this and more then that - advertise this approach somehow through the docs.

Maybe my feature request looks too narrow, but I mentioned this in other Yii 2 discussion topics - in would be good to review the approach to support JS code and AJAX requests.

For example, look here (tutorial for rails 3):

Replace the "Ruby on Rails" with "Yii" and "Prototype" with "jQuery" and here is the description of current situation with JS code in Yii.

So what in conclusion?

I’ve mentioned to use this solution:




<?php Yii::app()->clientScript->registerScript('script', <<<JS

    alert('my script');

JS

, CClientScript::POS_READY);?>



but its realy dirty, IMHO. Does any body found better at all?

Yes, see the last solution in my first post:

So you can have pure js code blocks inside the view file.

To make this work you need to extend CClientScript as described in this forum post.

I currently use a bit modified code:




class AppClientScript extends CClientScript {


    protected $activeScriptId;

    protected $activeScriptPosition;


    public function beginScript($id, $pos = parent::POS_READY) {


        $this->activeScriptId = $id;

        $this->activeScriptPosition = $pos;


        ob_start();

        ob_implicit_flush(false);

    }


    public function endScript() {


        $script = ob_get_clean();

        $script = strip_tags($script, '<p><a><br><span><div><b><i><strong>');

        parent::registerScript($this->activeScriptId, $script, $this->activeScriptPosition);


    }

}



This is a great idea, but strip_tags() is too restrictive, as it removes any tags you may use in your jQuery DOM manipulation. The answer above improves this, but is not complete. Since strip_tags() uses a whitelist, it is not really practical. It think preg_replace() will be more appropriate. Like this:


preg_replace( '%</?script[^>]*>\s*%', '', $script )