node.js ejs rerender div afte ajax - javascript

My issue is update div block after succes ajax call.
so I do it like that:
My div:
<div class="lists">
<% for(var i=0; i<searchItem.length; i++) {%>
<a class="list-link" href="#"><div class="list"><%= searchItem[i] %></div></a>
<% } %>
</div>
My jquery ajax call:
$.ajax({
url: '/search?val=' + encodeURIComponent($('#search-input').val()),
success: function(data) {
console.log('DATA-' + data);
new EJS({url:' /views/dropdown.ejs'}).update('lists',data)
}
});
My project structure:
But I got 404 error, because ejs cannot see dropdown.ejs template(http://localhost:3000/views/dropdown.ejs NOT FOUND). Why?
Another question what type of selector I need write in .update('lists',data) , Id or class of my div block?

I am assuming that you are using Express and views/dropdown.ejs is a view in your express backend.
If my assumption is correct, you can not directly access view of your backend. You have to create a router for this view or you can put this template in your static folder.
router.get('/template/:templateName', function(req, res){
res.render(req.params.templateName, { title: 'Template' });
});
// Or put your template inside public folder
app.use(express.static(path.join(__dirname, 'public')));
As for your second question regarding the selector for your DOM.It depends on what you want to achieve. Using class selector will get all the DOM with that class name and if give you an array of DOM, as for the ID (needs to be unique on a certain page) it will select the single DOM.
Looking at your sample, I think you need to use ID and target that DOM for updating the content.

Related

Python Flask Web App Navigation Without Page Refresh

I Want to develop a flask navigation bar like Google Contacts.
I Want to Render a particular HTML page inside the red box (as in the picture) when I click each of the navigation buttons (the green box as in picture) without refreshing the page.
I have already tried using
{% extends "layout.html" %}
As #Klaus D. mentioned in the comments section, what you want to achieve can be done using Javascript only. Maybe your question were
How can I send a request to my server-side (to get or fetch some information) and receive back a response on the client-side without having to refresh the page unlike the POST method usually does?
I will try to address the aforementioned question because that's probably your case.
A potential solution
Use Ajax for this. Build a function that sends a payload with certain information to the server and once you receive back the response you use that data to dynamically modify the part of the web-page you desire to modify.
Let's first build the right context for the problem. Let's assume you want to filter some projects by their category and you let the user decide. That's the idea of AJAX, the user can send and retrieve data from a server asynchronously.
HTML (div to be modified)
<div class="row" id="construction-projects"></div>
Javascript (Client-side)
$.post('/search_pill', {
category: category, // <---- This is the info payload you send to the server.
}).done(function(data){ // <!--- This is a callback that is being called after the server finished with the request.
// Here you dynamically change parts of your content, in this case we modify the construction-projects container.
$('#construction-projects').html(data.result.map(item => `
<div class="col-md-4">
<div class="card card-plain card-blog">
<div class="card-body">
<h6 class="card-category text-info">${category}</h6>
<h4 class="card-title">
${item.title_intro.substring(0, 40)}...
</h4>
<p class="card-description">
${item.description_intro.substring(0, 80)}... <br>
Read More
</p>
</div>
</div>
</div>
`))
}).fail(function(){
console.log('error') // <!---- This is the callback being called if there are Internal Server problems.
});
}
Build a function that will fetch the current page via ajax, but not the whole page, just the div in question from the server. The data will then (again via jQuery) be put inside the same div in question and replace old content with new one.
Flask (Server-side)
''' Ajax path for filtering between project Categories. '''
#bp.route('/search_pill', methods=['POST'])
def search_pill():
category = request.form['category']
current_page = int(request.form['current_page'])
## Search in your database and send back the serialized object.
return jsonify(result = [p.serialize() for p in project_list])
Thank you #CaffeinatedCod3r,#Klaus D and #newbie99 for your answers.
I Figured it out. instead of using Flask we can use Angular JS Routing for navigation.
Here is the example that i referred:
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<head>
<base href="/">
</head>
<body ng-app="myApp">
<p>Main</p>
Banana
Tomato
<p>Click on the links to change the content.</p>
<p>Use the "otherwise" method to define what to display when none of the links are clicked.</p>
<div ng-view></div>
<script>
var app = angular.module("myApp", ["ngRoute"]);
app.config(function($routeProvider, $locationProvider) {
$routeProvider
.when("/banana", {
template : "<h1>Banana</h1><p>Bananas contain around 75% water.</p>"
})
.when("/tomato", {
template : "<h1>Tomato</h1><p>Tomatoes contain around 95% water.</p>"
})
.otherwise({
template : "<h1>Nothing</h1><p>Nothing has been selected</p>"
});
$locationProvider.html5Mode(true);
});
</script>
</body>
</html>
By Using $locationProvider.html5Mode(true) i was able to remove the # from the URL.

