I am developing a simple pyramid application where I am using JQuery to do AJAX requests. I have until now had my javascript code within my chameleon templates. Now I want to extract my javascript into another location (e.g. as static resources).
My problem is that I find my javascript code relies on dynamically generated content like so:
$.post("${request.route_url('my_view')}",{'data': 'some data'}, function(html){
$("#destination").html(html);
});
The dynamic element being:
"${request.route_url('my_view')}"
Which is calling the route_url method of the request object within the template.
Is there a recognised pattern for separating such javascript files into their own templates and providing routes and views for them or do I simply keep my javascript in my page template?
Yes; you generally put context-specific information like expanded routes into the templates and access this information from your (static) JavaScript libraries.
Including the context info can be done in various ways, depending on taste:
You could use a data attribute on a tag in your generated HTML:
<body data-viewurl="http://www.example.com/route/to/view">
...
</body>
which you then, in your static JS code load with the jQuery .data() function:
var viewurl = $('body').data('viewurl');
Use a made-up LINK tag relationship to include links in the document head:
<head>
<link rel="ajax-datasource" id="viewurl"
href="http://www.example.com/route/to/view" />
...
</head>
which can be retrieved using $('link#viewurl').attr('href'), or $('link[rel=ajax-datasource]').attr('href'). This only really works for URL information.
Generate JS variables straight in your template, to be referenced from your static code:
<head>
...
<script type="text/javascript">
window.contextVariables = {
viewurl = "http://www.example.com/route/to/view",
...
};
</script>
</head>
and these variables are referable directly with contextVariables.viewurl.
Related
I am wondering if it is possible to name raygun variables using the BundleConfig file.
SO in my bundle I add my scripts as so:
bundles.Add(new ScriptBundle("~/master.js").Include(
"~/static/js/json2.js",
"~/static/js/jquery/jquery-ui.min.js",
"~/static/js/jquery/jquery.simplemodal.js",
"~/static/js/jquery/jquery.maskedinput.js",
"~/static/js/jquery/jquery.validate.js",
"~/static/js/jquery/jquery.inlineEditing.js",
"~/static/js/jquery/jquery.contextmenu.js",
"~/Scripts/raygun.js"));
I was wondering if it is possible to at all name my raygun.js reference as rg4js such that i could call it in my JS file where i want to use it. This is attempting to achieve the same thing as what they (Raygun) mention in their documentation by adding the following two script tags in the head and body, respectively.
<script type="text/javascript">
!function(a,b,c,d,e,f,g,h){a.RaygunObject=e,a[e]=a[e]||function(){
(a[e].o=a[e].o||[]).push(arguments)},f=b.createElement(c),g=b.getElementsByTagName(c)[0],
f.async=1,f.src=d,g.parentNode.insertBefore(f,g),h=a.onerror,a.onerror=function(b,c,d,f,g){
h&&h(b,c,d,f,g),g||(g=new Error(b)),a[e].q=a[e].q||[],a[e].q.push({
e:g})}}(window,document,"script","//cdn.raygun.io/raygun4js/raygun.min.js","rg4js");
</script>
<script type="text/javascript">
rg4js('apiKey', 'paste_your_api_key_here');
rg4js('enableCrashReporting', true);
</script>
As you can see, the first script defines the name (rg4js) whilst the latter one then uses it as a function name. Can I somehow achieve a similar thing with BundleConfig.cs, or would i need to instead insert those script tags into the shared views that act as templates for other parts of the website?
I've got a bit of Javascript that I only want to include on certain pages in my Phoenix application.
Right now I've got the Javascript inside a script tag in myapp/web/templates/post/form.html.eex.
I understand that I can move the JavaScript to web/static/js/app.js ...but I don't want to include the Javascript on every page (it's only required on 2 specific pages).
What's the best way to load this section of Javascript on certain pages in my application without duplication the code and violating the DRY principle?
1.
Put all that javascript from form.html.eex into its own file (maybe something like js/posts.js).
Add this at the bottom:
export var Post = { run: function() {
// put initializer stuff here
// for example:
// $(document).on('click', '.remove-post', my_remove_post_function)
}}
2.
In your app.html, under <script src="#{static_path(#conn, "/js/app.js")}"></script> add this:
<%= render_existing #view_module, "scripts.html", assigns %>
3.
Then, in your view (probably views/post_view.ex), add a method like this:
def render("scripts.html", _assigns) do
~s{<script>require("web/static/js/posts").Post.run()</script>}
|> raw
end
Conclusion
Now the javascript file post.js will only be loaded when the post view is being used.
Here is one way to achieve this.
The JavaScript you have in the script tag, you move that into a separate file.
You divide your "regular" javascript (to be included in every page) and this custom javascript (to be included in some specific pages) into separate directories. e.g. app/common/standard.js and app/custom/unique.js
You modify your brunch-config.js to as follows:
module.exports = {
files: {
javascripts: {
joinTo: {
'custom.js': /^app[\\\/]common[\\\/][\S*?]\.js/,
'app.js': /^app[\\\/]common[\\\/][\S*?]\.js/
}
}
}
Then you include app.js in all pages,
<script src="<%= static_path(#conn, "/js/app.js") %>"></script>
but custom.js only in page (or layout) templates that need it.
<script src="<%= static_path(#conn, "/js/custom.js") %>"></script>
Another way is to make use of page-specific classes/elements. For example, the following code in app.js will ensure that the code only gets executed on the lesson/show page, since only that page has an element with the id #lesson-container:
import { startLesson } from './lesson/show.ts';
if (document.querySelector('#lesson-container')) {
startLesson();
}
This is based on Gazler's comment on the question and is a slightly more general answer than the one submitted by cmititiuc. You don't strictly need to wrap your page-specific JavaScript code like in that answer, nor do anything beyond import your page-specific file in the page-specific script element.
Layout templates
Use Phoenix.View.render_existing/3 in your layouts like this:
<head>
<%= render_existing #view_module, "scripts.html", assigns %>
</head>
... or this:
<head>
<%= render_existing #view_module, "scripts." <> #view_template, assigns %>
</head>
For the first example, this will render a "scripts.html" template if one exists for the relevant view module.
For the second example, a "scripts." <> #view_template template, e.g. scripts.form.html, will be rendered if it exists.
If the 'scripts' template does NOT exist for a view module, nothing will be output in the page HTML.
View modules
For the first example using render_existing/3 in the layout template, you'd add code like this to the post view module:
def render("scripts.html", _assigns) do
~E(<script src="file.js"></script>)
end
... and for the second you'd add code like this:
def render("scripts.show.html", _assigns) do
~E(<script src="show-file.js"></script>)
end
def render("scripts.index.html", _assigns) do
~E(<script src="index-file.js"></script>)
end
Details
The difference between render_existing and render is that the former won't raise an error if the referenced template does NOT exist (and nothing will be output in the page HTML in that case either).
The ~E sigil provides "HTML safe EEx syntax inside source files" and is similar to (in most cases, or maybe even always) the corresponding code from cmititiuc's answer:
~s{<script>require("web/static/js/posts").Post.run()</script>}
|> raw
Conclusion
In general then, for any page for which you want to import specific JavaScript files via script elements in the page head (or at the end of the body), or link CSS files, or do anything to the page output in a portion thereof otherwise handled by the layout, you'd use render_existing in the layout template as above and then implement appropriate render clauses in the view modules for those pages.
And further, there's no reason why you couldn't use something like both of the two examples above so that, for any view module and its templates, you could both:
Include some script(s) (or CSS files or otherwise manipulate the HTML output of in a layout template) for all the view module templates (but not all templates for the entire app)
Include some script(s) (or ...) for only a single template
<script src="myscripts.js"></script>
Put your code in a new .js file. Include the script tag with a source to the file path in the relevant html files.
I'd like to have a Play template that is a JS file (as opposed to having <script> tags inside an HTML template). The reason for this is so that the script can be cached. However, I need to create a differences in the script depending on where it's included and hoped to do this with Play's template system. I can already do so if I use embedded scripts, but those can't be cached.
I found an existing question that also asks the same thing, but the answer is totally different (different goals).
That's easy, just... create view with .js extension, i.e.: views/myDynamicScript.scala.js:
#(message: String)
alert('#message');
//Rest of your javascript...
So you can render it with Scala action as:
def myDynamicScript = Action {
Ok(views.js.myDynamicScript.render(Hello Scala!")).as("text/javascript utf-8")
}
or with Java action:
public static Result myDynamicScript() {
return ok(views.js.myDynamicScript.render("Hello Java!"));
}
Create the route to you action (probably you'll want to add some params to it):
GET /my-dynamic-script.js controllers.Application.myDynamicScript()
So you can include it in HTML templite, just like:
<script type='text/javascript' src='#routes.Application.myDynamicScript()'></script>
Optionally:
You can also render the script into your HTML doc, ie by placing this in your <head>...</head> section:
<script type='text/javascript'>
#Html(views.js.myDynamicScript.render("Find me in the head section of HTML doc!").toString())
</script>
Edit: #See also samples for other templates types
I have recently discovered the new trend of including all .js script at the end of the page.
From what i have read so far seems pretty ok and doable with an exception.
The way I am working is using a template like:
<html>
<head>
<!-- tags, css's -->
</head>
<body>
<!-- header -->
<div id="wrapper">
<?php
include('pages/'.$page.'.php');
?>
</div>
<!-- footer -->
<!-- include all .js -->
</body>
</html>
Now, if I want to use this example on my page http://www.bootply.com/71401 , I would have to add the folowing code under my jquery inclusion.
$('.thumbnail').click(function(){
$('.modal-body').empty();
var title = $(this).parent('a').attr("title");
$('.modal-title').html(title);
$($(this).parents('div').html()).appendTo('.modal-body');
$('#myModal').modal({show:true});
});
But that would mean I either use that in every page - even if I do not have use for it, either generate it with php in the $page.'php' file and echoing it in the template file, after the js inclusion.
I am sure though, better methods exist and I don't want to start off by using a maybe compromised one.
Thanks!
Please avoid using inline scripts as they are not good maintainable and prevent the browser from caching them. Swap your inline scripts in external files.
Fore example you could put all your JavaScript in one file an check the presence of a specific element before initialize the whole code. E.g.:
$(document).ready(function(){
if($('.thumbnail').length) {
// your thumbnail code
}
});
A better way to execute "page specific" JavaScript is to work with a modular library like requirejs. You can modularize your scripts depending on their functionality (like thumbnails.js, gallery.js etc.) and then load the necessary script(s) depending e.g. on the existence of an element:
if($('.thumbnail').length) {
require(['ThumbnailScript'], function(ThumbnailScript){
ThumbnailScript.init();
});
}
The best way you can go is create a separate file for this code.
Let's name it app.js. Now you can include it under the jQuery inclusion.
<script type="text/javascript" src="app.js"></script>
This will prevent code repeat.
One more thing, pull all the code in $(document).ready(). Here is an example. So your app.js file will look like this:
$(document).ready(function(){
$('.thumbnail').click(function(){
$('.modal-body').empty();
var title = $(this).parent('a').attr("title");
$('.modal-title').html(title);
$($(this).parents('div').html()).appendTo('.modal-body');
$('#myModal').modal({show:true});
});
})
I'm writing a static web site that uses JQuery to make some AJAX calls to a RESTful API and populate the page with data.
The site functions correctly (and quickly), everything is good.
As I extend the site and add additional pages, I'm noticing that I'm duplicating certain regions on every page.
For instance, each page shares a common header element.
<header>...Some non-trivial content...</header>
Rather than repeat this definition on each page is there some mechanism, by which, I can define this section once and include it in each document.
Remember that the pages must be served statically but any standard complaint browser functionality can be utilised.
Is there a good way to do this, and what is it or, will I have to abandon DRY principles for this aspect of my client side code?
There's definitely some ways to achieve this. You could either do it using some features of your server-side language that allows to include the content of a page in another page, or if you do not have any server-side technology, you could simply put that code in it's own html document and load it's content using AJAX.
In jQuery it could look like:
$('#header').load('header.html');
However, if the content isin't static for all pages, you could always define a JS module that would be responsible to render this header. You module could make use of a client-side templating engine, like Mustache, Handlebars, etc. However you do not have to use any of these.
Here's a simple example:
DEMO
//in somefile.js, please note that you should namespace your modules
var Header = {
//default config
config: {
el: '#header',
title: 'Some title'
},
init: function (config) {
var cfg = this.config = $.extend({}, this.config, config);
$(cfg.el).html('<h1>' + cfg.title + '</h1>');
}
};
$(function () {
Object.create(Header).init({
title: 'Some other title'
});
Object.create(Header).init({
el: '#header1',
title: 'Yeah'
});
});
As I mentioned in the comment, this is how I do it:
main.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Main page</title>
<sript src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function(){
$('#commonsection').load('reusablefile.htm');
// which is eqvivalent to:
//
// $.ajax({
// url: 'reusablefile.htm',
// dataType: 'html',
// success: function(data){
// $('#commonsection').html(data);
// }
// });
});
</script>
</head>
<body>
<div id="commonsection"></div>
</body>
</html>
reusablefile.html:
<script>
(function($){ //separate scope to keep everything "private" for each module
//do additional javascript if required
})(jQuery);
</script>
<p>...Some non-trivial content...</p>
You could use jQuery's ajax as to load the header file. In each file you could load the html like so:
$('#header').load('header.html');
Since you're already using AJAX calls to populate your site with data, you could do the same for the common regions.
Just store the HTML for those regions in a separate file and load it in the page with AJAX. Also, you can work with caching using the Cache-Control headers on that file so you don't reload the entire content from the server with each page load.
If you're using straight HTML, you could do it with a SSI include command or by creating a template page and including it in jQuery. Both of these links might help you
Include another HTML file in a HTML file
and
http://www.htmlgoodies.com/beyond/webmaster/article.php/3473341/SSI-The-Include-Command.htm
It looks like this in modest:
main.xhtml
<?xml version='1.0' encoding='UTF-8'?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<include>reusablePiece</include>
</head>
<body>
<reusablePiece/>
</body>
</html>
reusablePiece.xml
<header>...Some non-trivial content...</header>
Very simple would be the jQuery .clone() function.
If you have more complex content I recommend looking at Handlebars.js which is a full fledged JS templating engine.