twig.js icon indicating copy to clipboard operation
twig.js copied to clipboard

In the PHP version, you can include templates from multiple locations

Open nebulousGirl opened this issue 9 years ago • 17 comments

It would be nice to be able to pass an array of locations for the path option.

nebulousGirl avatar Mar 18 '15 13:03 nebulousGirl

+1

brunowego avatar Mar 28 '15 01:03 brunowego

+1

mikelohmann avatar May 20 '15 09:05 mikelohmann

+1

micahgodbolt avatar Jun 11 '15 23:06 micahgodbolt

+1

marsjaninzmarsa avatar Aug 12 '15 19:08 marsjaninzmarsa

+1

fi5u avatar Sep 16 '15 07:09 fi5u

+1

raynor85 avatar Dec 09 '15 13:12 raynor85

if I understood you are talking about namespaces, it is now working at the last version: https://github.com/justjohn/twig.js/blob/master/CHANGELOG.md

and I added a example on how to implement on the wiki

rafaellyra avatar Dec 24 '15 13:12 rafaellyra

Namespaces are a great addition, but I am talking about cascading locations.

In php, you can specify an array of folders where to look for templates.

Twig will loop through the locations until it finds the template.

If I define my path as [folder-a, folder-b], Twig looks for template.twig first in folder-a and if it doesn't exist, it will cascade to look if it exists in folder-b.

nebulousGirl avatar Dec 24 '15 14:12 nebulousGirl

in Express 4 we'd hope to set it up like

app.set('views',  ['path/to/files/', 'path/to/other/files'] ;

But when you do this, Twigjs doesn't see this as an array of 2 items. It tries to look in:

'path/to/files/,path/to/other/files'

micahgodbolt avatar Mar 08 '16 00:03 micahgodbolt

Appears that the relativePath function assumes template.base to be a string, and not an array (which it is when multiple paths are defined in express.

I took a little stab at a fix, but it might require more of a rewrite than I'm able to do in order to support array paths.

micahgodbolt avatar Mar 08 '16 23:03 micahgodbolt

Namespaces are a great addition, but I am talking about cascading locations.

In php, you can specify an array of folders where to look for templates.

Twig will loop through the locations until it finds the template.

Actually, they are the same thing. In the PHP Twig, there is a default "main" namespace. When you add paths to search for templates, you are adding paths to the main namespace. Here's the relevant docs: http://twig.sensiolabs.org/doc/api.html#built-in-loaders

There are 2 incompatibilities with Twig.js here.

  1. Twig.js does not have a main namespace.
  2. Namespaces can only have a single path. But namespaces should have an ordered array of paths.

Right now we are required to pass namespaces every time we call Twig.twig() which is kind of a pain, but manageable. However, if Twig.js adds a main namespace, it is going to have to manage a global registry for namespaces which means a user should be able to register their custom namespace once with Twig.js and not have to provide it every time it calls Twig.twig().

JohnAlbin avatar May 06 '16 02:05 JohnAlbin

Here's more Twig docs. http://symfony.com/doc/current/cookbook/templating/namespaced_paths.html#multiple-paths-per-namespace

With Symfony, Twig.js' namespaces are configured by a YML file:

# app/config/config.yml
twig:
    # ...
    paths:
        "%kernel.root_dir%/../vendor/acme/themes/theme1": theme
        "%kernel.root_dir%/../vendor/acme/themes/theme2": theme
        "%kernel.root_dir%/../vendor/acme/themes/common": theme

In Twig without Symfony, you have to add namespace from within a loader. And then use that loader when creating the global Twig environment

$twig = new Twig_Environment($loader);

Or inserting it into the global twig with:

$twig->addLoader($loader);

Many Symfony-based apps use "services" to configure a Twig_Loader_Chain that includes several different loader types. So if you want custom namespaces, you register your custom loader with the services and it pushes it onto the loader chain stack. When a template is requested, Twig goes through each loader in the Twig_Loader_Chain stack and tries to find a match. Since your @mypath/template.twig will only match a namespace in your custom loader, your custom loader will be used to resolve any custom namespaces.

I just did this with a Drupal module, which is why I'm familiar with this API. http://drupal.org/project/components

TLDR;

Yes, its a pain in the ass to add namespaces with multiple paths in Twig for PHP. So how do we add it to Twig.js?

Twig.js has an equivalent to Twig_Loader_Chain in that Twig.exports.twig() goes through a long if/then/else checking for which loader to use.

I'd rather not have to implement a custom loader that was essentially a fork of the fs loader just so I can add custom namespaces.

Ideally, we would be able to configure Twig.js' fs or ajax loader by adding paths to the default namespace or adding custom namespaces. PHP Twig loaders have these methods for adding paths and namespaces: setPaths(), addPath(), and prependPath(). e.g. $loader->addPath('/etc'); adds a path to the main namespace and $loader->addPath('/etc', 'mynamespace'); adds that path to a custom namespace. If you want multiple paths in your namespace, you call addPath() or prependPath() multiple times. FYI, setPaths() only adds paths to the main namespace, you can't specify custom namespaces. https://github.com/twigphp/Twig/blob/f0a4fa678465491947554f6687c5fca5e482f8ec/lib/Twig/Loader/Filesystem.php

However, in Twig.js, we aren't resolving namespaces inside loaders, we are doing it in twig.path.js, so our separation is already a little muddy.

The twig.path.js module is in charge of replacing namespaces with paths for fs and it completely ignores the concept of namespaces for the ajax loader. Wouldn't ajax users want a nice conversion of @mysite/template.twig into http://mysite/myfolder/template.twig? To be clear, I don't think multiple URLs would make sense, but a single path per namespace would be mighty handy.

Still TLDR;

So we have 2 options:

  1. Export Twig.Templates.loaders, so users can use Twig.loaders.fs.addPath(), Twig.loaders.fs.setPaths(), Twig.loaders.ajax.addPath(), etc. That means Twig.path.parsePath() would just be calling the loader's findTemplate() method to get the correct path.
  2. Add methods to Twig.path that resolves namespaces for any loader. That means you can't have a single namespace that resolves to different paths; e.g. "myStuff" resolving to a URL for ajax and to a file system path for fs. Users can use Twig.path.setPaths(), Twig.path.addPath(), and Twig.path.prependPath().

Option 2 is shorter for the user (and easier to implement), but option 1 is closer to what Twig PHP does.

I'm happy to do the PR for option 1.

Sorry for the long comment. Thoughts?

JohnAlbin avatar May 06 '16 05:05 JohnAlbin

I'll counter your long comment with a short one :)

We go for whatever solution gets us closer to parity with Twig PHP. Adding sugar to said solution could be done in an additional module.

dave-irvine avatar May 06 '16 07:05 dave-irvine

I'll see if I can get a PR for this done this week. @micahgodbolt and I are both at the same conference this week.

JohnAlbin avatar May 11 '16 10:05 JohnAlbin

Any movement on this? It's been months and there doesn't appear to be a solution or an open PR

Snugug avatar Dec 22 '16 15:12 Snugug

Bump :wave: Would love this too!

EvanLovely avatar Jun 21 '17 06:06 EvanLovely

any updates ?

erikbaan avatar Sep 17 '19 11:09 erikbaan