How to use client side javascript variables as array index?

So I'm trying to teach myself to make a website. I'm using node and expressJS server side to send the template to the client like this.
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res) {
var cityData = require("../public/cityData/paris.json");
res.render('index', {
cityData : cityData
});
});
module.exports = router;
In this case I'm just passing a JSON file with information about the city of Paris that I want to display to the client. It has an array of image URLs that I can use for pictures. My issue is that I only want to show one image at a time and change the image to the next one when a key is pressed (or a button if necessary, but preference for key).
My issue comes on the client side, I can get the JSON object and generate the page based on it if I use a value eg [0] in the place of the "index" variable, but if I have it setup as shown, index is "undefined". So my question is, how does one make a variable that will exist clientside and allow me to rerender the page with a new picture, is that even possible? I've been reading stack overflow for hours and nothing seems to be what I want.
Thanks.
HTML code --->
<% include templates/head.ejs %>
<script "text/javascript" >
var index = 0;
</script>
<body>
<!-- This imports the naviagtion template I made -->
<% include templates/navigation.ejs %>
<div id="wrapper">
<% include templates/sidebar.ejs %>
<!-- Page Content -->
<div id = "page-content-wrapper">
<div class="container-fluid">
<!-- This imports an image -->
<div class = "row">
<div class = "text-center">
<img id = "imgArea" src = <%- cityData.imageUrl[index] %> class = "img-rounded col-xs-12 col-md-6" width = "device-width">
</div>
</div>
</div>
</div>
</div>
<!-- Menu Toggle Script -->
<script>
$("#menu-toggle").click( function(e) {
e.preventDefault();
$("#wrapper").toggleClass("menuDisplayed");
});
$(document).keydown(function(e){
index ++;
document.getElementById("imgArea").innerHTML = cityData.imageURL[index];
});
</script>
</body>
EJS runs on the server. It's the server who renders the webpage and then the variable is lost on the client. I would recommend a for loop to include all images when you render the index.ejs BUT put a class of hidden on all images except the first one. Then using some front-end javascript magic you can view images side by side. You can use jquery/bootstrap for that. ( Bootstrap carousel is a nice example ).
In order to store variables in the front-end you can use this:
<script>
var frontEndVar = <%= EJSvar %>;
</script>
Some times this will simply evaluate to [object Object] so you might want to first use JSON.stringify on the server side

Open link contents in django template

I have a django application and in one of the templates I have something similar to:
image
This code is called multiple times - for each memb there is an associated .svg image that can be accessed with this url. Of course at the moment, there is just a link on the word 'image' to a separate page with the .svg.
What I want is to have the .svg's loaded into the template page instead of a link out. What is the easiest/best way to do this?
I am relatively new to Python/Django but I understand the basic concepts as well as HTML/CSS, however, I have zero experience with JavaScript.
EDIT: The .svg's are not stored in the filesystem. There is a separate view (separate to the main one for the template I'm working on here) that goes a bit like this:
def svg_image(request, entry_nr):
svg_string = utils.DrawSVG.get_svg(entry_nr)
return HttpResponse(svg_string)
I then have the url, which is accessed in the HTML template code above:
url(r'^images/(?P<entry_nr>[0-9]+)/$', views.svg_image, name='svg_image')
{% load static %}
<p>
<img src="/location/images/{{memb.EntryNr}}" width="200"/>
</p>
While rendering the django template you need to pass the content_type
def myview(request):
svg_data = generate_some_svg_data()
return HttpResponse(svg_data, content_type="image/svg+xml")

