Embedded controller views with assets - templates

I am currently working on a symfony2 application and am using embedded controllers. My embedded controllers are like widgets which should encapsulate its own set of functionality and can be embedded anywhere and still be expected to function.
I have a controller called users online. The view it generates is simple, just a list of online users. But, I would like to add some javascript to that view so that I can use ajax to retrieve information for a user that's clicked on.
The controller basically returns a view:
return $this->render('AppBundle:Users:usersOnline.html.twig', array('somedata' => $data);
Here's the view for that controller:
{% extends partial.html.twig" %}
{% block content %}
<ul>
<li>User 1</li> (this would all be generated using 'somedata')
<li>User 2</li>
....
<ul>
{% endblock content %}
{% block scripts %}
..some javascript for interacting with this widget
{% endblock %}
This is the partial that is extended from:
{% block content %}
{% endblock content %}
{% block scripts %}{% endblock %}
Here's the main page that embeds the controller:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block content %}
..some markup here
<div id="usersonline">
{% render "AppBundle:Users:usersOnline" with {'max': 4} %}
</div>
{% endblock %}
{% block scripts %}
..some javascript
{% endblock %}
This is the base that it extends:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock %} - App</title>
...Some stylesheets
</head>
<body>
{% block content %}{% endblock %}
<script src="http://yui.yahooapis.com/3.5.0pr2/build/yui/yui-min.js"></script>
{% block scripts %}{% endblock %}
</body>
</html>
The problem I am facing now is including javascripts from the embedded controller. In my case, the view extends partial and is fully rendered as 1 unit before it is inserted into the main page. In this case, I can only put my javascript in the content block, which means I will have <script> tags within <div> tags. I would also prefer to have scripts at the end of the body for user interface performance.
How should I structure my templates (or is it even possible) so that I can render the appropriate pieces from an embedded controller's view into appropriate blocks in the template that embeds the controller? In my current template the YUI library would be loaded after the embedded controller's rendered HTML markup, so accessing using the YUI library within the embedded controller would be impossible.

I have written an extension which is essentially a token parser to deal with this. The documentation for the token parser is very sparse and the code is also mostly uncommented, so it took a bit of fiddling around to get working.
I have structured the parser so that at the beginning, you would declare something (similiar to {% use %}):
{% myparser "AppBundle:Users:usersOnline" with {'max': 4} as xyz %}
Then blocks like xyzcontent would be avaliable and then you just render them in the appropriate places using {{ block('xyzcontent') }}. It works quite well. However the following does not work:
{% set max = 4 %}
{% myparser "AppBundle:Users:usersOnline" with {'max': max} as xyz %}
I could not find a way to evaluate an expression and get its value directly within the token parser. For the most part it works fine, except for this problem.
If someone has some idea as to how to get the value of a variable within the token parser, it would be great :)

