Including Leaflet.js in a Django template - django

I am writing a Django app that displays properties of certain objects that have latitute and longitude associated with them. When someone clicks on an object name, I'd like to be able to show them the map of where the objects is. I tried including leaflet.js's map directly into my object detailed view's template like this:
{% extends "base_generic.html" %}
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.3.1/dist/leaflet.css" integrity="sha512Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin=""/>
{% block content %}
<div id="mapid" style="width: 10px; height: 10px;"></div>
<script src="https://unpkg.com/leaflet#1.3.1/dist/leaflet.js" integrity="sha512/Nsx9X4HebavoBvEBuyp3I7od5tA0UzAxs+j83KgC8PU0kgB4XiK4Lfe4y4cgBtaRJQEIFCW+oC506aPT2L1zw==" crossorigin=""></script>
<script>
var mymap = L.map('mapid').setView([51.505, -0.09], 13);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: 'mapbox.streets',
accessToken: '<my-token-here>'
}).addTo(mymap);
</script>
{% endblock %}
But this gives me the weirdest looking map - it's split into several pieces and I seem to be able to move it freely around the web page into random spots (see below and scroll all the way down, you'll see the break in the middle). Can anyone tell why?

Related

Removing loading screen and spinner after all contents of the window have loaded but before iframes have finished loading

I am using a loading screen with a spinner that is displayed before all contents in the window have loaded. It works well on all pages and the window loads very fast on webpages with less content but on one of my pages, I am loading many iframes that embed youtube videos. $(window).on('load', function(){}); doesn't trigger until all contents have loaded, including iframes. That means that loading takes a long time and the loading screen with the spinner is shown long after the browser has finished loading the HTML, CSS, JS, and all images. I want to use skeleton loading for the iframes after the HTML, CSS, JS, and all images have loaded to cut down on perceived load time. Is there a way to tell that the rest of the window has fully loaded but the iframes are still loading? This is what I am currently doing to remove the loading screen with the spinner:
<html>
<head>
</head>
<div class="spinner-wrapper">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<body>
{% for video in range(videos|length) %}
<iframe class="yvideo" src="{{ "https://www.youtube.com/embed/%s" + videos[video]}.get("url") }}"></iframe>
{% endfor %}
<script type=module src="{{ url_for('static', filename='js/video.js') }}"></script>
</body>
</html>
videos.js:
$(window).on('load', function() {
preloaderFadeOutTime = 300;
function hidePreloader() {
var preloader = $('.spinner-wrapper');
preloader.fadeOut(preloaderFadeOutTime);
}
hidePreloader();
});
There can be multiple ways to solve this issue:
One simple approach can be to keep source of iframes empty initially and dynamically set source of iframes upon window.load.
Here sample code:
<script>
$(window).on('load', function() {
document.getElementById('myIframe').src = "your URL";
document.getElementById('myIframe2').src = "your URL";
preloaderFadeOutTime = 300;
function hidePreloader() {
var preloader = $('.spinner-wrapper');
preloader.fadeOut(preloaderFadeOutTime);
}
hidePreloader();
});
</script>
<iframe id="myIframe" src="" ></iframe>
<iframe id="myIframe2" src="" ></iframe>
EDIT:
If you cannot change the video.js file but can create your own js files which can interact with html on the page then do below in your custom js file:
//your external js
// first get collection of your iframes by class
var listOfIframes = document.getElementsByClassName("yvideo");
for (let item of listOfIframes) {
item.src = ""; // set them to empty.
}
window.addEventListener("load", function() {
//once page is loaded then populate the source with your json file.
for (let item of listOfIframes) {
item.src = "URLs from JSON";
}
});
Second approach:
Instead of calling hidePreloader() only at window.load. You can also check for the rest of the items in your page that if they are loaded or not. Once they are loaded, then you can use call hidePreloader()

Is it possible to load bootstrap modal directly with flask

