How to Change links in node.js - javascript

i've been trying for a while to get the tabs on my navigation bar to change (including the link) when a user logs in. The whole thing is a problem on the index page, as you should enter it both logged in and not logged in. i have a in my navigation bar where all tabs are listed via an anchor. This is my app.get of the index:
app.get('/', (req, res)=>{
const {userId} = req.session;
if(userId==undefined){
res.render('index');
}
else{
res.render('index');
}
});
and this Is my header :
<nav>
<div class="logo">Logo</div>
<div class="openMenu"><i class="fa fa-bars"></i></div>
<ul class="mainMenu">
<li>Startseite</li>
<li>Anmelden</li>
<li>Registrieren</li>
<li>Impressum</li>
</ul>
<div class="closeMenu"><i class="fa fa-times"></i></div>
</nav>
I want to change "Anmelden" to "Log out" with this :
<li><form id="abmelden" method="POST" action="/logout">Abmelden</form></li>
And I want to change "Registrieren" to "Dashboard" therefore the link of the a tag and the text must be changed.
Is there a way, where I can use a command like res.send() but to send the depends on, if the user is logged or not

One common technique is to dynamically insert a class on the body tag that is either loggedIn or notLoggedIn. You can then control the rest of your page purely with CSS.
You just insert both the loggedIn and not loggedIn tabs in your navbar and use CSS so one of them is always hidden based on what class is in the tag:
<body class="loggedIn">
or
<body class="notLoggedIn">
Then, you use the same page for both, but use CSS to hide/show things based on the state in the tag. You use conditionals in EJS to insert the right class for the tag based on the logged in state.
You can do this in your index.html page with an EJS conditional to dynamically construct the <body> tag with the right class in it:
<body
<% if (loggedIn) { %>
class="loggedIn" >
<%} else { %>
class="loggedOut" >
<% } %>
I'm not an expert on EJS so if this isn't perfect EJS syntax, you can hopefully see the concept here. When you render your template, you need to pass the loggedIn boolean to the rendering engine so it can dynamically adjust based on that.
Then, you add CSS that shows or hides:
/* hide/show based on login status */
.loggedIn .whenLoggedIn, .loggedOut .whenLoggedOut {display: block;}
.loggedOut .whenLoggedIn, .loggedIn .whenLoggedOut {display: none;}
Then, you put both tabs in your navbar and the class in the <body> tag combined with your CSS will hide/show one of them
<nav>
<div class="logo">Logo</div>
<div class="openMenu"><i class="fa fa-bars"></i></div>
<ul class="mainMenu">
<li>Startseite</li>
<li class="whenLoggedOut">Anmelden</li>
<li class="whenLoggedIn">Ausloggen</li>
<li>Registrieren</li>
<li>Impressum</li>
</ul>
<div class="closeMenu"><i class="fa fa-times"></i></div>
</nav>
Note: You could also use EJS conditionals to just generate the desired tab also so just one tab was generated. But, the technique shown here creates one EJS conditional and then everything else is just HTML/CSS which some people find to be simpler to manage. So, you could have multiple places in the page that have loggedIn and notLoggedIn HTML, but they all work off the class/CSS generates from the one EJS conditional.

By "tab" you mean changing the html page title ?
it's done with this (add it in your template) :
<html>
<head>
<title>title of the doc</title>
</head>
...
</html>
you can add if statements in your template to adapt things.
But it depends of the templating engine you choosed
for twig its done like that :
{% if userId %} <h1>im logged as user {{userId}}</h1> {% endif %}
dont forget to add userId in the render part like so
res.render('index', {userId});