After one day of looking for a satisfying solution, I finally choose for the following approach:
Render a controller for your widget's functionality (like you are already doing);
Render templates to embed your widget's assets (controllers are not needed and will waste your performance). Ignore include if the template doesn't exist.
Use a consistent naming convention to make the widget functionality more dynamic.
Example for one widget:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block content %}
..some markup here
<div id="usersonline">
{{ render(controller("AppBundle:Users:usersOnline", {'max': 4})) }}
</div>
{% endblock %}
{% block scripts %}
{{ include("AppBundle:Users:usersOnline_js.html.twig" ignore missing }}
{% endblock %}
{% block stylesheets %}
{{ include("AppBundle:Users:usersOnline_css.html.twig" ignore missing }}
{% endblock %}
(As you can see, I use the Symfony 2.2 way of including and embedding)
Or, a more dynamic way of widget rendering:
{% "base.html.twig" %}
{% block title %}Main{% endblock %}
{% block widgets %}
..some markup here
{% for widget in widgets %}
{{ render(controller(widget.controller ~ ':widget', widget.options)) }}
{% endfor %}
{% endblock %}
{% block scripts %}
{% for widget in widgets %}
{{ include(widget.controller ~ ':widget_js.html.twig' ignore missing }}
{% endfor %}
{% endblock %}
{% block stylesheets %}
{% for widget in widgets %}
{{ include(widget.controller ~ ':widget_css.html.twig' ignore missing }}
{% endfor %}
{% endblock %}
My conclusion
Rendering multiple controllers in one page, while "merging" it's templates blocks, cannot be achieved out of the box in Symfony2. Symfony2 has always been focusing on one controller for each request, which explains why multiple controllers outputs are separated and hard to combine.
I think the Symfony CMF has (or will have) a proper solution for this, because this whole widget topic is more applied in dynamic content managed websites.
Anyway, I think my way of combining embedded controllers and included templates is the best approach for Symfony 2 so far.
The alternatives, in my opinion, do have to many disadvantages:
Using multiple controllers: too heavy and not needed because assets won't need any controller.
Loading the widgets assets directly in the page: hard to maintain when changing widgets / assets.

Related

Purpose and use of Wagtail/Django template tags of the form {% block body_class %}template-homepage{% endblock %}

In the Wagtail docs (Your first Wagtail site) we are told to create a homepage.html template as follows:
{% extends "base.html" %}
{% load wagtailcore_tags %}
{% block body_class %}template-homepage{% endblock %}
{% block content %}
{{ page.body|richtext }}
{% endblock %}
In the above code, what is that tag {% block body_class %}template-homepage{% endblock %} supposed to be doing? I can't find any mention of it in the text of the tutorial.
The tutorial goes on to instruct us to create two more templates, blog_index_page and blog_page templates, both of which are to contain {% block body_class %} tags. Again, I can find no mention at all of those lines in the docs, let alone an explanation of how they might be used/modified.
A search for "body_class" in the docs finds only the three code blocks just mentioned, and one other mention: in the version 1.2 release notes, under 'What's new — Minor features' the notation: "Simplified body_class in default homepage template."
Any insight as to what I'm supposed to do with these tags? What special meaning or function, if any, does the text enclosed within the tags in those tutorial templates (for example, "template-homepage" in the code above)?
In base.html you have a part that looks like <body class="{% block body_class %}{% endblock %}">. What you add into that block in your templates will be rendered into that section.
For example, lets say on your design your <body> contains a bottom margin, however on a particular template you don't want the margin there and create a css class .remove-margin { margin-bottom: 0 };
You can then add {% block body_class %}remove-margin{% endblock %} into your template and it will apply the class to the <body> tag without you having to modify the base.html template for that one use case.
You are also able to create your own ones. In base.html add a custom block such as {% block my_custom_block %}{% endblock %} and then in your template include some text or html inside that my_custom_block and it will be rendered wherever you've place that part in the base template.
Just to help clarify the correct answers above with a picture as I was also wondering what that block tag was doing, here is the rendered html. As per answer above, this is standard Django.
In the base.html you have this line:
<body class="{% block body_class %}{% endblock %}">
The substitution happens in the home_page.html template:
{% block body_class %}template-homepage{% endblock %}
And it renders as per below screenshot:
<body class="template-homepage">

Jinja2 templating with components? blocks? templates?

A little question to jinja2 templating:
I want to create a reusable template to include and then overwrite blocks. Macros do not let me write junks of HTML easily as parameters do they? Say I want to reuse an include several times and am using BIG junks of HTML in blocks that I want to dynamically assign
how would I do it?
certainly not with macros I guess, or am I wrong?
{% render_foo('bar',2) %} is fine
{% render_foo('<table><tr><th>something</th><th>somethingelse</th></tr><tbody><tr>....etc') %} is not fine any more is it
"what do you really want to do?"
yes, what I told you, I have a way I create containers for my data. The container is ALWAYS the same. The content is completely different on each usage. Once a table. Once a bootstrap component. Once a form.
The surrounding elements are always the same
to reproduce the simple error this is what I did:
{% include 'full_section.html' %}
{% block fullsection %} <table><tr><th>something</th><th>somethingelse</th></tr><tbody><tr>....etc{% endblock %}
{% include 'full_section.html' %}
{% block fullsection %} <form>//some cool LONG big form </form>{% endblock %}
full_section.html contents just for completeness, it is a lot more complex in reality
<div class="my_cool_full_section">
{% block full_section %}{% endblock %}
</div>
TemplateAssertionError: block 'fullsection' defined twice
I found the answer well hidden in the jinja2 docs
http://jinja.pocoo.org/docs/2.9/templates/#block-assignments
so you use a macro and a block assignment e.g. like this:
{% set section_content %}
<table><tr><td>etc</td> <td>etc</td> <td>etc</td></tr></table>
<table><tr><td>etc</td> <td>etc</td> <td>etc</td></tr></table>
<table><tr><td>etc</td> <td>etc</td> <td>etc</td></tr></table>
{% endset %}
{{ render_full_size_section(section_content) }}
{% set section_content %}
aaaaaaaaaaa
{% endset %}
{{ render_full_size_section(section_content) }}
wonder what they were doing pre 2.8... dark dark middle age
then in the macro:
{% macro render_full_size_section(content) %}
<div class="mycoolsection">
{{ content | safe }}
</div>
{% endmacro %}

Django-CMS show_placeholder not working as expected

I'm working on a site where the footer content is shared across all pages. What is the best way to do in Django-CMS?
I tried using show_placeholder tag, but it somehow didn't work. A little more details on what I did:
First, I have a {% placeholder footer_info %} in base.html. Then I add a page called "Home" (template homepage.html) in django admin and put some text under footer_info as a Text plugin. As the accepted answer in this question suggested (http://stackoverflow.com/questions/3616745/how-to-render-django-cms-plugin-in-every-page),
I add
{% placeholder footer_info or %}
{% show_placeholder footer_info "Home" %}
{% endplaceholder %}
In a template called services.html which I used as the template for page Services.
However, the content in home page is not showing up in services page. I also tried adding an id home_cms_page to home page in the Advanced option area, so that I can reference it in services.html like this:
{% placeholder footer_info or %}
{% show_placeholder footer_info "home_cms_page" %}
{% endplaceholder %}
But the content is still not showing up.
Could anyone tell me what I am doing wrong? And is this the best way of getting some content from a page across all other pages (and I have to add show_placeholder in every other page)?
Thank you
EDIT:
It is not a multilingual site. I commented out 'cms.middleware.multilingual.MultilingualURLMiddleware', because the only language I use on the site is English.
I have this in my base.html:
{% load cms_tags sekizai_tags %}
<!-- all the rest of the HTML markups -->
<div class="span4">
{% placeholder footer_info %}
</div>
Then I added a page in the admin called "Home" with a Text plugin and an id of "home_cms_page".
The following is in my services.html:
{% extends "base.html" %}
{% load cms_tags %}
{% block base_content %}
{% placeholder services_info %}
{% endblock base_content %}
{% block page_content %}
Home page
{% endblock page_content %}
{% placeholder "footer_info" or %}
{% show_placeholder "footer_info" "home_cms_page" %}
{% endplaceholder %}
Read the documentation:
If you know the exact page you are referring to, it is a good idea to
use a reverse_id (a string used to uniquely name a page) rather than a
hard-coded numeric ID in your template. For example, you might have a
help page that you want to link to or display parts of on all pages.
To do this, you would first open the help page in the admin interface
and enter an ID (such as help) under the ‘Advanced’ tab of the form.
Then you could use that reverse_id with the appropriate templatetags:
{% show_placeholder "right-column" "help" %}
I added "index" in the advanced options of the index page, and added {% show_placeholder "banner" "index" %} in the base template. It all works.

Django: How to include a block site wide?

Apologies, this is pretty basic. I have abstracted my static html navigation bar to a block so that it can be dynamically rendered from a model. I have created a new block marker in base.html with the following syntax
{% block navigation %}{% endblock %}
How can I ensure this is rendered on every page? Do I need to create some sort of middle layer for this? Everything I have done so far has simply used the primary block.
EDIT Sunday, 14 August 2011 11:25 AM
I didn't explain this very well. The content of navigation block is
{% extends 'base.html' %}
{% block navigation %}
<nav>
<ul>
{% for item in items %}
<li>{{ item.name }}</li>
{% endfor %}
</ul>
</nav>
{% endblock %}
I want to render this on every page without having to go through child templates of base.html individually and add it to them, if that is possible.
You have got static navigation bar, so you can just write code in your base.html page and then use it with {% extends 'base.html' %} tag on every new page. Your markup will be in every page. Also, if you use only extends tag in your child page and then render it - you will see base.html without any edition.
If you have got code, which need to appear on few pages, but not on everyone - create 'includes' directory, save code there and extend your base template with {% include %} tag. It won't avoid repating, but make your code shorter.
And last thing you'll need in future, maybe with dynamic code - caching. With {% cache %} tag you can cache block for some time.
Your block is not doing anything here, but if you want something to appear in all the templates, you have to define it in a base template (base.html) and make the others extend from it:
base.html
Yor menu and the stuff you want to appear everywhere
{% block content %}{% endblock %}
Another template
{% extends "base.html" %}
{% block content %}
The actual content of the page
{% endblock %}
{% block FOO %}{% endblock %} reserves a space to be overwritten in sub templates.
{% include "foo.html" %} pulls content from another file into the current file.
To get {% block navigation %}{% endblock %} to display on every page, you need to add content
{% block navigation %}SHOW ME{% endblock %}

Override inner template block in Django

I have the following structure of templates (simplified for clarity):
base1.html:
<html>
<head>{% block head %}{% endblock %}</head>
<body>{% block body %}{% endblock %}</body>
</html>
base2.html:
{% extends "base1.html" %}
{% block head %}
<meta .... />
<title .... />
css includes etc.
{% endblock %}
{% block body %}
{% block header %}{% endblock %}
{% block featured %}{% endblock %}
{% block navigation %}{% endblock %}
{% block content %}{% endblock %}
{% block footer %}{% endblock %}
{% endblock %}
Also base3.html and base4.html which futher detalize the generic body structure defined in previous base templates (not shown here). The last template extends base4.html, overriding blocks with concrete content (markup is handled by base templates).
The question is: I have two templates: main.html and article.html which both extend base4.html. But in article.html I want the order of body blocks defined in base2.html to be different (featured block to go after navigation). How can I achieve that? Or how to refactor the structure of templates to make this possible? Overriding the block body in top-level template doesn't work.
I don't think that you're going to have any luck going about it that way. A simple solution could be to have an optional variable that base2.html looks at, which determines the alternate order. In fact it may even be that you can define the presence of this variable in the article.html template itself. I haven't tried this, but something like the following may work:
{% with alternate_order=1 %}
{% include base4.html
{% endwith %}
Why do you have so many levels of inheritance? As a note, django provides no limitations, but typically we use the three-level approach. See docs
Here are a few suggestions:
You can probably combine base1.html and base2.html.
If you can put the html code in the parent then do so. I usually implement my templates like this:
<div id='sidebar'>{% block sidebar_content %}{% endblock %}</div>
As for your problem, if you need a different layout, that's when you branch. That is, if using a different css won't work.