Twig - Showing/Hiding Element Based Upon Date - javascript

I am looking to customize a Twig template by making the display of an element (i.e. a url link) sensitive to the current date. As I'm fairly new to Twig, would Twig have any in-house ways to facilitate this, or would I most likely utilize some Javascript to achieve this?
Thanks for any leads.

You could use the date-filter and the special keyword NOW to achieve this
{% if "NOW"|date('U') > '2018-06-10'|date('U') %}
Show this
{% endif %}
{% if "NOW"|date('U') > '2029-06-10'|date('U') %}
Don't show this
{% endif %}
do note that you could just pass the date as a DateTime to your template to avoid the static code in the template`

Related

Can I use liquid inside JS file?

I'm using Shopify and I need to show some message if specific product tag is exist. I need to check for product tags from JS file and need to know how can I do that.
I tried to use {{product.tags}} in the js file but I got an error, I also tried that in .js.liquid file and it still throw an error.
{% assign clearance = false %}
{% for tag in product.tags %}
{% if tag contains 'Clearance' %}
{% assign clearance = true %}
{% endif %}
{% endfor %}
There is a way that I could use that in js? If not, how can I grab all product tags in js way? I know I can do that from liquid file but I have to do that only in the js file.
You are probably looking for an extension to Javascript called jsx.
Aside from that, natively, you have template literals, but you can only use expressions in that, no looping or other logic (aside from ternary expressions).
If what you are looking for is not related to creating HTML, but only to modifying a variable (or executing other code on data), you can use standard Javascript:
let clearance = false;
for (const tag in product.tags) { // this assumes product.tags is an object
for (const tag of product.tags) { // this assumes product.tags is an array
if (tag.includes('Clearance') {
clearance = true;
}
}
The .js.liquid file will only accept translation string as var in Liquid (example: {{ "my_text" | t }}.
If you want to use other kind of Liquid strings in Javascript code, you may use a section and include your javascript inside {% javascript %} tags.
This will also work in a snippet or a template.

Shopify - using if statements within js.liquid and scss.liquid files

I am trying to merge JS files together in 1 file, however I need to set conditions using the if statements within the js file.
I have created file.js.liquid but it does not seem to working; I added a test for example:
{% if template contains 'index' %}
console.log('homepage loaded');
{% endif %}
Example of what i am trying to do, of course have plenty i need to do.
{% if settings.the_simpsons_enabled and settings.simpsons_video_enable %}
// video popup
$(".video-popup").videoPopup();
{% endif %}
The conditional statements are also not working within scss.liquid files either:
{% if template contains 'index' or template contains 'collection' or template contains 'product' %}
#import url("{{ 'owl.carousel.scss' | asset_url }}");
{% endif %}
Any ideas why if would not be working or if its even possible? I assume it is being a liquid file.
The Liquid extension on such files like CSS or JS allows use of Liquid variables but not statements.
So {{ write_my_liquid_var_here }} will work. {% if liquid_statement %}do something{% endif %} won't.
Regarding Shopify and SASS
The following won't work as you can't use #import in Shopify's SASS files.
#import url("{{ 'owl.carousel.scss' | asset_url }}"); //This will not work
Regarding using conditional liquid statements in .js.liquid files
I don't know everything about this subject, but I do know the following two things.
1) Check out this thread and you'll see that this type of statement won't work because js files are parsed independent of normal liquid templates, so it would have no idea which template you're viewing.
{% if template contains 'index' %} //This will not work
A better place to put the above logic would be at the bottom of your theme.liquid file above the </body> tag. However this won't help you with what seems to be your goal of reducing HTTP requests by using one JavaScript fie.
2) However, you can access values in your settings_data.json file
{% if settings.the_simpsons_enabled and settings.simpsons_video_enable %} //This will work
I hope this helps

How to include properly Javascript using a Javascript Twig Template

I know how to access my assets, but i wanted to create a template working with Javascript and Twig, which would allow me to use Twig variables in my script.
For example, I have a template called 'details.html.twig', which extends a 'layout.html.twig':
{% extends '#AcmeBundle/Front/layout.html.twig %}
{# SOME CODE AND CONTENT #}
{% block page_script %}
{{ parent() }}
<script>
{# Part of my script #}
</script>
{% endblock page_script %}
All of this worked perfectly, but to be able to reuse some js code (using twig variables), I cut some part of my script and put I in an external template, called 'viewer.html.twig'. To be able to use it in my 'details.html.twig', I used:
{% include '#AcmeBundle/Front/Model/viewer.html.twig' %}
I put this line right after the {{ parent() }} one and before the <script> tag.
However, my script which used to work when directly written in 'details' template seems to be quite ignored right now.
If any of you could have a clue of what makes my template being included but skipped, or a way to include the template easier or properly, I'm open to all kind of observations.
G'day,
Jérémy
If you code a file containing only javascript you should name your file:
{% include '#AcmeBundle/Front/Model/viewer.js.twig' %}
Then Twig (as of 1.17) automatically defines its escaping strategy based on the extension.
See the twig docs and an example of js twig file in symfony.

changing class of element in django template using javascript function

In my django template I have some Event items listed using a for loop.The event has an associated date_of_occurrence attribute which is a python DateTime.I need to make the background for the event red ,if the datetime is less than current datetime.
I thought of doing it like this
In template
<ul>
{% for event in events %}
<div id="event_div"
<li>
event.name<br>
event.date_of_occurrence
</li>
</div>
{% endfor %}
</ul>
In css
.pastevent{
background-color:red;
}
I need a javascript function to set the class of element with id event_div to pastevent.Can someone tell me how this function can be implemented?
As I understand the function is not fired by any click event ,but when page loads. How will the datetime value of event passed from the template to do the comparison to the current javascript date?
You wouldn't necessarily have to use javascript to do this. There are a couple of different options using the template tags.
The simpler approach (and more crude) would be to render the current datetime to the template as eg. current_date then do a logic check in your template.
{% for event in events %}
{% if event.date_of_occurrence < current_date %}
<div id="pastevent">
{% else %}
<div id="event_div">
{% endif %}
<li>
event.name<br>
event.date_of_occurrence
</li>
</div>
{% endfor %}
The other option would be to create a custom template filter that returns whether the event was in the past or not and apply the same logic as above.

a better way to do ajax in django

The other day I wrote some AJAX for a Django app that i have been working on.
I come from Ruby on Rails, so I haven't done much in the way of raw JS.
So based on Rails' partials, I something similar to the following in a sort of pseudocode, don't sweat the details:
1) JS function using prototype's Ajax.Updater ('tablediv' being the id of the table i wanted to update Ajaxily, and url pointing to the proper django view)
function updateTable(){
new Ajax.Updater('tablediv',url {params: params....etc
2) django view that got new data to populate the table with:
def ajaxTable
objects = Objects.object.all...
return render_to_response('ajaxtable.html',objects)
3) ajaxtable.html was just a sort of Rails "partial" so basically a table w/o <table> </table> ...:
<th>{{object.data}}</th>
<td>{{object.moredata}}</td>
so to my actual question:
This seemed hacky to me, I sort of threw it together after getting tired of searching online for what i wanted.
Is this the way it's done? It works fine, I just don't know enough to know, you know?
It kinda depends what you want to do I think. Ajax being quite a wide range of scenarios from Google Maps to a simple auto-complete varys greatly in complexity and the best approach.
However, there are some useful things you can do that help.
1) Template level
Make sure you have "django.core.context_processors.request" in your TEMPLATE_CONTEXT_PROCESSORS setting. Then you can do this;
{% if not request.is_ajax %}
<html>
<head>
...
</head>
<body>
...
{% endif %}
actual content
{% if not request.is_ajax %}
</body>
</html>
{% endif %}
Basically then say this page is /test/ you can do a browser request and get the full content or a request via JavaScript and just get the content. There is a blogpost somewhere that explains this in more detail but I can't find it at the moment.
2) In the view
In the template we are just accessing the request object in the template. In the view you can do very similar things.
def my_view(request):
if requst.is_ajax():
# handle for Ajax requests
# otherwise handle 'normal' requests
return HttpResponse('Hello world')
The above methods don't really do it differently than you do but allow you to re-use views and write it bit more concisely. I wouldn't really say what you are doing is wrong or hacky but you could write it to make it more concise and re-use the templates and views.
say for example you could have just one template and if its a Ajax request have it only return the section that will need to be updated. In your case it would be the tables views.
I am quite late, but I want to document how to combine and adapt the solutions presented by d0ugal
in a way, that it will resolve a much cleaner template-code.
I have a model representing contact persons.
The (generic) view to get one ContactPerson looks like this:
def contcactperson_detail_view(request, name):
try:
person = ContactPerson.objects.get(slug=name)
except:
raise Http404
if request.is_ajax():
return contcactperson_detail_view_ajax(request, person)
return list_detail.object_detail(
request,
queryset = ContactPerson.objects.all(),
object_id = person.id,
template_object_name = "contactperson",
)
#render_to('cms/contactperson_detail_ajax.html')
def contcactperson_detail_view_ajax(request, person):
return {'contactperson':person, 'is_ajax':True}
The template to render the view that handles one ContactPerson is called contcactperson_detail_view.html:
{% extends "index.html" %}
{% block textpane %}
<h1 id="mainheader">{{ contactperson.first_name }} {{ contactperson.family_name }} </h1>
<div class="indentation"> </div>
{% include 'cms/contactperson_detail_photo.html' %}
<div id="text_pane">
{% include 'cms/contactperson_detail_textpane.html' %}
</div>
{% endblock %}
It includes two sub-templates
contactperson_detail_textpane.html
<p>{{ contactperson.description }}</p>
<ul>
<li>
<dl>
<dt>Email</dt>
<dd>
{{ contactperson.mail }}
</dd>
</dl>
</li>
<li>
<dl>
<dt>Contact Person for</dt>
<dd>
<ul>
{% for c in contactperson.categories.all %}
<li>{{ c }}</li>
{% endfor %}
</ul>
</dd>
</dl>
</li>
</ul>
and contactperson_detail_photo.html
{% with contactperson.photo.detailphoto as pic %}
{% with pic.url as pic_url %}
<div {% if not is_ajax %}id='imageContainer'{% endif %} style="float: right;padding-right:0.5em;
padding-bottom: 1em; padding-left:0.5em;clear:both;
width:{{ pic.width }}px">
<div style="width:{{ pic.width}}px">
<img style="clear:both" src="{{ pic_url }}" alt="{{ i.name }}"/>
</div>
</div>
{% endwith %}
{% endwith %}
this 3 templates will be used, if the request isn't ajax.
But if the request is ajax, contcactperson_detail_view will return the view contcactperson_detail_view_ajax, that uses the template contactperson_detail_ajax.html for rendering. And this template looks like this:
<h1>{{ contactperson.first_name }} {{ contactperson.family_name }}</h1>
{% include 'cms/contactperson_detail_photo.html' %}
{% include 'cms/contactperson_detail_textpane.html' %}
So it uses the same sub-templates but isn't extending anything, therefore only the needed markup delivered. As the ajax view passes is_ajax = True to the template, it can be used to adjust minor things, like setting correct id-attributes.
No context-processor or additional url-conf needed.
Finally the Javascript code:
$("#contact_person_portlet a").click(function(event){
event.preventDefault();
$.ajax({
type: "GET",
url: event.target.getAttribute('href'),
success: function(msg){
overlay(msg);
}
});
});
Hope that it will be useful for some people. If so, please leave a comment!
No matter what, you're going to need at least two things:
Your javascript code to make the call (you have this)
Server side code to handle the request (this is your view and url-config)
There is absolutely nothing "hacky" about this.
The third thing, your template file, is optional - but is generally good practice. You want to separate your markup from the code, for many reasons.
So I think you've got the right idea. Carry on.
What exactly seems hacky about it? Seems like a perfectly valid way of doing something.
I guess an alternative would be serialising to json and sending it back to a javascript templating snippet.

Categories

Resources