In flask we can render a page using render_template('home.html, data=data).
In the same way is possible to load bootstrap modal without having to use javascrpt?
For e.g if an element is clicked in the page,
<span class="example" data-toggle="modal" data-target="#showExample" Testing</span>
A call is made to function in flask function and it trigger the modal pop-up and pass data that can used to populate field
If i'm understand what you mean, you can do this in JavaScript (just paste it in bottom of your page, in the tag "script":
document.getElementById('showExample').addEventListener('click', function() {
// Do some stuff, that you need to do...
});
Or, if you want, you can do this without JavaScript (i guess that you using Jinja2):
Insert it into your page template:
{% if show_example_modal %}
<span class="example" data-toggle="modal" data-target="#showExample" Testing</span>
{% endif %}
Python view code:
#app.add_route('/something/')
def something():
return render_template("something.html", show_example_modal=True)

Why does d3 always append to the body instead of my selection?

I am using d3js in a django web app. I have a static js script that contains a function to render a visualization given some data (passed in the context) and a selection (e.g. histogram(data,selection)). Why would d3 ignore the passed selection and directly append to the body?
This is a simplified version of my code.
Template:
{% load staticfiles %}
<script type="text/javascript" src="{% static 'd3.js' %}"></script>
<script src="{% static 'myd3vis.js' %}"></script>
<body>
<div class="vis"></div>
...
</body>
<script type="text/javascript">
mselection = d3.select(".visualization")
data = {{data|safe}}
histogram(data,selection)
</script>
In myd3vis.js file I define the histogram(data,selection) function, which contains something like this:
svg = console.append("svg").attr({"class":"visualization",
"width": 100,
"height":200})
...
When the template loads, the visualization svg is always appended to the body, instead of being appended to the <div class="vis"> element. Any idea why this would happen?
In most browsers’ JavaScript environments, console is a global object unrelated to the DOM – it provides debugging functions (e.g. console.log())… unless you’re clobbering it with a d3 DOM selection elsewhere, console.append() is unlikely to behave the way you seem to think it will.
Replace console with a bona-fide d3 selection, like:
var svg = d3.select('div.vis').attr(…); // et cetera
and inspect this object, to ensure your DOM logic is sound.

Ember.js: Complex view layout, what's the proper approach?

We're building app that allows users to post messages to various social media outlets. Our designer has created a series of interactions which allow users to change various settings in their application by use of sliding panels. I've done a quick screen cap to illustrate:
http://screencast.com/t/tDlyMud7Yb7e
The question I have is one of architecture. I'm not sure whether I should be using a View or a Controller (or both) to store some of the methods these panels will contain. Here's the HTML for the panels. They're not currently in a script tag or view:
<div id="panel-account-settings" class="panel closed">
<div class="panel-inner">
<i class="icon-cancel"></i>close
<h3>Account Settings</h3>
Google Analytics
Link Shortening
Disconnect Account
</div>
<div id="panel-google-analytics" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Google Analytics</h3>
<div class="toggle">
<label>Off</label>
</div>
<p>We <strong>won't</strong> append stuff to your links, so they <strong>won't</strong> be trackable in your Google Analytics account.</p>
<img src="{{ STATIC_URL }}images/ga-addressbar.png" />
</div>
<div id="panel-disconnect" class="panel-inner">
<i class="icon-arrow-right"></i>back
<h3>Disconnect This Account</h3>
<p>If you disconnect this account you will lose all the metrics we tracked for each message. Are you absolute sure you want to get rid of them?</p>
<div class="button-group">
Disconnect
</div>
</div>
</div>
The gear icon shown in the video is contained with the accounts template
<script type="text/x-handlebars" data-template-name="accounts">
{{#each account in controller}}
<div class="avatar-name">
<p>{{account.name}}</p>
<p>#{{account.username}}</p>
<i class="icon-cog" {{action "openPanel" Social.SettingsView account }}></i>
</div>
{{/each}}
</script>
which has a bare bones controller
Social.AccountsController = Ember.ArrayController.extend({
openPanel: function(view,account){
console.log(view,account);
$(this).parents(".item-account").addClass("active");
$("#panel-account-settings").prepareTransition().removeClass("closed");
}
});
as well as a Route and a Model. Given the interaction I'm looking to accomplish, my question is where should I be putting the pieces and parts? At a minimum I need to pass in the current Account model so that I know which account I'll be applying changes to. I thought about creating a mainPanel view which would contain the other view...something like this:
<script type="text/x-handlebars" data-template-name="panelView">
<div id="panel-account-settings" class="panel closed">
{{ partial "panelSettingsView" }}
{{ partial "panelAnalyticsView" }}
{{ partial "panelDisconnectView" }}
</div>
</script>
and then the action helper on the gear icon could pass in the account AND the required view. But I'm not sure if that's the right approach. I'd appreciate some input or suggestions. Thanks.
UPDATE 1
Ideally I'd like to eventually load in the content of each panel via AJAX but that's a want to, not a need to.
UPDATE 2
I tried creating a PanelView which would contain the logic on which panels to load:
Social.PanelView = Ember.View.extend({
tagName: 'div',
classNames: ['panel-inner'],
openPanel: function(view,account){
console.log(view,account);
}
});
But when I tried to call it from the gear icon I got an error. This:
<i class="icon-cog" {{action openPanel target="Social.PanelView" }}></i>
Threw this error:
Uncaught Error: assertion failed: The action 'openPanel' did not exist on Social.PanelView
Isn't that the correct syntax?
UPDATE 3
Adding version information:
DEBUG: Ember.VERSION : 1.0.0-rc.1
DEBUG: Handlebars.VERSION : 1.0.0-rc.3
DEBUG: jQuery.VERSION : 1.9.1
The best practice is to always put any DOM- or UI-related logic into your view, and leave data representation to the controller (i.e., a reference to a 'selected' item in the controller is a common example).
Your Social.AccountsController.openPanel method has logic that touches the DOM, which is entirely a view concern. A good start would be to move that logic into the view (Social. SettingsView ?).
It'd be a bit easier to understand your goals and offer more suggestions if you had a jsfiddle of what you have so far.
EDIT: Another good practice is to decompose things into very small objects. So you could explore having a selectedAccount ObjectController whose content is the currently chosen Account (and a corresponding View for it).

Auto-arrange <li> elements into equal columns

I have several values I'm rendering as <li> elements on my template and I want to further this by having them arranged into equal (or near-equal columns) automatically in the template. How can I achieve this?
So far I'm rendering all the values as
<ul>
{% for feature in features %}
<li>{{ feature }}</li>
{% endfor %}
</ul>
What about styling each li with style="float:left; width: {% width %}px;"
All you have to do is calculate the width parameter... eg width = 1000/features.length;
In newer browsers you can achieve this behaviour using CSS:
ul {
-moz-column-count: 2; -moz-column-gap: 12px;
-webkit-column-count: 2; -webkit-column-gap: 12px;
column-count: 2; column-gap: 12px;
}
Unfortunately, IE doesn't play along (as of today). To work around this, I wanted to achieve the same effect using javascript. At first, I tried Columnizer, but that inserts divs into those innocent ul elements, which I didn't like.
So I went to build our own little plugin to do exactly what we need: ColumnizeLists, part of our jQuery extensions library.
Include the following script tag into your page:
<script src="https://github.com/teamaton/jquery-xt/blob/master/columnize-lists/tn.columnizelists.jquery.js"></script>
(or a local copy) and simply call
$('ul').columnizeLists({ useHeights: true })
and you're done :-)
Hope this helps.