In your code you first check if the user is logged in with:
if(userId==undefined){
If userID is undefined, that means the user is not logged in, so you send them index.js:
res.render('index');
Then you have an else statement for if the user is logged in, but in your else statement you send the exact same thing as you did for a non-logged in user:
else{ res.render('index'); }
So if you want a different menu for a logged-in user you probably need to send them different html.

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.

Use a variable set to ng-click to change template

Let's say I have a list of links and I want each of them to change the DOM on the current page
using ng-click and templating, how would I do that?
Edit: I guess what I am trying to understand is how to move as much of the logic away from the .html file and into my app.js file. I'm a little new to JS and Angular and don't know where or how to pass "active" to choose what I'd like to place inside
For example:
<ul>
<li>
<h1>foo1</h1>
</li>
<li>
<h1>foo2</h1>
</li>
<li>
<h1>foo3</h1>
</li>
</ul>
<active></active>
Where the active element displays only what template is set to the active one.
ie; the template associated with foo1 is displayed, and then if foo2 is clicked the template for foo2 replaces foo1
I am not sure what is the implementation of <active>, but you can use ng-include to implement what you are expecting
<active ng-include='active'></active>
Now create a client side or server side template with the name which matches variable active defined on scope and corresponding template would get loaded
A template definition could be like
<script type="text/ng-template" id="foo1">
<!-- html template for foo here-->
</script>
See ng-include documentation.
You could use ng-show/ng-hide. Basically something like this.
<div ng-show="foo1">
<p> This will only show when foo1 is true on your scope.
</div>
<div ng-show="foo2">
<p> This will only show when foo2 is true on your scope.
</div>
<div ng-show="foo3">
<p> This will only show when foo3 is true on your scope.
</div>
Also if you're wanting to keep the logic of the active = 'foo1' just change up the logic in the ng-show. Usually you want to keep logic out of the view though, so move that to your controller.

Appended content disappears after the change of URL

On my webpage I am using Flask microframework. To give you better understanding of my problem I have decided to include several of my files. Hopefully I won't discourage you.
Example bare in mind this example is incomplete due to complexity of my website. But I hope it will ilustrate the problem.
My script.py which runs the server:
from flask import Flask, request, render_template
app = Flask(__name__)
#app.route('/')
#app.route('/<name>')
def home(name=''):
return render_template('home.html', name=name)
#app.route('/user/')
#app.route('/user/<task>')
def user(task=''):
return render_template('user.html', task=task)
Then I have template.html:
<!-- some html code -->
<div id="navBar"></div>
<div id="mainContent">
{% block content %}
{% endblock %}
</div>
<!-- some html code -->
and user.html:
{% extends 'template.html' %}
{% block content %}
{% if task == 'homework' %}
{% include '/tasks/homework.html' %}
{% endif %}
{% endblock %}
And finally my script.js:
// some jQuery script
$('#button').click(function(){
$('#navTask').empty();
$('#navTask').append('\
<div class="task">\
Homework\
</div>');
});
// some jQuery script
Back to my problem! Basically it does this. Once you click the #button it appends #navTask (which is div located somewhere on the page) with some div with link to /user/homework. Once you click on Homework Flask recognises the change of URL and renders user.html so some part of the webpage changes. My problem is: once you click the link Homework the link Homework disappears too. I am not sure, how to fix it.
I thought, that by appending the div.task to #navBar it will changes throughout the webpage no matter the URL. But as far as I can tell it does not.
I am hoping for this behaviour - somewhere on the page there is a div, after clicking #button, the content changes and new link appears. When you click the link the content somewhere on the page changes, but the link stays where it was. The link might disappear e.g. only when you are on home page.
Note I am now quite sure, if my expectations are real. I could probably do this with just using jQuery, but because the main.py is much more complicated and I am using the data obtained from URL I still go this "way".
Maybe I misunderstood you, but you need to use AJAX request triggered from click on the link if you don't want to refresh all page content.
Whenever was committed not Ajax request to server - the page will be completely redrawn.
On the server side you could check whether it XMLHTTPRequest and if it is return JSON data or other convenient for you.
Maybe this little flask-jquery-example will help you.
P.S. Current location on JS side you can get by window.location.pathname or $(location).attr('href')

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