I'm having issues calling functions from twig views in Symfony 4.4. This view is called UserList.html.view and it extends base.html.twig
The beginning of the file is as following :
{% extends 'base.html.twig' %}
{% block body %}
{% block javascripts %}
<script src="{{ asset('build/js/custom.js') }}"></script>
...
I also tried with Encore by adding an entry but it's not working. The only way to access external functions is to call them from the parent view which is not what I want obviously.
The error I get :
Uncaught ReferenceError: coucou is not defined
at HTMLButtonElement.onclick (VM3883 manageAccounts:183)
onclick # VM3883 manageAccounts:183
I read some other posts about this but none of them actually provided a working solution.
Thank you for your help !
Hello there and welcome to SO forums. It is hard to put a finger on your issue based on the provided pieces of code - but the twig block usage might be something that is not implemented / working as you assume. Namely the javascript block inside the implemented body block has no relevancy to the a similarly named block in base template (I assume that there is similarly named block there) because it is placed inside the body block which you are completely overwriting in this UserList.html.twig template.
A basic working twig structure would be something like this:
base.html.twig
<html>
<head>
...
</head>
<body>
...
{% block body %}
...
{% endblock %}
...
{% block javascripts %}
...
{% endblock %}
...
</body>
</html>
UserList.html.twig - please note the parent() call that makes sure that the block contents from the inherited template are included as well (i.e. if you have some generic jquery or some other generic js includes defined there) - without the parent() you will be defining the javascripts block contents completely again in this template.
{% extends 'base.html.twig' %}
{% block body %}
...
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('build/js/custom.js') }}"></script>
{% endblock %}
I hope everyone is doing great during this troubles times ! France started containment last week so I had plenty of time finding the solution.
Dumbest thing ever but I didn't know that..
In your js external files, you have to declare your functions like this :
window.myfunction = function myfonction(){}
That's all..
Could that potentialy cause security issues ? I don't know so I'm asking xD
Related
I have a question about, how to separate a loading specify javascript file of specify template twig file.
I got for example admin.html.twig which extends base.html.twig, in base i got
{% block javascripts %}
<script src="/assets/js/core/jquery.min.js"></script>
<script src="/assets/js/core/popper.min.js"></script>
<script src="/assets/js/core/bootstrap-material-design.min.js"></script>
<script src="/assets/js/plugins/perfect-scrollbar.jquery.min.js"></script>
<script async defer src="https://buttons.github.io/buttons.j"></script>
<script src="/assets/js/plugins/chartist.min.js"></script>
<script src="/assets/js/plugins/bootstrap-notify.js"></script>
<script src="/assets/js/material-dashboard.min.js"></script>
<script src="/assets/demo/jquery.sharrre.js"></script>
<script src="/assets/js/sparkline.js"></script>
<script src="/assets/js/plugins/chartjs.min.js"></script>
{% endblock %}
and I got next file like dashboard.html.twig which extends a admin.html.twig file, and my question is that in dashboard.html.twig file i got at the a little writed-self small javascript code, and this javascript of course use a jquery library but this library i loaded in base.html.twig file a next of my selfwrited script which is in dashboard.html.twig.
My question is, how i can for example load my small code of javascript (of course i can save it in separated file like mycode.js) but how to load only when this route of dashboard.html.twig file i used and after jquery is loaded ? becouse in another routers i dont need this mycode.js so I dont wanna put it to base.html.twig file in javascript block, any idea ?
If dashboard directly extends admin then u can do the following to ensure to load all the admin scripts and to add the dashboard specific script:
{% extends "admin.html.twig" %}
{% block javascripts %}
{{ parent() }} {# execute the parent block, thus loading all scripts in admin #}
<script src="/assets/js/dashboard/mycode.js"></script>
{% endblock %}
You can dived
{% extends "admin.html.twig" %} {# use only in case your all templates are at the same places like app/Resources/views/admin.html.twig #}
{% block javascripts %}
{{ parent() }} {# loads the parent javascript block, from the template you are extending in first line. #}
<script src="/assets/js/dashboard/code.js"></script>
{% endblock %}
in case you are trying to extend a template from another bundle then you can use this
{% extends "YourBundleName:Default:admin.html.twig" %} {# YourControllerRelativeName just in case your view structure is like views/Default/admin.html.twig#}
{% block javascripts %}
{{ parent() }} {# loads the parent javascript block, from the template you are extending in first line. #}
<script src="/assets/js/dashboard/code.js"></script>
{% endblock %}
to the better understanding you should read a little here:
https://symfony.com/doc/2.8/templating.html
Note:- in the documents you should change version as per your current version.
I would suggest the following solution.
Declare the following block in base.html.twig after {% block javascripts %}{% endblock %}
{% block javascript_page %}{# specific code for current page #}{% endblock %}
Then in your page you can include page specific scripts
{% extends "admin.html.twig" %} or {% extends "base.html.twig" %}
{% block javascript_page %}
<script src="/assets/js/pages/my_page.js"></script>
{% endblock %}
Although answer by DarkBee is also correct but in this way you don't have to worry about calling {{ parent() }} in each page.
I've in Django 1.11 a base.html which contains all the scripts references.
Then, I've another page.html that extendes base.html with {% extends base.html %} and {% block content %} / {% endblock content %} tags.
Well. In base.html I've a reference to Chartjs.js plugin. In page.html, if I try to call to Chart() function or just $ jquery, I get "function is not defined". If I open console debugger and try to call $ or just Chart(), it works. So I think that there's a problem with loading time. The page.html is rendered before the js are downloaded or requested!
How can I solve it? I've done it before, I don't know what could be the problem.
Thanks!
Without your code showing, hard to tell. Based on what you wrote, perhaps you forgot to put a block and block.super to get the parent (base.html's) Chartjs reference.
This should be in your page.html at the bottom after your {% endblock content %} tag. See example below (using DataTables as example since your code is not shown):
</div>
{% endblock content %}
{% block javascript %}
{{ block.super }}
<script type="application/javascript">
$(document).ready(function () {
$('#table').DataTable({
responsive: true,
});
});
</script>
{% endblock javascript %}
Base.html would have the js section enclosed in
{% block javascript %} …. {% endblock javascript %}
What is the best way to pass a variable from a Flask template into the Javascript file? Here is my code
I have a simple view in my webapp:
#webapp.route('/bars')
def plot_d3_bars():
return render_template("bars.html", calendarMap = calendarMap)
I have a templated HTML file that looks like this:
{% extends "base.html" %}
{% block title %} Bar View {% endblock %}
{% block content %}
{% with calendarMap=calendarMap %}
{% include "buttons.html" %}
{% endwith %}
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="/static/css/d3.tip.v0.6.3.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<!-- Custom codes for d3 plotting -->
<link href="/static/css/bars.css" rel="stylesheet">
<script> var calendarMap = {{ calendarMap|tojson }}; </script>
<script src="/static/bars.js"></script>
{% endblock %}
Previous answers told me that I could just jsonify the variable into a JSON object and I'll be able to use it. However, I want to use calendarMap inside of bars.js? but I am running into some scoping problems (i.e. bars.js doesn't recognized this calendarMap), what should I do instead?
Well, maybe it is too late, but here we go.
When you use a JavaScript code embedded in HTML code, this script will be rendered together with HTML. So any variable referenced in JavaScript, as a Flask variable, will be available in the page rendered.
When you use an external JavaScript file linked in HTML code, its code already exists, before the HTML be rendered. In some cases, I may say most of them, you aren't the owner of this file. So any variable referenced in JS file will not be rendered.
You may put this variable in HTML, via JS code, and consume this data with functions from foreign JS file.
Or you can render this JS file, before render the template, and use it. But I strongly recomend not to use this approach.
Is there a good, performant and/or recommended way to declare and provide JS dependencies for blocks in Django templates?
Basically what I want to do is this:
In a Django template file containing a block, declare: I need JS library X to function.
In the tag of the page, upon rendering the page, insert a script tag for this library, but never more than once.
The reasons:
Minimize number of unnecessary JS libraries included in the page to keep load time acceptable in old browsers. (some libs like jquery-ui are very heavyweight for old IEs)
Prevent potentially repeated loading of JS libraries, both for performance and bug-prevention reasons. This happens when you have repeated blocks or multiple blocks including the same JS lib.
I've seen some potential solutions to this but none of them were very compelling from a performance or clarity perspective, so far.
You could use the {% include %} template tag. If you load js into the header you could do something like this:
base.html
<head>
<title>XXXX</title>
...
<script type="text/javascript" src="{{ STATIC_URL}}js/jquery.js"></script>
...
{% block site_header %}{% endblock %}
</head>
other.html
{% extends "base.html" %}
{% block site_header %}
...
<script type="text/javascript" src="{{ STATIC_URL }}admin/js/urlify.js"></script>
..
{% endblock %}
You will have to adjust templates/pathes/etc. to your needs.
For this purpose, I personnaly use Django
Sekizai.
In my base template I have this block :
{% load sekizai_tags %}
<body>
# your own logic here
{% with_data "js-data" as javascripts %}
{% for javascript in javascripts %}
<script type="text/javascript"
src="{{ STATIC_URL }}{{ javascript }}" ></script>
{% endfor %}
{% end_with_data %}
</body>
Then, in my included or extending templates :
{% load sekizai_tags %}
{% add_data "js-data" "myapp/js/script.js" %}
Note you can define multiple blocks, and also use it for CSS, which is very
convenient.
Files added with "add_data" tag will never be repeated even if added several
times.
Yahoo's Best Practices for Speeding Up Your Website states:
Put Scripts at the Bottom
That there are two types of scripts in my Django application:
Scripts included in my base (e.g. inherited) template; and
Scripts written inside templates instantiated by templatetags
Scripts to support UI controls are necessarily a part of the template for their supporting template tag to handle stuff like unique IDs and to keep dependent code together.
The problem is that if I follow Yahoo's advice and put the libraries (#1) at the bottom of the page, 100% of the scripts included inline (#2) will fail because the libraries haven't been loaded and parsed yet.
I can't extend a {% block %} element in my base template because anything I do within the context of the templatetag won't propagate outside it -- by design, to avoid variable name conflicts (according to Django's documentation).
Does anyone have any idea how I can defer JavaScript from my templatetags' templates to render at the bottom of my base template?
I usually have a base template setup like this.
<html>
<head>
{% block js-head %} Tags that need to go up top {% endblock js-head %}
</head>
<body>
{% block header %} Header {% endblock header %}
{% block body %} Body goes here {% endblock body %}
{% block footer %} Footer {% endblock footer %}
{% block js-foot %} All other javascript goes here {% endblock js-foot %}
</body>
</html>
Then I can extend this base template and only override the blocks that I care about. I put all javascript that doesn't have to be in the header in js-foot and because it is at the bottom of the template it will load last. If you have have to move where your javascript loads you just need to move the js-foot block around in the base template.
Nothing fancy but it works.
Wrap the scripts that you're including inline in
jQuery(function(){ ... });
Which will execute when the the DOM is ready and scripts have been downloaded.
Another option might be to create some kind of custom template tag like:
{% inlinescript %}
// some javascript code.
{% endinlinescript %}
Which you could use to aggregate inline scripts as execution proceeds. You'd need aggregate this data as your template gets parsed - which gets tricky because template tags have different contexts and this is something you'd want to store in a global context in a custom variable, say inline_scripts.
I'd look at the way Django implements the various with ... as .. constructs in the default template library for an example of how to add your own variable to a context.
Then at the bottom of your page you could do {{ inline_scripts }}.
The easiest solution is the jQuery.ready(function(){}) / jQuery(function(){ }) (The two methods are synonymous).
Or you might want to reconsider Yahoo's advice. There are positive things about inlining your javascript - it can reduce FOUC / FOUBC (Flash of unbehavioured content). Yahoo tends to get kind of pedantic - (just look at the YUI API ;). If you need to rewrite parts of your application for what's going to be moderately perceptible performance improvement, it might not be worth it.
To do the script aggregation (originally based off captureas on django-snippets):
#register.tag(name='aggregate')
def do_aggregate(parser, token):
try:
tag_name, args = token.contents.split(None, 1)
except ValueError:
raise template.TemplateSyntaxError("'aggregate' node requires a variable name.")
nodelist = parser.parse(('endaggregate',))
parser.delete_first_token()
return AggregateNode(nodelist, args)
class AggregateNode(Node):
def __init__(self, nodelist, varname):
self.nodelist = nodelist
self.varname = varname
def render(self, context):
output = self.nodelist.render(context)
if context.has_key(self.varname):
context[self.varname] += output
else:
context[self.varname] = output
return ''
Usage:
{% aggregate inline_scripts %}
var foo = 'bar';
{% endaggregate %}
... template code
{% aggregate inline_scripts %}
var baz = 'bar';
{% endaggregate %}
... somewhere near the bottom of your page
{{ inline_scripts }}
There is an application django-sekizai just for that
This worked for me. Wait till the dom loads...
<script>
document.addEventListener("DOMContentLoaded", function (event) {
{% if my_var %}
doSomething({{myVar}});
{% endif %}
});
</script>