TWIG - variable == objectattribute - arrays

I am new to Twig and Symfony2 (and PHP as well). I currently have an array of objects where I can access an attribute by doing {{result.attribute1}} after a {for result in results} statement.
I would like to use a variable {{var| removePath}} (where removePath is an extension I made in Twig), and use it to iterate over the results array. What I would like to do is return the row if {{var| removePath}} == result.attribute1.
Is this possible to do in Twig? If so, how should I approach this? I have already tried something similar to the code found below, but the "else" statement is executed (saying there's no match). I also tried var == result.attribute, but this didn't work either.
{% for result in results %}
{% if var|removePath in result.attribute1 %}
{{ var | removePath }} exists.
{% else %}
{{ var | removePath }} doesn't exist in array.
{% endif %}{% endfor %}
Thus I think I have 2 questions;
1) Is it possible in TWIG (if so, how)
2) Is there an easier/better way to get the whole row (object array also contains attributes 2, 3 & 4, and I would like to return those that are in common with attribute 1 (if it matches var))
See schema of my array recently added
Thank you in advance for your help! I hope my question is understandable :S

Just surround the expression with brackets.
use (var|removePath) which groups an expression.
example:
{% if ( 1+1 ) == 2 %}
And you can use the attribute() function to access properties and methods of an object/array.
{{ attribute(object, method) }}
{{ attribute(object, method, arguments) }}
{{ attribute(array, item) }}
attribute chapter in the twig docs.
Further shorten template code using the ternary operator like this:
{% set strippedVar = var|removePath %}
{{ strippedVar }}{{ (strippedVar in result.attribute1) ? 'exists' : 'does not exist' }}.
Setting the filtered variable to another prevents executing the filter multiple times.

Related

Comparing two arrays, checking for matching values