Interweave EJS and Javascript variables inside <% tags

I need to use a Javascript variable (defined in the front-end) in some EJS code, as follows:
var selected = 1;
<% for (var i=0; i < supplies.length; i++) { %>
if (i == selected) {
console.log(supplies);
}
<% } %>
I'm using EJS, Express.js and socket.io. I could convert the Javascript variable to an EJS variable by sending a message to my Node.js server instance, but that's kind of silly... Is there a way to use Javascript variables inside EJS?
EDIT:
I want to access supplies, a javascript array, after the user selected an item from a drop down menu. When he selects this item, a javascript function with the above code needs to access some EJS. That's why I need to use a normal Javascript variable in EJS.
Can I pass a JavaScript variable into a template?:
It is possible to get more data into the template and re-render it, but not in the manner that you're thinking, and without making another request to the server (unless it's to get more data, not to get more HTML).
Solution:
This question is going to be difficult to answer without more details, so I'm going to make some assumptions about why you want to pass a selected value into an EJS template. I'll do my best to answer this with limited information about your goals.
It seems like your user is performing some action on the page, like selecting a cleaning supply, and you want to render the data differently, based on which element the user selected. To do this, you can re-render the template and pass in data that identifies which element is selected, using a view helper to apply a specific class to the selected element:
Here is the modified cleaning.ejs template, with the class added using the view helper:
cleaning.ejs:
<script>
// NOTE: even if uncommented, JavaScript inside a template will not run.
// var selected = 1;
</script>
<h1><%= title %></h1>
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<li>
<!-- apply a class to the selected element -->
<a class='<%= supplies[i].selected %>' href='supplies/<%= supplies[i].value %>'>
<%= supplies[i].value %>
</a>
</li>
<% } %>
</ul>
The rendered HTML looks like this:
<script>
/** NOTE: Script blocks will not fire in rendered templates. They are ignored
// var selected = 1;
</script>
<h1>Cleaning Supplies</h1>
<ul>
<li>
<a class="" href="supplies/Broom">
Broom
</a>
</li>
<li>
<!-- Note the selected element -->
<a class="selected" href="supplies/mop">
mop
</a>
</li>
<li>
<a class="" href="supplies/Hammer">
Hammer
</a>
</li>
</ul>
This view was rendered using the following JavaScript code:
// modified data structure so that array of supplies contains objects
// with the name of the item and whether or not it's selected.
data = {
"title":"Cleaning Supplies",
"supplies":[
{
"value":"Broom",
"selected":""
},
{
"value":"mop",
"selected":"selected"
},
{
"value":"Hammer",
"selected":""
}
]
};
// pass data into template and render
var html = new EJS({url: 'cleaning.ejs'}).render(data);
// add HTML to the DOM using a <div id="container"></div> wrapper.
document.getElementById("container").innerHTML = html;
As you can see, supplies[i].selected applies the selected class to the element that was marked as selected in the data structure. I modified the href value so that it accessed the object in the ith array item instead of the value of the array itself.
Now, when the selected item is modified, we simply modify the data variable, re-render the EJS template, and add it to the DOM.
With this CSS in the head of your HTML document, you'll see something similar to what's displayed below:
<style>
.selected { color:red; }
</style>
Why JavaScript in the template doesn't run:
The method that you're attempting to use to manipulate JavaScript values or use JavaScript values inside the EJS template won't work. This has to do mainly with the context of when the JavaScript is executed.
You're right to think that the EJS templates are compiled on the client-side. However, the JavaScript in the view helpers are executed independent of the JavaScript in the Global context. From looking at the ejs.js source code, it appears as if eval is used in the process.
Additionally, EJS returns the rendered HTML as a string, and the documentation for EJS instructs us to inject that rendered template string into the DOM using innerHTML:
document.getElementById("container").innerHTML = html;
No matter what view technology you're using, one of the fundamental truths of some browsers when it comes to JavaScript is this: Some browsers may not evaluate <script> blocks added to the DOM using innerHTML.
In other words, in my test template, when I tried adding a script tag to output the selected value to the console, I could see that the script block was added, but due to the way innerHTML works, it was not executed:
Example Template Demonstrates JavaScript won't run if added using innerHTML:
<h1><%= title %></h1>
<ul>
<% for(var i=0; i<supplies.length; i++) { %>
<span id="selected"></span><script>console.info('test <%= i %> = <%= supplies[i] %>');</script>
<li>
<a href='supplies/<%= supplies[i] %>'>
<%= supplies[i] %>
</a>
</li>
<% } %>
</ul>
Rendered HTML:
As you can see below, the console.log statements are present in the HTML. However, when added using innerHTML, they will not fire.
The approach to take with view technologies is to limit their use to just that, rendering the view. Keep your logic in the "regular JavaScript".
<h1>Cleaning Supplies</h1>
<ul>
<span id="selected"></span><script>console.info('test 0 = Brrom');</script>
<li>
<a href='supplies/Brrom'>
Brrom
</a>
</li>
<span id="selected"></span><script>console.info('test 1 = mop');</script>
<li>
<a href='supplies/mop'>
mop
</a>
</li>
<span id="selected"></span><script>console.info('test 2 = Hammer');</script>
<li>
<a href='supplies/Hammer'>
Hammer
</a>
</li>
</ul>
More examples and documentation can be found on EJS Templates on the Google Code Embedded JavaScript site.

Append Django template tag with Jquery

I want to build a menu where I can set one link highlighted using the {% block %} tag. I have something like this in my Javascript:
<loop>
$('#a-div').append('{% block ' + variable + ' %} <a href...</a> {% endblock %}')
<endloop>
In the source, this is displayed as "{% block Home %}"
How can I make JQuery not append this as a string but as a template tag?
You can't. At least not without making an AJAX request to a Django template. In your case, it would be slow and make unnecessary extra requests. It's just not worth it. You can insert snippets from Django templates via jQuery by using, for example, the jQuery load function. But you can't replace a specific {% block %} tag, because by the time jQuery runs, the template has already been processed (and references to block tags removed). But this is not a situation where you should be doing that in any case.
Why don't you rather highlight the menu with a CSS class? Here is my usual solution to this problem:
Create a file called base_extras.py in one of your templatetags folders. If you don't have one, create one in an appropriate folder.
Inside base_extras.py, paste this code:
from django import template
from django.core.urlresolvers import reverse
register = template.Library()
#register.simple_tag
def navactive(request, urls):
if request.path in ( reverse(url) for url in urls.split() ):
return "active"
return ""
Now, in your template, on your menus in your base template, do something like this:
<ul class="menu">
<li class="home {% navactive request 'home' %}">Home</li>
<li class="contact {% navactive request 'contact' %}">Contact</li>
<li class="signup {% navactive request 'signup' %}">Sign up</li>
</ul>
This will make that the menu where your URL currently is has the active class. Then, in your CSS, just add a special class for a menu item with active to look slightly different than the other menus.
ul.menu li.active {background: red; color: white;}
And if you happen to need to change the active menu with jQuery, you can just remove the active class on all menus, and add it to the newly selected menus:
$('ul.menu li').removeClass('active').find('.home').addClass('active'); // for example
You can't do that like that. The Django template tags are processed on the server side, before the page is even sent to the browser. Javascript (including jQuery) is, on the other hand, invoked in the browser, after the page has been received from the server.
What you can do is prerender the content of {% block %} tag to JS variable and use it in jQuery code:
var blockContent = "{% block Home %} ... {% endblock %}";
// ...
$("#a-div").append(blockContent);
If you need more than one block to choose from (as you seem to indicate in the code sample you've provided), you could resort to an array of prerendered blocks.
Your best bet is to create a proxy view that makes what is currently your AJAX request, processes the results like the javascript would, and then returns whatever you're trying to get from the Django templating system.
Then, instead of making the AJAX call you're currently making, you call your own view instead. Django does the processing in the view like it should, you get fine-grained control over what's returned to your javascript, and it's still only one (client-side) server call.

Categories

Resources