Git Product home page Git Product logo

twig.js's Introduction

Twig.js

[Build Badge] Build Status [Scrutinizer Badge] Scrutinizer Code Quality

Twig.js is a PHP project that compiles Twig templates into executable Javascript for client-side execution. It is not to be confused with Twig.js, which is a pure Javascript implementation of the Twig templating language.

Twig Compatibility

Compatibility with vanilla PHP Twig is not yet at 100%. If you need your templates to work with both Twig.js and Twig, stick to the supported filters and functions described below.

Supported Filters

  • abs
  • batch
  • capitalize
  • default
  • e
  • escape
  • first
  • join
  • json_encode
  • keys
  • last
  • length
  • lower
  • merge
  • nl2br
  • raw
  • replace
  • reverse
  • title
  • trim
  • upper
  • url_encode

Supported Functions

  • block
  • include
  • max
  • min
  • random
  • range

Incompatibilities

The following is a list of functionality present in Twig that is not yet available in Twig.js. There are some really easy pickings in these lists for anybody hoping to make a contribution to the project.

Unsupported Filters

  • convert_encoding
  • date (See pull request #11)
  • date_modify
  • format
  • number_format
  • round
  • slice
  • sort
  • split
  • striptags

Unsupported Functions

  • attribute
  • constant
  • cycle
  • date (See pull request #11)
  • dump
  • parent
  • source
  • template_from_string

Testing

To run the tests, you'll need Composer, Node and NPM on your system.

$ make test

License

Twig.js is released under the Apache License, Version 2.0.

twig.js's People

Contributors

anod avatar br0wn avatar dermanomann avatar fduch avatar fran6co avatar henrycatalinismith avatar jeremyfreeagent avatar jonathaningram avatar josiah avatar jstuhli avatar julesbou avatar kimlai avatar schmittjoh avatar stefk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

twig.js's Issues

For loop variable scopes

Let's look at the following code:

{% for myVar in myArr %}
  {% if myVar.myAttr %}
    {% set aValue = 'text' %}
  {% endif %}
  {{ aValue | default('') }}"
{% endfor %}

Let's assume multiple objects in myArr and that myAttr is true on the first object in the array and false on subsequent ones.

Is it intentional that aValue will now have the value text in all loop iterations? I would assume each loop iteration to start with a new scope.

Need an example on how to include a template inside another

I have this two templates

{# user.notebooks.item.html.twig #}
<li class="clearfix notebook-item _notebook-item">
    <input type="checkbox" class="checkbox" name="notebook[]" value="{{ notebook.id }}"/>
        <a class="_btn-notebook" href="#">
            <div class="icon">
                <span class="{% if(notebook.privacy == 0) %}icon-20-padlock{% else %}icon-20-heart{% endif %}"></span>
            </div>
            <div class="block-content">{{ notebook.title }}</div>
        </a>
</li>

and

{# user.notebooks.html.twig #}
<ul>
{% for notebook in notebooks %}
    {% include user.notebooks.item.html with {'notebook' : notebook} %}
{% endfor %}
</ul>

As you can see i'm trying to include the first template inside the second one but i'm not able to "find" the right syntax to do so...
Both templates have been loaded with assetic this way:

{% javascripts  
              "@SbaamPortalBundle/Resources/views/components/common/user.notebooks.item.html.twig"
              "@SbaamPortalBundle/Resources/views/components/common/user.notebooks.html.twig"
              filter="twig_js"
%}
    <script language="javascript" src="{{ asset_url }}"></script>
{% endjavascripts %}

It's doesn't work... Am I missing something?

(Include with {}) only keyword support

Is it possible to add support for the only keyword?

i.e. if I have the following template, a.html.twig:

{% set shouldBeUndefined = 0 %}
{% include 'b.html.twig' with { x: 0 } only %}

b.html.twig should not have access to shouldBeUndefined then (if I understand only correctly). In my case, I see no difference between using only or not; the included template B can always access it.

Major addition problem (with temporary fix)

In some cases when the addition operator (+) is used, the operands are concatenated together instead of added.

At first, I thought this was a problem with Twig itself; see #936.

Let's say the price is 30 and the mark-up is 5:

<p>{{ offer.product.price + offer.markUp}}</p>

Result: <p>305</p>

({{ 1 + 1 }}, as per the Twig documentation, works fine)

From our application

Rendered server-side:
Screen snippet of before

Once TwigJS renders the template (exact same data-source):
Screen snippet of after

Temporary fix

<p>{{ offer.product.price * 1 + offer.markUp * 1 }}</p> 

Result: <p>35</p>

Included templates not receiving context

I have a template that looks like this:

{% set dt = '' %}
{% for game in games %}
    {% set stats = game.GameStatistics %}
    {% set gameDt = game.PlayDate|date('l F jS') %}
    {% if dt != gameDt %}
        {% set dt = gameDt %}
        <h2 class="date row">{{ dt }}</h2>
    {% endif %}
    {% include 'JMSHockeyAppBundle:Games:tpl.listgame.html.twig' %}
{% endfor %}

Within tpl.listgame.html.twig, I attempt to use the variables game, stats, gameDt, etc, however, in all cases they are null except for gameDt.

I'm not sure if I'm doing anything wrong, but any clues to debugging this would be helpful. I've also tried using the {% include template with object %} syntax to no avail.

Requesting adding "extensions" behaviour to twig.environment

As part of my FormExtension.js work, it seems to me that is makes sense for my extension to be registered In the twig environment via twig.environment.addExtension (I.e. verbatim to \Twig_Environment).

Two queries:

  1. Does it seem reasonable to do this? In which case the extension can also provide additional globals, filters, functions as per Twig
  2. This means that the current implementation won't work until it is updated to use extensions too (I think). E.g. instead of having a call to my method FormExtension.render_errors, as per Twig, I'd like the compiler to generate "this.env_.getExtension('form').renderErrors(...)" (I can't remember the exact syntax right now).

What do you think? Sound like this is the way to go ahead?

Add support for js side trans and path

There are 2 projects that add JS side support for some Symfony components.

https://github.com/FriendsOfSymfony/FOSJsRoutingBundle
https://github.com/willdurand/BazingaExposeTranslationBundle

It would be interesting to connect twig.js to them (if enabled).

At the moment I'm using something similar to this:

if (typeof ExposeTranslation !== 'undefined') {
    ExposeTranslation.placeHolderPrefix = '';
    ExposeTranslation.placeHolderSuffix = '';

    Twig.setFilter('trans', function(value,params) {
        return ExposeTranslation.get(value,params);
    });
}
if (typeof Routing !== 'undefined') {
    Twig.setFunction('path', function(value,params) {
        return Routing.generate(value,params);
    });
}

Not sure where it should be the integration, I came up with some options:

  • Part of the twig.js engine
  • In the JMSTwigJsBundle
  • As a documentation file

What do you think?

Issue with creating a function

Hey ! First of all, thanks for your work, it is great.

I'm using the twigJsBundle with symfony2 and it works nice.
Now, I'm trying to make it work with this bundle : https://github.com/Gregwar/ImageBundle.

Basically, it is used like this : {{ image('linux.jpg').width }}px in a template. The function works well in a standard template.

The problem is that when I use this function with the twigJsBundle, the function image is not defined.

So I looked in the doc to find how to fix it and what I did is that I added this in the JSCompiler.php file : (line 213)

$this->functionMap = array(
'range' => 'twig.range',
'image' => 'image'
);

Can you help me with this ?

Thanks a lot :)
Louis

Node "ifexpr" does not exist for Node "Twig_Node_For"

Hello,

I'm using twig version 1.5, function $node->getNode('ifexpr') throws an exception.

Exception throws in ForCompiler.php(123): Twig_Node->getNode('ifexpr')

There is a couple of places where getNode is called: if (null !== $node->getNode('ifexpr'))
and all of them needs to be updated with if ($node->hasNode('ifexpr'))

I can commit a patch, but not sure what is the right way to do it....

Thank you.

replace filter '.'

I needed the replace filter to slug a name to use it for a css class. I had to replace the '.' to '-'.
This fails in twig.js, because the replace filter is always using a regex and the '.' has a special meaning in regular expressions.

example:

<div class="{{domain | replace({ '.' : '-' })}}">

for example if domain is mydomain.nl it will be replaced as mydomain-nl in twig, but it's being replaced as ----------- in twig.js

Documentation

Hi ,
I am wondering where should I place the twig templates and after that what should I run to get the javascript .
Have downloaded all the dependencies and I am able to run the tests.
But wondering where I should place the twig files.
Please help .

Thank you

Hari K T

is not null expression missing brackets

Hello,

Twig template contain next statement:
{% if item.review.subratings.location is not null %}

It is not compiled correctly:
if ((!null === twig.attr(twig.attr(twig.attr(tmp_item, "review"), "subratings"), "location"))) {

Expected result
if ((!(null === twig.attr(twig.attr(twig.attr(tmp_item, "review"), "subratings"), "location")))) {

Changing NoneCompiler to wrap subnodes with brackets fixes the issue:

$compiler
->raw('(null === ')
->subcompile($node->getNode('node'))
->raw(')')
;

Problem when underscores in template name (IE8)

In Internet Explorer 8, I got the "object does not support property or method" error. After enabling debugging it seemed to be where the global function is declared;

template_name = function(env){ //IE debugger stopped here
     twig.Template.call(this, env);
}

Context: I'm retrieving the template scripts with AJAX after the page loads, the error occurs when they're being interpreted I guess.

It works now since I've removed the underscores from the template name, but I'm not really sure why there's a problem with this.

Use the form

Can I use the form with twig.js?
If you use a template with a form back to me an internal error 500!

Amd module explicit name

Hi @h2s,

I don't understand the purpose of this commit. It broke everything for me, and the require-js doc says this :

You can explicitly name modules yourself, but it makes the modules less portable -- if you move the file to another directory you will need to change the name. It is normally best to avoid coding in a name for the module and just let the optimization tool burn in the module names.

Could you elaborate on why you changed it ?

Thanks (and thanks for merging the amd module compiler in the first place).

Create a new FunctionNamingStrategy

Hi,

I would like to create another strategy to name my generated Twig templates. In the JsCompiler.php Class I found the 'setFunctionNamingStrategy'. What is the proper way to call this method in my application without modifying the extension code? Is there a configuration I can override to specify a custom implementation of a FunctionNamingStrategy?

twig.renvironment.render method does not pass through "opt_blocks" variable

Is it meant to?

Here is the environment render method:

/**
 * Returns the rendered template.
 *
 * @export
 * @param {Function} ctor The constructor of the template
 * @param {Object.<*>=} opt_context
 * @return {string}
 */
twig.Environment.prototype.render = function(ctor, opt_context) {
    var template = this.createTemplate(ctor);

    return template.render.call(template, twig.extend({}, this.globals_, opt_context || {}));
};

And the template render method:

/**
 * @param {Object=} opt_context
 * @param {Object.<twig.Template.Block>=} opt_blocks
 * @return {string}
 */
twig.Template.prototype.render = function(opt_context, opt_blocks) {
    var sb = new twig.StringBuffer();
    this.render_(sb, opt_context || {}, opt_blocks || {});

    return sb.toString();
};

Notice this accepts opt_blocks but since twig.environment.render does not have that parameter you cannot bubble through to twig.Template.render. Is this an oversight? If so I can provide a PR. If not, why was it done like this?

Note: this is how you get around it right now:

var sb = new twig.StringBuffer();

Twig.createTemplate(some.template.html).render_(sb, {}, {
    a_block: function(sb, context, blocks) {
        var s = '<p>...</p>';
        sb.append(s);
    },
    b_block: function(sb, context, blocks) {
        sb.append('<p>...</p>')
    },
    c_block: function(sb, context, blocks) {
        sb.append('<p>...</p>');
    }
});

console.debug(sb.toString());

twig.forEach does not iterate over (for example) FormView's children

Is there a way to represent a class implementing traversable in JavaScript?

e.g. the PHP code:

<?php

$context['_seq'] = twig_ensure_traversable($this->getContext($context, "form"));
foreach ($context['_seq'] as $context["_key"] => $context["item"]) {
    // line 10
    echo $this->env->getExtension('form')->renderRow($this->getContext($context, "item"), array("mykey" => $this->getContext($context, "mykey")));
}

Works because:

<?php

class FormView implements \ArrayAccess, \IteratorAggregate, \Countable
{
}

But in twig.js, it generates:

var seq = "form" in context ? context["form"] : null;
    twig.forEach(seq, function(v, k) {
        context["_key"] = k;
        context["child"] = v;
        // line 263
        sb.append("        ");
        if ((!twig.attr("child" in context ? context["child"] : null, "rendered"))) {
            // line 264
            sb.append("            ");
            sb.append(this.env_.getExtension("form").renderRow("child" in context ? context["child"] : null));
            sb.append("\n        ");
        }
        // line 266
        sb.append("    ");
    }, this);

Which does not know that FormView can be traversed via formView.children

setFilterFunctionName()

I'll have to add/reference a custom filter, According to the filter.rst documentation, Sounds good to add this during the compile phase.

Unfortunately, can't find any setFilterFunctionName() method in the TwigJs\JsCompiler.php class nor in the whole Twigjs repository

Could you confirm this feature is implemented ? Is the $compiler object referenced in the filter.rst documentation correspond really to the Twigjs\JsCompiler class or to something else ?

Please let me know,

Best,

Test failure relating to check for absence of '' in an object

This is another test failure in the ported Twig tests.

--TEST--
Twig supports the in operator
--TEMPLATE--
{% if bar in foo %}
1 TRUE
{% endif %}
{% if not (bar in foo) %}
{% else %}
2 TRUE
{% endif %}
{% if bar not in foo %}
{% else %}
3 TRUE
{% endif %}
{% if 'a' in bar %}
4 TRUE
{% endif %}
{% if 'c' not in bar %}
5 TRUE
{% endif %}
{% if '' not in bar %}
6 TRUE
{% endif %}
{% if '' in '' %}
7 TRUE
{% endif %}
{% if '0' not in '' %}
8 TRUE
{% endif %}
{% if 'a' not in '0' %}
9 TRUE
{% endif %}
{% if '0' in '0' %}
0 TRUE
{% endif %}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
--EXPECT--
1 TRUE
2 TRUE
3 TRUE
4 TRUE
5 TRUE
6 TRUE
7 TRUE
8 TRUE
9 TRUE
0 TRUE

The 6th line is missing, which means that the error is coming from here:

{% if '' not in bar %}
6 TRUE
{% endif %}

The value of bar is bar, so it doesn't contain the empty string. Looks like a potential edge case not yet handled by twig.contains. Should be an easy fix (famous last words!).

"Object #<Object> has no method 'require'" error on compiled templates

Getting a bunch of these since the last commit, came through on a composer update (would be great if a stable version could be tagged)

Seems like this might be something minor with closure but I don't understand closure's workings enough to confirm this. I just know that the "goog" object only have a "provide" function and no "require" function on it when I look at it on the console.

goog
    Object
        provide: function ($name$$53$$) {
        __proto__: Object

Using apply_to assetic configuration

I'm trying to use 'apply_to' configuration to match .twig files when defining my resources dependencies.

With something like this:

assetic:
    filters:
        twig_js:
            apply_to: "\.twig$"

I'm getting the following error:

InvalidArgumentException: The file "filters/twig_js.xml" does not exist (in: /home/christophe/devs/Symfony/vendor/bundles/Symfony/Bundle/AsseticBundle/DependencyInjection/../Resources/config).

I'm a symfony newbie, any clues to solve this?

jQuery 1.9 complains when parsing a template because of its trailing spaces

Iโ€™d like to "jquerify" templates generated from twig JS like that :

var element = $(Twig.render(someTemplate,...));

Generated outputs may likely have some spaces (or tabs or new line) before / after html content.
Since jquery 1.9, parsing HTML with trailing spaces (space / tab / new line) fails with an error (unrecognized expression), cf http://stage.jquery.com/upgrade-guide/1.9/#jquery-htmlstring-versus-jquery-selectorstring.

Hereโ€™s a small jsfiddle to illustrate the problem : http://jsfiddle.net/dHR3n/1/

So what do you think to trim the output in the render method so jQuery wonโ€™t complain ?

IncludeCompiler context variable

Hello,

This is include string:
{% include 'paging.html' %}

compiled into
list.html.prototype.render_ = function(sb, context, blocks) {
// line 1
(new paging.html(this.env_)).render_(sb, $context);
...

And $context is not exist, I think there is a type in IncludeCompiler

Move compiled template constructor into Twig internal templates collection

I started using twig.js today so, before continue reading, keep in mind that my knowledge of the whole library is poor.

I've just noticed that every template is compiled by creating a function variable (in the main scope) with the name of the template.
Suppose I have a template called hello_world.html.twig i will get something like this (without closures):

/**
 * @constructor
 * @param {twig.Environment} env
 * @extends {twig.Template}
 */
hello_world.html = function(env) {
    twig.Template.call(this, env);
};

The constructor function is in the main scope (window), so suppose now I want to create a template called try, var, function, throw, native, protected, etc... or even if i want to create a template called hello-world, I haven't try it but I'm sure I can't!

So wouldn't it be so much better to store the compiled template constructors (and methods) in a way such the following?

/**
 * @constructor
 * @param {twig.Environment} env
 * @extends {twig.Template}
 */
Twig.templates['hello_world.html'] = function(env) {
    twig.Template.call(this, env);
};

And by doing so you can render the template by calling

Twig.render('hello_world.html', data);

I mean passing the template as a string (referring to the name of the template).
This would allow avoiding collisions with reserved javascript keywords and with other functions (suppose my template is called jQuery or $).

trans filter configuration

I'm trying to use the trans filter in my twig templates but it seems that the TransFilterCompiler is not applied. The resulting generated javascript does not replace the key and produce something like this:

this.env_.filter("trans", "my_key")

I tried to use the latest versions of assetic as suggested here: http://stackoverflow.com/questions/8795819/twigjs-and-dynamic-translations

It didn't work.

Is there any specific configuration to be done to enable twig_js.filters.trans_compiler?

Extra first parameter in method call

Hi all. I have following code (simplified version):

// script.js
var foo = {
  bar: function(baz) {
    alert(baz);
  }
}
Twig.render(template, {foo: foo});
# template.html.twig
{{ foo.bar(123) }}

I expect to see 123, but I get 0! When I dump bar method arguments, I see:

Arguments { 0=0, 1=123}

When I check compiled template file, I see:

sb.append(twig.filter.escape(this.env_, twig.attr(("foo" in context ? context["foo"] : null), "bar", [0, 123], "method"), "html", null, true));

So there are one extra first variable with value 0. Why it is added? And how to avoid it?

twig.js causes error in AsseticBundle

Hi, I got configured assetic like this:

   assetic:
    debug:          %kernel.debug%
    use_controller: false
    filters:
        cssrewrite: ~

        closure:
          jar: %kernel.root_dir%/Resources/java/compiler.jar
        yui_js:
          jar: %kernel.root_dir%/Resources/java/yuicompressor.jar
        yui_css:
          jar: %kernel.root_dir%/Resources/java/yuicompressor.jar

(indents changed to make post nicer - they are not wrong in the config file).

And my template like this:

            {% javascripts "@MyAdminBundle/Resources/views/Resource/videoView.html.twig"
                filter="twig_js, ?yui_js" %}
                <script language="javascript" src="{{ asset_url }}"></script>
            {% endjavascripts %}

Which generates the following stacktrace in _dev:

 [exception] 500 | Internal Server Error | Exception
 [message] SplObjectStorage::serialize() must return a string or NULL
 [1] Exception: SplObjectStorage::serialize() must return a string or NULL
            at n/a
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 138

            at serialize()
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 0

            at Assetic\Asset\AssetCache::getCacheKey(object(Assetic\Asset\FileAsset), null, &#039;dump&#039;)
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 62

            at Assetic\Asset\AssetCache-&gt;dump()
                in /home/tarjei/myproject/vendor/bundles/Symfony/Bundle/AsseticBundle/Controller/AsseticController.php line 80

            at Symfony\Bundle\AsseticBundle\Controller\AsseticController-&gt;render(&#039;4bc396c&#039;, &#039;0&#039;)
                in  line 

            at call_user_func_array(array(object(Symfony\Bundle\AsseticBundle\Controller\AsseticController), &#039;render&#039;), array(&#039;4bc396c&#039;, &#039;0&#039;))
                in /home/tarjei/myproject/app/cache/dev/classes.php line 3905

            at Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;)
                in /home/tarjei/myproject/app/cache/dev/classes.php line 3875

            at Symfony\Component\HttpKernel\HttpKernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;, true)
                in /home/tarjei/myproject/app/cache/dev/classes.php line 4852

            at Symfony\Bundle\FrameworkBundle\HttpKernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;, true)
                in /home/tarjei/myproject/app/bootstrap.php.cache line 547

            at Symfony\Component\HttpKernel\Kernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request))
                in /home/tarjei/myproject/web/app_dev.php line 26

[2] Exception: SplObjectStorage::serialize() must return a string or NULL
at n/a
in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 138

            at serialize()
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 0

            at Assetic\Asset\AssetCache::getCacheKey(object(Assetic\Asset\FileAsset), null, &#039;dump&#039;)
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 62

            at Assetic\Asset\AssetCache-&gt;dump()
                in /home/tarjei/myproject/vendor/bundles/Symfony/Bundle/AsseticBundle/Controller/AsseticController.php line 80

            at Symfony\Bundle\AsseticBundle\Controller\AsseticController-&gt;render(&#039;4bc396c&#039;, &#039;0&#039;)
                in  line 

            at call_user_func_array(array(object(Symfony\Bundle\AsseticBundle\Controller\AsseticController), &#039;render&#039;), array(&#039;4bc396c&#039;, &#039;0&#039;))
                in /home/tarjei/myproject/app/cache/dev/classes.php line 3905

            at Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;)
                in /home/tarjei/myproject/app/cache/dev/classes.php line 3875

            at Symfony\Component\HttpKernel\HttpKernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;, true)
                in /home/tarjei/myproject/app/cache/dev/classes.php line 4852

            at Symfony\Bundle\FrameworkBundle\HttpKernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;, true)
                in /home/tarjei/myproject/app/bootstrap.php.cache line 547

            at Symfony\Component\HttpKernel\Kernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request))
                in /home/tarjei/myproject/web/app_dev.php line 26

[3] Exception: SplObjectStorage::serialize() must return a string or NULL
at n/a
in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 138

            at serialize()
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 0

            at Assetic\Asset\AssetCache::getCacheKey(object(Assetic\Asset\FileAsset), null, &#039;dump&#039;)
                in /home/tarjei/myproject/vendor/assetic/src/Assetic/Asset/AssetCache.php line 62

            at Assetic\Asset\AssetCache-&gt;dump()
                in /home/tarjei/myproject/vendor/bundles/Symfony/Bundle/AsseticBundle/Controller/AsseticController.php line 80

            at Symfony\Bundle\AsseticBundle\Controller\AsseticController-&gt;render(&#039;4bc396c&#039;, &#039;0&#039;)
                in  line 

            at call_user_func_array(array(object(Symfony\Bundle\AsseticBundle\Controller\AsseticController), &#039;render&#039;), array(&#039;4bc396c&#039;, &#039;0&#039;))
                in /home/tarjei/myproject/app/cache/dev/classes.php line 3905

            at Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;)
                in /home/tarjei/myproject/app/cache/dev/classes.php line 3875

            at Symfony\Component\HttpKernel\HttpKernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;, true)
                in /home/tarjei/myproject/app/cache/dev/classes.php line 4852

            at Symfony\Bundle\FrameworkBundle\HttpKernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request), &#039;1&#039;, true)
                in /home/tarjei/myproject/app/bootstrap.php.cache line 547

            at Symfony\Component\HttpKernel\Kernel-&gt;handle(object(Symfony\Component\HttpFoundation\Request))
                in /home/tarjei/myproject/web/app_dev.php line 26

I think this is due to Twig.js keeping a reference to a simplexml object somewhere - but I have no idea where. I tried to find it but got lost.

Minifyiing JS using Google Closure Compiler fails because of missing goog.provide

In our build system, weโ€™re concatenating all JS libraries, twig templates and our source code in a single file, then using Google Closure Compiler to build it.

When building, weโ€™ve got some errors :

main.js:73862: ERROR - required "twig" namespace never provided
goog.require('twig');
^
main.js:73863: ERROR - required "twig.filter" namespace never provided
goog.require('twig.filter');

It seems that Twig.js generates some goog.require annotations in generated templates :

goog.require('twig');
goog.require('twig.filter');

but it seems that some goog.provide are never provided which causes thoses errors.

Adding thoses goog.provide at the head of twig.js file fixes the problem :

goog.provide("twig")
goog.provide("twig.filter")

So why not add them (eventually using an if syntax if goog is not loaded) ?

For-loop variable scope bug makes some variables inaccessible after loops

Let's cut straight to some example code.

{% set minimum = 9999 %}
{% set list = [1, 2, 3] %}
{% for element in list %}
  {% if element < minimum %}
    {% set minimum = element %}
  {% endif %}
{% endfor %}
{{ minimum }}

Regular PHP-compiled templates output 1. Twig.js-compiled templates output nothing because of a TypeError:

 tmp_minimum is not defined

The cause of the issue becomes clear if we inspect the compiled Javascript.

example.prototype.render_ = function(sb, context, blocks) {
    // line 1
    context["minimum"] = 9999;
    // line 2
    context["list"] = [1, 2, 3];
    // line 3
    var tmp_list = ("list" in context) ? context["list"] : null;
    context['_parent'] = context;
    var seq = tmp_list;
    twig.forEach(seq, function(v, k) {
        context["_key"] = k;
        context["element"] = v;
        // line 4
        sb.append("  ");
        var tmp_element = ("element" in context) ? context["element"] : null;
        var tmp_minimum = ("minimum" in context) ? context["minimum"] : null;
        if (((tmp_element) < (tmp_minimum))) {
            // line 5
            sb.append("    ");
            context["minimum"] = tmp_element;
            // line 6
            sb.append("  ");
        }
    }, this);
    // line 8
    sb.append(twig.filter.escape(this.env_, tmp_minimum, "html", null, true));
    sb.append("\n");
};

The tmp_minimum is declared inside the function passed to twig.forEach, so when line 8 tries to sb.append it from outside that scope, it's undefined. The correct value is available in context["minimum"] though.

Any thoughts about what the best way of fixing this might be?

Using replace filter

Hello,

I see that twig.filter.replace can recieve 2 parameters: string and map,
I cannot figure out which format I need to pass the map.

Example:
{{ 'Number of reviews: ' | replace({'': '20'}) }}
Compiled into:
sb.append(twig.filter.replace("Number of reviews: ", {0: "", 1: "20"}));

And I expect that it will be compiled into:
{"": "20"}

I noticed that Twig_Node_Expression_Array use getKeyValuePairs in order to build hash array.

Thanks

Support for e/escape filter?

I know that twig.js has support for the escape filter - is it easy enough to also support the e shorthand filter? Otherwise I guess all our templates must use escape.

Failing Test with any version of Twig

Hey guys!

I'm trying to run the tests, but I can't seem to get them to pass - no matter where I move my Twig library head to. The failure is:

1) TwigJs\Tests\TemplateGenerationTest::testGenerate with data set #2 ('/Users/ryan/Sites/clients/learnernation.com/vendor/twig.js/tests/TwigJs/Tests/Fixture/templates/simple_standalone.twig', '/Users/ryan/Sites/clients/learnernation.com/vendor/twig.js/tests/TwigJs/Tests/Fixture/generated/simple_standalone.js')
Twig_Error_Runtime: An exception has been thrown during the compilation of a template ("There is no compiler for node type "Twig_Node_Expression_Test_Defined".") in "/Users/ryan/Sites/clients/learnernation.com/vendor/twig.js/tests/TwigJs/Tests/Fixture/templates/simple_standalone.twig".

/Users/ryan/Sites/clients/learnernation.com/vendor/twig/lib/Twig/Environment.php:528
/Users/ryan/Sites/clients/learnernation.com/vendor/twig.js/tests/TwigJs/Tests/TemplateGenerationTest.php:25

So, are the tests passing for everyone else? It seems that the TestCompiler isn't visited, so the JsCompiler::getTestCompiler is never called (and the test node is used like a normal node).

I was running the tests after one of my templates failed when using a for loop:

[RuntimeException]                                        
  There is no compiler for node type "Twig_Node_ForLoop". 

Are the tests passing for everyone else? Does the for loop work or does it need some more work?

Thanks! I really like the library - opens a lot of exciting doors.

Variables and if statements

Hey

When I use an if statement and inside it I want to use a variable, TwigJS will compile it while it won't create the variable in the right place, for example:

Twig

 <p> 
 {% if (abc.rack > 1) %} 
 <span>{{ myVariable }}</span> 
{% endif %} 
 <div> - {{ myVariable }} - </div> 
 </p>

JS

 if (((twig.attr(tmp, "rack") > 1)) {
 .
 .
 .
 var myVariable = .....
 }
 // line 166
 sb.append("\t\n                   \t<div>-");
 // line 167
 sb.append(twig.attr(myVariable, "...", ...., "array"));
 // line 168
 sb.append("\t\n                   \t -</div>");

Sorry for deleting some data but it was not relevant. Anyway the idea is that if twig.attr(tmp,"rack") is not greater then 1 myVaribale is not defined although it should be defined before the if statement

Current solution is:

Twig

 <p> 
{% set myVariable = myVariable %} 
 {% if (abc.rack > 1) %} 
 <span>{{ myVariable }}</span> 
{% endif %} 
 <div> - {{ myVariable }} - </div> 
 </p>

JS results:

var myVariable = ... 
 if (((twig.attr(tmp, "rack") > 1)) {
 .
 .
 .
 // USE myVariable here because it already defined..  
 }
 // line 166
 sb.append("\t\n                   \t<div>-");
 // line 167
 sb.append(twig.attr(myVariable, "...", ...., "array"));
 // line 168
 sb.append("\t\n                   \t -</div>");

Thanks

Ensure that an attempt is not made to access attributes of non-objects

In my testing of #17, I am finding an issue with twig.attr when it is passed undefined, null, empty object, etc. as the first obj argument, e.g. in welcome.js:

// line 19
sb.append(twig.filter.escape(this.env_, twig.attr(context["__internal_2547295a140ac5330ced4d5f977bde50_1"], "link", ["\/login", "Please login.", "Login"], "method"), "html", null, true));

In this case context == {} because we passed an empty object to renderBlock as in #17 and so typeof context["__internal_2547295a140ac5330ced4d5f977bde50_1"] === "undefined" because that key is non-existent. However, in twig.attr, an attempt is then made to do this:

if (attr in obj) {
    // ...
}

Which is of course illegal because obj is undefined. So the question here is what behaviour do we expect? I've not made a PR or added tests for this case because I am not sure what we want here. The safest and quietest option seems to be to return null at the start of twig.attr if !obj.

Perhaps it would be better if the compiler added a check to the first param of twig.attr such as this:

twig.attr(typeof context["the_key"] !== "undefined" ? context["the_key"] : {}, "the_attr")

And if it so happens that context["the_key"] is a string or an integer or something else, then that is a user-land error.

Default filter does not work properly in macros compiled

Hi guys!

Probably, this issue is partially related to this one: #40

I am using macro like this in my project:

{% twig_js name="TwigRender.templates.template" %}
{% if doOrNot|default(false) %}
    {{ 'do' }}
{% else %}
    {{ 'not' }}
{% endif %}

twig.js compiles it in javascript code like this:

// line 2
TwigRender.templates.macros.prototype.gettest = function(opt_doOrNot) {
    var context = twig.extend({}, this.env_.getGlobals());

    var sb = new twig.StringBuffer;
    // line 3
    sb.append("    ");
    if (((("doOrNot" in context)) ? (twig.filter.def(opt_doOrNot, true)) : (true))) {
        // line 4
        sb.append("        ");
        sb.append("do");
        sb.append("\n    ");
    } else {
        // line 6
        sb.append("        ");
        sb.append("not");
        sb.append("\n    ");
    }

    return new twig.Markup(sb.toString());
};

Next, when i am trying to call this macro i always get do as result because context does not contain doOrNot variable (so we have in if condition always true value). More generaly, default filter for any variable always results to default value. That's the problem.

This issue appears only in macro compiling case. If we will try to execute simple template like this:

{% twig_js name="TwigRender.templates.template" %}
{% if doOrNot|default(true) %}
    {{ 'do' }}
{% else %}
    {{ 'not' }}
{% endif %}

the result will be correct (so it depends on doOrNot value).
Internally, this template is compiled into the function that uses context variable filled with scope variables:

/**
 * @inheritDoc
 */
TwigRender.templates.template.prototype.render_ = function(sb, context, blocks) {
    // line 2
    if (((("doOrNot" in context)) ? (twig.filter.def("doOrNot" in context ? context["doOrNot"] : null, true)) : (true))) {
        // line 3
        sb.append("    ");
        sb.append("do");
        sb.append("\n");
    } else {
        // line 5
        sb.append("    ");
        sb.append("not");
        sb.append("\n");
    }
};

As i know, when twig compiles simple templates into php code, the resulting function (i.e doDisplay) has $context variable already filled with all template properties. But when twig renders macro, the result function php function has no $context variable filled with the scope variables - this function internally fills it itself with necessary variables and then uses $context.

@schmittjoh, as i can see, you are using in twig.js a little bit another approach when you are deal with macros - you do not fill context with scope variables (see #40 ), but hold special localVarMap in JsCompiler instead and than use it when compile Name expressions. Nevertheless, for example default filter constructions are compiled to conditional expressions with first term, that looks like this:
(varName in context)
thanks to NameCompiler.

May be it is pertinently to fill context with scope variables in macros?
Or we should use localVarMap in this case and become compiled

 if (((opt_doOrNot) ? (twig.filter.def(opt_doOrNot, true)) : (true)))

instead of

 if (((("doOrNot" in context)) ? (twig.filter.def(opt_doOrNot, true)) : (true)))

?

Can we port functions like form_errors, form_row, form_widget?

Is it easy and possible for me to provide a PR to enable use of the Symfony\Bridge\Twig\Extension\FormExtension functions:

<?php
'form_enctype'             => new \Twig_Function_Method($this, 'renderEnctype', array('is_safe' => array('html'))),
'form_widget'              => new \Twig_Function_Method($this, 'renderWidget', array('is_safe' => array('html'))),
'form_errors'              => new \Twig_Function_Method($this, 'renderErrors', array('is_safe' => array('html'))),
'form_label'               => new \Twig_Function_Method($this, 'renderLabel', array('is_safe' => array('html'))),
'form_row'                 => new \Twig_Function_Method($this, 'renderRow', array('is_safe' => array('html'))),
'form_rest'                => new \Twig_Function_Method($this, 'renderRest', array('is_safe' => array('html'))),
'csrf_token'               => new \Twig_Function_Method($this, 'getCsrfToken'),
'_form_is_choice_group'    => new \Twig_Function_Method($this, 'isChoiceGroup', array('is_safe' => array('html'))),
'_form_is_choice_selected' => new \Twig_Function_Method($this, 'isChoiceSelected', array('is_safe' => array('html'))),

In twig.js? I could be on the wrong workflow with what I am doing, but basically I have overridden some blocks for my forms (this is for a collection type, so think of many rows in the form that you can add and remove), and I'd like to be able to render the row in JavaScript in order that I can dynamically add rows using twig.js. My overridden template for my type mytype (e.g. form.html.twig) is similar to this:

{%- block acme_core_mytype_row -%}
    <tr>
        <td>
            {{- form_errors(form) -}}
            {{- form_row(form.key) -}}
            {{- form_row(form.value) -}}
        </td>
        <td class="tools">
            {{- block('tools') -}}
        </td>
    </tr>
{%- endblock -%}

With the intent that I can call this from JS:

var myTemplate = new acme.core.mytype.form.html(Twig);

$("table.mytable tbody").append(myTemplate.renderBlock("acme_core_mytype_row", {
    form: {
        // all the properties of the form that I want to be in the new row
    }
}));

What do you think?

Tag?

can you plz tag this project? Thank you!

include when template name is a variable

I need to include templates based on a variable. So for example I have a list and each list item has a "style" value, so I'd call the template with the name "list_item_" + style to render it. Works in PHP Twig, but fails in TwigJs as it seems to expect a constant template name with include in it's IncludeCompiler.php

{% set item_twig = 't_list_item_' ~ item.kind ~ '.twig' %}
{% set item_vars = {'item': item } %}
{% include item_twig with item_vars only %}

Issue regarding Backbone and twig.js

Suppose that a backbone model MyModel has "public" methods such as :

getFoo() { return 'foo' );

Now suppose we hydrate n MyModel in a Backbone Collection MyCollection.
In a backbone View it will looks like this :

render: function() {
   this.$el.html ( Twig.render( tpl.name, { collection : this.MyCollection }) ) ;
}

Unfortunately,

{% for model in collection %}
  {{ model.getFoo() }}  
  {{ model.get( 'attr' ) }}
{% endfor %}

doesn't work and return void.
the only thing working right now to access attributes of a Backbone Model is to Jsonize the collection itself :

//this.MyCollection.toJSON() 
this.$el.html ( Twig.render( tpl.name, { collection : this.MyCollection.toJSON()  }) ) ;

and access attrs thru :

{{ model.attr }}

It's restrictive... As an example the _.template() from underscore allow to access the whole methods

Is a normal behavior of twig.js ?

Best,

Operator precedence bug causing incorrect evaluation of polarity

After porting a bunch of integration tests over from Twig, the following test failure exposes this bug.

--TEST--
"even" test
--TEMPLATE--
{{ 1 is even ? 'ko' : 'ok' }}
{{ 2 is even ? 'ok' : 'ko' }}
{{ 1 is not even ? 'ok' : 'ko' }}
{{ 2 is not even ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok
ok
ok

Instead of four "ok" strings in a row, the third line contains "ko", meaning that 1 is not even is incorrectly evaluating to false. This seems to be the result of an operator precedence issue, because the compiled JS for that check looks like this:

sb.append((((!0 === 1 % 2)) ? ("ok") : ("ko")));

As usual, throwing a few extra parens into the mix will probably fix this.

Compiler does not allow dynamic template inheritance

Example template:

{# 'AcmeCoreBundle:Users:user.html.twig' #}

{# has some blocks #}
{# 'AcmeCoreBundle:Users:specialUser.html.twig' #}

{% extends AcmeCoreBundle:Users:user.html.twig' %}

{# overrides some blocks #}
{# 'AcmeCoreBundle:Users:userSearchResult.html.twig' #}
{# so by default this extends the "user" template #}
{% set parent_template = 'AcmeCoreBundle:Users:user.html.twig' %}

{% extends parent_template %}

{# overrides some blocks in "user.html.twig" #}
{# 'AcmeCoreBundle:Users:specialUserSearchResult.html.twig' #}
{# but this extends the "specialUser" template because this search results is showing a special user #}
{% set parent_template = 'AcmeCoreBundle:Users:specialUser.html.twig' %}

{% extends 'AcmeCoreBundle:Users:userSearchResult.html.twig'  %}

{# overrides even more blocks in "user.html.twig" as well as blocks in "specialUser.html.twig" #}

In this example, the TwigJS compiler will fail essentially because it tries to find a template which does not exist because it doesn't first compile the "set" nodes which are in the "body".

Additionally, the getParent compilation starts compiling with a return statement, when really it should compile the "body" first, then return the template.

Comparing this to the equivalent Twig compilation method, it seems we need some sort of template lookup. Here is the twig method:

<?php

    protected function compileGetParent(Twig_Compiler $compiler)
    {
        if (null === $this->getNode('parent')) {
            return;
        }

        $compiler
            ->write("protected function doGetParent(array \$context)\n", "{\n")
            ->indent()
            ->write("return ")
        ;

        if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
            $compiler->subcompile($this->getNode('parent'));
        } else {
            $compiler
                ->raw("\$this->env->resolveTemplate(")
                ->subcompile($this->getNode('parent'))
                ->raw(")")
            ;
        }

        $compiler
            ->raw(";\n")
            ->outdent()
            ->write("}\n\n")
        ;
    }

And the PHP template that gets compiled is roughly this:

<?php

    protected function doGetParent(array $context)
    {
        return $this->env->resolveTemplate((("AcmeCoreBundle:Users:" . $this->getContext($context, "parent_template_name")) . ".html.twig"));
    }

What do you think?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.