Pass current route to template - templates

I've been playing around with golang for a couple of weeks now, and recently started to look into the revel framework.
In other languages/frameworks that I've used, it's always been possible to check what the current route is from within the template. Something that made it easier for me to keep things like navigation in a separate template file, and just do this:
<!-- in header template -->
<ul class="nav">
{{ if eq .req.action "App.Index" }}
<li class="active"><a href="#">
{{ else }}
<li><a href="{{url "App.Index" }}">
{{end}}
Home</a></li>
<!-- other links here -->
</ul>
This isn't the actual code I'm writing, but I hope this makes it clear what the idea is: Have a template for the navigation handy, and set classes/links according to which action is the current one.
After some time going through the source code, and a number of golang template examples, I couldn't quite see any way in which the current action or anything else is exposed to the template.
To get around this, I'm currently using a func interceptor which automatically sets a render argument for me:
func prependRoute(c *revel.Controller) revel.Result {
c.RenderArgs["_current_route"] = c.Action
return nil
}
This is working fine, but it feels a bit hacky. I think I'm probably missing something really obvious that would allow me to reserve func interceptors for when I really need them.
The question, then, is simple: What is the correct, and most reliable way to find out what the current action is in a revel template?

Related

Unit tests for checking content that is rendered in template

I was browsing a little and was not able to find any solutions for something I want to accomplish.
I am in Symofny 5 project and I am writing some Unit tests.
The goal of my tests is to check, if the messages will appear as they are defined. The messages are rendered by the twig template. I am not that familiar with Symfony and twig regrading to tests. I do not know, if I somehow can render the output of a view using that twig template for just that entry and then check, if the message contains the expected text in the right manor.
Is it something like this doable?
Can some help and give me guidance as example for something like this?
I think it can be beneficial to other coders out there in the future.
I have an entity like User, than want to get the status, like:
$user->getStatus('ready);
in twig:
{% if item.status == 'ready' %}
<p>The status of user is {{ item.status }}</p>
{% endif %}
So, I want to assert that twig templates are rendering correct output for given status.
Update 2:
According to your comment, you are probably trying to test multiple things at once. While not inherently bad, it's probably not what you would call Unit Test. In the ideal application, you want to separate your data persistence logic from the rendering logic completely.
I cannot provide you with any meaningful code sample without knowing what you want to do, but I can give you some general pointers:
You can test your persistence logic by itself without twig. If you want to test if your data correctly persists with some default values, you can simply assert the objects retrieved from the database.
Inversely, if you need to check your template being rendered correctly, you can mock all the data you need, provide dummy objects and such, everything should work the same.
If you really want to bring it all together and test the application as a whole, then you should look into proper integration tests that actually call your endpoints with proper HTTP Requests and assert received HTTP Responses.
Update 1: I've updated the example according to your updated question
I am not exactly sure what are you trying to accomplish exactly, but it seems that you want to assert that twig templates are rendered correctly.
First, you need to figure out the end result. First let's assume your template is:
<html>
{% if user.status is not empty %}
<p>The status of user is {{ user.status }}</p>
{% endif %}
</html>
Then your test code would be something like this:
public function testTwigRender(): void
{
$expected=<<<'EXPECTED'
<html>
<p>The status of user is ready</p>
</html>
EXPECTED;
$user = new User();
$user->setStatus('ready');
$twig = self::$kernel->getContainer()->get('twig');
$actual = $twig->render('AppBundle::app/template.html.twig', ['user' => $user]);
self::assertEquals($expected, $actual);
}

Tag or inherit the same code in Django template with minor changes

I have a bunch of code that I will need to use repeatedly on a page, and on multiple pages. For example, here is a shorter version of the code:
<a href="#"
data-toggle="popover"
title="{% for terms in s_terms %}{% if terms.slug == 'neuron' %}{{terms.title}}{% endif %}{% endfor %}"
data-content="{% for terms in s_terms %}{% if terms.slug == 'neuron' %}{{terms.para_one}}{% endif %}{% endfor %}">
Toggle popover
</a>
There is a lot more code in the block. Now, for obvious reasons I do not want to keep repeating such large chunks of code. I am a fan of the DRY approach.
However, I can't figure out how to render this same piece of code repeatedly. The only thing that would change is the word = "neuron" in there. I thought of using template tags, but that didn't work.
I tried saving the code as a separate file, and inherit it within my template, but then I can't change the keyword ('neuron'). I also tried creating a separate dynamic page, and include that in my Django template, but looks like the include tag only works for templates, and not for dynamic pages.
Can anyone help, please? Thank you, in advance.
You could use Django template built-in template tag include.
From the documentation:
Loads a template and renders it with the current context. This is a
way of “including” other templates within a template.
So, you can just extract your snippet in a separate template and then use it with:
{% include "snippet_template.html" %}
Additionally, you can pass a variable to the include template using the with keyword - you would use this to pass your word parameter:
{% include "snippet_template.html" with word="neuron" %}
As #bonidjukic wrote the include statement is what you search.
But include statement inside for-loop could reach one weakness of Django template Engine (vs Jinja). You include just variables, so it will be fast.
In the case of needing tags (like trans), Django will load tags at each include. Where Jinja will have global "tags".
So just be careful, with how you DRY you templates.

Replace string in HTMLbars/Handlebars expression with component

I've got a blog. Each blog-posts can have multiple downloads. For the downloads I created a component downloads
Currently I render them at the end of each post like this:
{{#each download in sortedDownloads }}
<p>
<a class="dl-button" {{ action "incDownload" download }}>
{{ download.name }} ({{ download.size}}MB)
</a> - {{ download.downloadcount }} Hits
</p>
{{/each}}
I'd like to be able to write something like [downloads] in the post content itself (which is simply rendered via {{{post.parsedBody}}}and replace it with a partial like the above one.
Is this possible or do you have a better way to achieve this?
This does not really look achievable by using either outlet or yield, since the post content will not be interpreted by the render engine.
What should be working though is to have the placeholder in your content just as you mentioned it, and replace it by some identifiable HTML placeholder in your post.parsedBody.
You could then create a View on didInsertElement, and call that view's appendTo() method to render the downloads inside the placeholder.
One could say writing some jquery-ish elements also works, but I think inserting arbitrary elements in the views tree is horrible and goes against the Ember way of managing views tree.
Cheers!

Template Contexts not recognized from external file (Django)

So, I've been editing a website and have many JavaScript functions that utilize the Contexts that the views.py file passes to the page. Until now, these functions have been contained in the base.html file and so have been loaded onto every page directly. However, to make things cleaner, I copy and pasted all the functions to an external .js file. Now, rather than use the contexts, the functions consider them to be literal strings.
Example:
$('#title').text('{{ event.name }}');
The above line will actually set the text of the element to say "{{ event.name }}" rather than the name of the event. Any ideas on how to fix this? I really don't want to keep these functions in the base file where they can be seen by anyone who inspects the page source.
It doesn't matter if you put your javascript functions in an external file or in your base.html it would still get exposed to the user. Only a minification of the file would actually help to trick the user from seeing the actual values but with javascript all your code is public.
Why you're having this problem is because when you rendered the javascript inline (in your base.html) you had access to the template context.
This is no longer the case and the Django template engine doesn't interpolate your {{ event.name }} anymore.
The problem you're facing as well is a good one. You should never mix and match javascript with Djangos template language or any template language for that matter and the only way of fixing it is to
a) start pulling the values from the DOM ie. render a proper DOM
b) to start to fetch the values from the server, traditionally using AJAX.
And the smallest example that I can muster at the moment is below:
Your view:
def my_django_view(request):
return HttpResponse(json.dumps({'meaningoflife':42}), mimetype='application/json')
Your HTML
<input type="hidden" id="myMeaning" value="{{ meaningoflife }}" />
Your javascript
var meaning = document.querySelector('#myMeaning').value;
alert(meaning); //should alert 42.
In your view you return some form of render_to_response which takes a template argument and a context argument. What the render_to_response function does is read your template, and replace all {{ placeholders }} with the values passed via the context dictionary.
Templates are essentially a complex version of this
"""
<h1>{{ person.name }}</h1>
<p>{{ person.phone_number }}</p>
""".format(person)
The problem is the templating engine does not know files specified by a scripts src attribute is actually a Django template. To fix this don't use the script src attribute. Instead do something like this.
<!--base.html-->
<h1>Site Title</h1>
<p>Some content</p>
<script>
{% include 'jsfile.js' %}
</script>
Using the include statement should do the trick.

Django: Passing argument to parent template

I have templates of this style
project
- main_templates (including nav bar)
-- app1
--- app1_base_template
--- app1_templates
-- app2
--- app2_base_template
--- app2_templates
So when rendering, app2_templates extends app2_base_template which extends main_template.
What I need to do, is have the corresponding nav item be bold when app2's template is being rendered (to show the user where he is).
The easiest would if I could pass a variable in the {% block xxx %} part.
Is this possible ?
What other generic ways are there ?
Have you tried the {% with %} template tag?
{% block content %}
{% with 'myvar' as expectedVarName %}
{{block.super}}
{% endwith %}
{% endblock content %}
There's no direct way to pass a variable up the template inheritance tree the way you describe. The way that people implement navigation bars that highlight the current page is often tweaked to the nature of the site itself. That said, I've seen two common approaches:
The low-tech approach
Pass a variable in the template context that indicates which tab is active:
# in views.py
def my_view(request):
return render_to_response('app2_template.html', {"active_tab": "bar"},
<!-- Parent template -->
<div id="navigation">
<a href="/foo" {% ifequal active_tab "foo" %}class="active"{% endifequal %}>Foo</a>
<a href="/bar" {% ifequal active_tab "bar" %}class="active"{% endifequal %}>Bar</a>
</div>
The higher-tech approach
Implement a custom template tag to render your navigation bar. Have the tag take a variable that indicates which section is active:
<!-- Parent template -->
<div id="navigation">{% block mainnav %}{% endblock %}</div>
<!-- any child template -->
{% load my_custom_nav_tag %}
{% block mainnav %}{% my_custom_nav_tag "tab_that_is_active" %}{% endblock %}
You can pretty much go crazy from there. You may find that someone has already implemented something that will work for you on djangosnippets.org.
Inability to pass args on template inclusion is one of many failings of the Django template system.
I had to deal with an almost identical problem: Deeply nested templates needed to cause parent templates to format/highlight differently.
Possible solutions:
Use a "super context" routine that sets a number of values based on where in the hierarchy you are. I.e. super_context = MySuperContext(request, other, values, etc.), where super_context is a dict you pass to the view (or to RequestContext). This is the most Django-thnonic(?) approach, but it means that presentation logic has been pushed back into the views, which feels wrong to me.
Use the expr template tag to set values in the lower level templates. Note: this only works when you {% include %} a template because it must be evaluated before the inclusion occurs. You can't do that with an {% extends %} because that must be the first thing in the child template.
Switch to Jinja2 templating, at least for the views where you need to do this.
Once you have these values set you can do things like this:
<div class="foo{% if foo_active%}_active{%endif%}"> stuff </div>
This makes the div class "foo" when it's not active and "foo_active" when it is. Style to taste, but don't add too much cinnamon. :-)
I have taken Jarret Hardie's "low tech" approach in a similar, err... context (yes, it's a pun... which won't make perfect sense to you unless I tell you that I was not doing navs but setting the border color of buttons in order to show which one had been pressed).
But my version is a bit more compact I think. Instead of defining just one simple context variable activebar in the view, I return a dictionary, but always with only one key-value pair: e.g. activebar = {'foo': 'active'}.
Then in the template I simply write class="{{activebar.foo}}" in the foo anchor, and correspondingly in the other anchors. If only activebar.foo is defined to have the value "active" then activebar.bar in the bar anchor will do nothing. Maybe "fail silently" is the proper Django talk. And Bob's your uncle.
EDIT: Oops... a couple of days have passed, and while what I had written above did work for me a problem appeared when I put into the navbar an anchor with a new window as target. That seemed to be the cause of a strange glitch: after clicking on the new window (tab in Firefox) and then returning to the one from which the new window was launched, portions of the display below the navbar became blank whenever I quickly moved the cursor over the items on the navbar--- without clicking on anything. I had to force a screen redraw by moving the scroll bar (not a page reload, though that too worked because it involves a screen redraw).
I'm much too much of a noob to figure out why that might happen. And it's possible that I did something else that caused the problem that somehow went away. But... I found a simpler approach that's working perfectly for me. My circumstances are that every child template that is launched from a view should cause an associated navbar item to be shown as "active". Indeed, that navbar item is the one that launched the view that launched the child template--- the usual deal.
My solution--- let's take a "login" navbar item as an example--- is to put this in the child template that contains the login form.
{% block login %}active{% endblock %}
I put it in below the title block but I don't suppose the placement to matter. Then in the parent template that contains the navbar definition, for the li tag that surrounds the anchor for the login navbar item I put... well, here's the code:
<li class="{% block login %}{% endblock %}">Login</li>
Thus when the child template is rendered the parent will show the login navbar item as active, and Bob's still your uncle.
The dictionary approach that I described above was to show which of a line of buttons had been pressed, when they were all on the same child template. That's still working for me and since only one child template is involved I don't see how my new method for navbars would work in that circumstance. Note that with the new method for navbars views aren't even involved. Simpler!
Variables from the parent template are automatically included in the child's scope. Your title says you want to pass a variable TO the parent, which doesn't make sense, as you can't define variables in templates. If you need it a variable in the both the parent and the child, just declare it in the view.
Sadly I can't find a clean way.
Ended up putting a tag in each app's base.html:
<span class="main_nav_bar_hint" id="2"></span>
(Actually I use two. One set by app's base for the main nav. One set by app pages for app's nav bar)
And a bit of JQuery magic in the project base.html
$(document).ready(function() { $("#nav_menu_" + $(".main_nav_bar_hint").attr("id")).removeClass("normal").addClass("selected"); })
Its a bit of a hack, but this way its easy to understand and I only need to make logical changes once more apps are added.
You can use HttpRequest.resolver_match.
In the parent html template:
<li class="nav-item {% if request.resolver_match.view_name == 'my_simple_blog:homepage' %}active{% endif %}">Home</li>
<li class="nav-item {% if request.resolver_match.view_name == 'my_simple_blog:about' %}active{% endif %}">About</li>
It checks for the current namespace and compare it, if they are the same, add active in the class of the list.