Using HubL (as I'm building a module in HubSpot), I have two arrays:
topics : Which is a list of topics.
all_tags: Which is an array of all the blog tags in the system.
If I dump out these arrays, this is what it will return:
{{ topics }} prints the following: [Data, Accounting, Insight]
{{ all_tags }} prints the following: [Accounting, Press, Data]
So essentially, {{ topics }} has a tag ("Insight") that doesn't exist in the system yet.
What I'm trying to do is to create a third array, which will contain matching results from the two above arrays. For example, topics_final, once returned, should print [Data, Accounting].
But, when printing {{ topics_final }}, the array is empty.
What I've tried:
<!-- this gets all tags -->
{% set all_tags = blog_topics( blog_id , 250) %}
<!-- create arrays -->
{% set topics = [] %}
{% set topics_final = [] %}
<!-- append topic data to the array -->
{% for item in module.add_topics.topics %}
{% set topic_option = item|striptags %}
{% do topics.append( topic_option ) %}
{% endfor %}
<!-- check if topic tags exists in HubSpot -->
{% for topics in all_tags %}
{% if topics in all_tags %}
{{ topics }}
<!-- results with above
Data, Accounting, Insight
-->
{% else %}
else
{% endif %}
{% endfor %}
With the above, it just prints out the {{ topics }}, even though Insight isn't in the all_tags array.
Note: Tagging Jinja2 as the syntax is similar
A combination of the filter reject and the built in test in could help you achieve this.
Sadly, it seems the reject filter does not accept negating a test, still, you can reject all the elements of topics that are not in all_tags then reject the said elements from the topics list.
So that ends with:
{{ topics | reject('in', topics | reject('in', all_tags) | list) | list }}
Switch yields:
[
"Data",
"Accounting"
]

TemplateSyntaxError - Invalid filter: 'translate' [duplicate]

I want to use AngularJS with Django however they both use {{ }} as their template tags. Is there an easy way to change one of the two to use some other custom templating tag?
For Angular 1.0 you should use the $interpolateProvider apis to configure the interpolation symbols: http://docs.angularjs.org/api/ng.$interpolateProvider.
Something like this should do the trick:
myModule.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
Keep in mind two things:
mixing server-side and client-side templates is rarely a good idea and should be used with caution. The main issues are: maintainability (hard to read) and security (double interpolation could expose a new security vector - e.g. while escaping of serverside and clientside templating by themselves might be secure, their combination might not be).
if you start using third-party directives (components) that use {{ }} in their templates then your configuration will break them. (fix pending)
While there is nothing we can do about the first issue, except for warning people, we do need to address the second issue.
you can maybe try verbatim Django template tag
and use it like this :
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.min.js"></script>
{% verbatim %}
<div ng-app="">
<p>10 is {{ 5 + 5 }}</p>
</div>
{% endverbatim %}
If you did separate sections of page properly then you can easily use angularjs tags in "raw" tag scope.
In jinja2
{% raw %}
// here you can write angularjs template tags.
{% endraw %}
In Django template (above 1.5)
{% verbatim %}
// here you can write angularjs template tags.
{% endverbatim %}
We created a very simple filter in Django 'ng' that makes it easy to mix the two:
foo.html:
...
<div>
{{ django_context_var }}
{{ 'angularScopeVar' | ng }}
{{ 'angularScopeFunction()' | ng }}
</div>
...
The ng filter looks like this:
from django import template
from django.utils import safestring
register = template.Library()
#register.filter(name='ng')
def Angularify(value):
return safestring.mark_safe('{{%s}}' % value)
So I got some great help in the Angular IRC channel today. It turns out you can change Angular's template tags very easily. The necessary snippets below should be included after your angular include (the given example appears on their mailing lists and would use (()) as the new template tags, substitute for your own):
angular.markup('(())', function(text, textNode, parentElement){
if (parentElement[0].nodeName.toLowerCase() == 'script') return;
text = text.replace(/\(\(/g,'{{').replace(/\)\)/g, '}}');
textNode.text(text);
return angular.markup('{{}}').call(this, text, textNode, parentElement);
});
angular.attrMarkup('(())', function(value, name, element){
value = value.replace(/\(\(/g,'{{').replace(/\)\)/, '}}');
element[0].setAttribute(name, value);
return angular.attrMarkup('{{}}').call(this, value, name, element);
});
Also, I was pointed to an upcoming enhancement that will expose startSymbol and endSymbol properties that can be set to whatever tags you desire.
I vote against using double parentheses (()) as template tag. It may work well as long as no function call is involved but when tried the following
ng:disabled=(($invalidWidgets.visible()))
with Firefox (10.0.2) on Mac I got a terribly long error instead of the intended logic. <[]> went well for me, at least up until now.
Edit 2012-03-29:
Please note that $invalidWidgets is deprecated. However I'd still use another wrapper than double braces. For any angular version higher than 0.10.7 (I guess) you could change the wrapper a lot easier in your app / module definition:
angular.module('YourAppName', [], function ($interpolateProvider) {
$interpolateProvider.startSymbol('<[');
$interpolateProvider.endSymbol(']>');
});
API docs.
You could always use ng-bind instead of {{ }}
http://docs.angularjs.org/api/ng/directive/ngBind
<span ng-bind="name"></span>
I found the code below helpful. I found the code here: http://djangosnippets.org/snippets/2787/
"""
filename: angularjs.py
Usage:
{% ng Some.angular.scope.content %}
e.g.
{% load angularjs %}
<div ng-init="yourName = 'foobar'">
<p>{% ng yourName %}</p>
</div>
"""
from django import template
register = template.Library()
class AngularJS(template.Node):
def __init__(self, bits):
self.ng = bits
def render(self, ctx):
return "{{%s}}" % " ".join(self.ng[1:])
def do_angular(parser, token):
bits = token.split_contents()
return AngularJS(bits)
register.tag('ng', do_angular)
If you use django 1.5 and newer use:
{% verbatim %}
{{if dying}}Still alive.{{/if}}
{% endverbatim %}
If you are stuck with django 1.2 on appengine extend the django syntax with the verbatim template command like this ...
from django import template
register = template.Library()
class VerbatimNode(template.Node):
def __init__(self, text):
self.text = text
def render(self, context):
return self.text
#register.tag
def verbatim(parser, token):
text = []
while 1:
token = parser.tokens.pop(0)
if token.contents == 'endverbatim':
break
if token.token_type == template.TOKEN_VAR:
text.append('{{')
elif token.token_type == template.TOKEN_BLOCK:
text.append('{%')
text.append(token.contents)
if token.token_type == template.TOKEN_VAR:
text.append('}}')
elif token.token_type == template.TOKEN_BLOCK:
text.append('%}')
return VerbatimNode(''.join(text))
In your file use:
from google.appengine.ext.webapp import template
template.register_template_library('utilities.verbatim_template_tag')
Source:
http://bamboobig.blogspot.co.at/2011/09/notebook-using-jquery-templates-in.html
You can tell Django to output {{ and }}, as well as other reserved template strings by using the {% templatetag %} tag.
For instance, using {% templatetag openvariable %} would output {{.
I would stick with a solution that uses both django tags {{}} as well angularjs {{}} with a either a verbatim section or templatetag.
That is simply because you can change the way angularjs works (as mentioned) via the $interpolateProvider.startSymbol $interpolateProvider.endSymbol but if you start to use other angularjs components like the ui-bootstrap you will find that some of the templates are ALREADY built with standard angularjs tags {{ }}.
For example look at https://github.com/angular-ui/bootstrap/blob/master/template/dialog/message.html.
If you do any server-side interpolation, the only correct way to do this is with <>
$interpolateProvider.startSymbol('<{').endSymbol('}>');
Anything else is an XSS vector.
This is because any Angular delimiters which are not escaped by Django can be entered by the user into the interpolated string; if someone sets their username as "{{evil_code}}", Angular will happily run it. If you use a character than Django escapes, however, this won't happen.

Django - Access list values on template

I've to get access to all values availables in a list(array) in a Django template. All examples I find online, use dictionaries instead. So, the list is passed correctly to the template, but the following code doesn't work:
{% for item in array %}
field_names = field_names + "{{ item }},";
{% if forloop.last %}
field_names = field_names + "{{ item }}";
{% endif %}
{% endfor %}
I could access the values using {{ field.[0] }} , {{ field.[1] }} , etc. But, I have to make it work independently of the list's size.
Can anybody give me a hint?
P.S. - I cannot use a dictionary to pass the values to the template, since that code wasn't donne by me, and I'm not supposed to touch it.
In your view:
my_joined_list = ",".join(somelist)
In your template:
field_names = {{ my_joined_list }}
Resolved it!! I just passed the variable to the template exactly as i needed it and used {{ array|safe }} to resolve it to a Javascript variable. Done!

Django template - compare date to today

I'm trying to make a simple IF function that checks if the date of an item is equal to today or not. Unfortunately I can't get it to work. I basically decides that the statement is false and doesn't show any content, even when it show. I am not getting any error either.
The code I use is following:
{% if item.valid_until.date == now.date %}
<div id="what_i_want_to_show">
CONTENT
</div>
{% endif %}
The content of valid_until is a DateTimeProperty from a Google App Engine app. Normally working with this in Django template doesn't cause any problems. Solutions from similar questions on SO didn't work so far. Am I missing something obvious?
UPDATE 1:
This statement runs in a loop on the result of a database query. Therefore doing the comparison in the view didn't work as far as I could see, since I would have to send the variable with each item.
There are 2 aproach on this case:
1st:
you can add a #property on model
Model:
from datetime import date
#property
def is_past_due(self):
return timezone.now() > self.valid_until # if valid until default is timezone.now else change it
Template:
{% if item.is_past_due %}
<!--In the past-->
{% else %}
{{ item.valid_until.date|date:"Y-m-d" }}
{% endif %}
2nd:
declare a today date with format on template
{% now "Y-m-d" as todays_date %}
{% if todays_date < item.valid_until.date|date:"Y-m-d" %}
<div id="what_i_want_to_show">
CONTENT
</div>
{% endif %}

Twig array access

I'm trying to print out value of the variable passed to the twig template. I'm using this code:
{{ naziv[0] }} Index is 0 because passed array has only one element. Mentioned code produces following error:
Key "0" for array with keys "title" does not exist in...
but when I use for loop like this:
{% for key,value in naziv %}
{{ value }}
{% endfor %}
I get what I want.
What's wrong with {{naziv[0]}} ?
Based on the var_dump of array(1) { ["title"]=> string(11) "SpaceVision" }
You should access your array in this way: {{ naziv['title'] }}.
The key of your array is associative and not a numerically indexed array. That is why you cannot use naziv[0].
You can also use: {{ naziv.title }} aswell.
See the documentation.
Your array is not number indexed, thus naziv[0] is not defined. Access it as naziv.title.

Resources