Add Javascript string variable INSIDE a django template variable - javascript

I have a chart that is populated by django template variables. I have an update chart function that updates the data after certain buttons are clicked. Instead of writing the same js function 7x, can I update the javascript function to call the correct data within a Django template variable?
Basically, the btn_id will either be A, B, C, D, E, or F and I want the django template variable to display the correct data based on the btn_id value.
var results = {{ all_test_results.A.curve_series.curve_series }}.map(function(val, i) {
return val === 'None' ? null : val;
});
becomes
const btn_id = this.id.slice(-1); # A, B, C, D, etc
var results = {{ all_test_results.{{ btn_id }}.curve_series.curve_series }}.map(function(val, i) {
return val === 'None' ? null : val;
});
I am sure there is a simple way to inject a js string into a Django template variable, right?

Simple, no
I am sure there is a simple way to inject a js string into a Django
template variable, right?
Here's the problem. {{ all_test_results.A.curve_series.curve_series }} is handled by the server, which is handled by Django, which takes this tag, and fills it in with the data and returns an HTML document back to the client. Now it is the client, that is the end user, who clicks on say a button C, but how does Django, who lives on the server know about this? It doesn't, and that's why this is not so simple.
Solution
When the user clicks C, you can have a simple AJAX request sent from the client to the server, with this information. The server can then send back the value of all_test_results.C.curve_series.curve_series (you will have to write a query for this, you wouldn't be able to use a variable in place of C here), which can be received by your JavaScript, so that you don't have to do this 7 times.
Details
# views.py
from django.http import JsonResponse
def get_result(request, letter):
return JsonResponse({'answer': [result of your query that uses the variable, "letter", which corresponds to the letter the user clicked, here]})
Add a path to this view
# urls.py
urlpatterns = [
...
path('get_result/<str:letter>', views.get_result, name='get_result')
]
In your template you have your buttons with values that can be targeted by JavaScript, and the container that will show the result:
<button type="button" class="result_btns" value="A" id="A">A</button>
<button type="button" class="result_btns" value="B" id="B">B</button>
<button type="button" class="result_btns" value="C" id="C">C</button>
<button type="button" class="result_btns" value="D" id="D">D</button>
<button type="button" class="result_btns" value="E" id="E">E</button>
<button type="button" class="result_btns" value="F" id="F">F</button>
<div id="result">Result will appear here</div>
And finally the JavaScript, which will make the AJAX request, get the response from the server, and finally update the HTML with the result:
async function get_result(e) {
// Make the AJAX request to the server:
const response = await fetch('/get_result/' + e.target.value);
// Once the response is received, turn it into json format:
const result = await response.json();
// Update the div you want to carry the result:
// NOTE: you should not use innerHTML here.
document.getElementById('result').innerHTML = result.answer;
}
// Get all the buttons of class result_btns from the HTML
let btns = document.querySelectorAll("button");
// For each button, create an event listener, that will
// send to the function get_result, it's value
btns.forEach((btn) => {
btn.addEventListener('click', get_result);
});

Related

HTML Button filtering based on two conditions

I have a button that is named "atCloudBttn", when you click it, it filters my data by status by all data that is pending.
How could I make this button also filter by another condition. The other condition would be IO by yes so, ('IO', 'Yes')?
<button role="button" name="atCloudBttn" class="btn btn-primary" (click)="changeFilter('Status', 'Pending')">
The change filter functions code is written in Typescript and I have included it below. I'm not 100% sure if I have to edit this code as well to reflect the change in structure.
ChangeFilter(field: string, term: string) {
this.filteredtable = true;
var filter = {};
filter[field] = term;
var title = ' ';
var activeColDef = this.columnDefs;
Why don't you write these two condition in a js function
and use click="functionname()" or add .addEventListener("click",functionname());

Split string when comma is inserted and add it to a string array

I have an input that converts strings into 'styled tags' when the user types a comma, then, when the form is submitted, the strings are pushed into an array called 'content'.
On the EJS views, I am printing the result like <%= course.content %> but the result I am getting is 'string1,string2,string3,string4' and what I am after is that when is pushed into the array, each string must be a different element:
content ['string1','string2','string3','string4']
only then it will render properly in my views by looping the array. I want to achieve this by javaScript or jQuery only, please.
UPDATE: this is how I am rendering in my view
<ul>
<% var i; for (i = 0; i < course.content.length; i++) { %>
<li><i class="fas fa-check"></i> <%= course.content %></li>
<% }; %>
</ul>
UPDATE: this is my route where this POST request is being done
router.post("/", middleware.isLoggedIn, function(req, res) {
Course.create(req.body.course, function(err, course) {
if (err) {
req.flash("error", err.message);
return res.redirect("back");
}
res.redirect("/courses/" + course.id);
});
});
SOLVED! by using split on the server side like this:
router.post("/", middleware.isLoggedIn, function(req, res) {
Course.create(req.body.course, function(err, course) {
if (err) {
req.flash("error", err.message);
return res.redirect("back");
} else {
var content = req.body.course.content.toString().split(",");
course.content = content;
course.save();
console.log(course.content);
res.redirect("/courses/" + course.id);
}
});
});
Here is another solution in javaScript using function beforesubmit() by #garry man see below.
codepen
Long way
Otherwise there's one work around is as many tags you enter, that much input elements you should generate.
For e.g. my input tags are foo,bar then 2 input tags will be generated like
Note square brackets below
<input id="hiddenInput" type="hidden" name="course[content][]" required>
<input id="hiddenInput" type="hidden" name="course[content][]" required>
This is long way.
Another way
If you submit form via AJAX, then you can manipulate data before submitting and convert string into array with the use of .split(',').
Another way (Server side)
Split string by , on server side.
Okay so the problem is that you are submitting a form containing a single input which can only contain string values.
HTML Form practice is to have a single input for each array element to be posted, like:
<input name="course[content]"/> //value 1
<input name="course[content]"/> //value 2
<input name="course[content]"/> //value 3
So, in order to achieve that, before submit, you can call this function that generates those elements for you:
function beforesubmit(){
let submitVal = document.getElementById('hiddenInput');
let values = submitVal.value.split(',');
//let's get the container of the params passed to post
let paramsContainer = submitVal.parentElement;
// remove the param containting the string with commas as we're generating new ones
paramsContainer.removeChild(submitVal);
for(let i =0; i<values.length;i++){
//for each value, add a submit param containing each value
let tmp = document.createElement('input');
tmp.setAttribute('type','hidden');
tmp.setAttribute('name','course[content]');
tmp.setAttribute('value',values[i]);
paramsContainer.appendChild(tmp);
}
document.getElementsByTagName('form')[0].submit();
}
in order to call this function, replace your submit input with this:
<input type="button" value="submit" onclick="beforesubmit()">
Using this code you can already see the POST request difference between before and after. In your codepen it sends a single parameter, while with this snippet of code you are going to send an array of course['content'].
Now it's all up to how you are going retrieve data server side, you should be retrieving the course['content'] param as an array.

Reload changed content created via ng-repeat without refreshing whole page

I'm currently trying to do the following, unfortunately without any success:
Basically, I have an array of objects, where for every object a button is created dynamically via the ng-repeat directive. When clicking a button, a new object is appended to the mentioned array (data will be sent to the server via api calls, the server will update the list internally and send the new array with the new object appended back to the view).
The same goes for deleting elements from that array.
Here's a little sample code of what I mean:
<span ng-repeat="x in allElements">
<button class="btn btn-primary" id="elementButtons">{{x.id}}</button>
</span>
There will be as many buttons as elements in $scope.allElements.
Then there's also this button, which basically causes the array to be reset to 0 elements:
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="deleteAllElements()">Clear all</button>
The $scope.deleteAllElements() function calls the api to delete all elements from the server and fetches the new (empty) array from the server assigning it to $scope.allElements.
Now, how can I update the view without having to refresh the whole page such that only the created buttons are reloaded?
Thanks for any answers in advance,
a.j.stu
EDIT:
This is the function that is called when an element is to be added:
$scope.addElement = function(elementName) {
if ($scope.checkElementName(elementName)) {
var data = {"name": elementName.toUpperCase(),
"type": /*retrieve type of element from html element*/}
$http.post("api/addElement/", JSON.stringify(data))
.then(function(response) {
$scope.allElements = response.data; //the api call adds the element in the backend and returns an array with all elements appended the new one. This SHOULD refresh the view of all element buttons, but doesn't.
})
.catch(function(response) {
console.log("something went wrong adding element " + elementName);
})
.then(function(response) {
$('#newElementModal').modal('hide'); //#newElementModal is the modal that pops up when clicking a button to add a new element. here the name and type of the element are set and used in the api call above to add the element. Basically, when the modal hides, the view of all buttons should be refreshed.
});
} else {
console.log("invalid identifier of element.");
}
As far as I've understood, the .then() calls are asynchronous. But, if there are only .then() calls following the api call, this should not be a problem, right?
You should not have to worry about resfreshing the page. If your view is connected to the controller whose $scope is updated by the API calls adding and deleting elements, your view will adapt and display the new content.
For what it's worth, here's a snippet showing how it could work. Minus the API calls that add / delete data.
angular.module('dummyApp', [])
.controller('DummyController', function($scope) {
$scope.allElements = [
{ id : 1, name : "Joe"},
{ id : 2, name : "John"},
{ id : 3, name : "Jane"},
{ id : 4, name : "Alice"},
];
$scope.deleteAllElements = function () {
// deleting elements empties the $scope.allElements array
$scope.allElements.length = 0;
};
$scope.addElement = function () {
var element = {
id : generateId(),
name : 'whatever'
}
// new elements are pushed into the $scope.allElements array
$scope.allElements.push(element);
};
function generateId() {
return Math.floor(Math.random()*1000);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.js"></script>
<div ng-app="dummyApp" ng-controller="DummyController">
<span ng-repeat="x in allElements">
<button class="btn btn-primary" id="elementButtons">{{x.id}}</button>
</span>
<br/><br/>
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="deleteAllElements()">Clear all</button>
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="addElement()">Add</button>
</div>
use trackby x.id to update the view without refreshing the whole page
Just assign new response data from your server to $scope.allElements and it will be refreshed without reloading page.

Create Array from HTML to send via AJAX

Im having a unusually hard time with this. If I have form that looks like this
HTML
<form id='logForm' method='post' action='seatingProcess.php'>
<span class='close' style='display:none' id='255' desk='9-4' deskval='2-3' changeType='REMOVE'></span>
<span class='close' style='display:none' id='255' desk='7-4' deskval='5-3' changeType='ADD'></span>
<span class='close' style='display:none' id='255' desk='8-4' deskval='8-3' changeType='CHANGE'></span>
<div class='btn btn-primary' type='submit' id='submit'>Submit</div>
</form>
What I want to happen is, when i click the button to submit the form, I want to have an array of the different elements in the span created so it can be sent via AJAX to process in PHP. How do you recommend I do this?
Also, this information will be dynamically created in this form based on user action. They will all be span's but will contain more or less the same attributes with a value attached to them. The idea is for php to receive the arrays and depending on what the attribute "changeType" says, it will generate the SQL script to perform that action. I may need help with this as well.
All I have for javascript. I dont have anything about making the array, thats what I need help with.The HTML above is an example output, but ill post one of the functions that generates the information. :
Javascript
function remDeskId(){
userObject = $dropObject.find('div.dragTest');
userObjectChange = 'REMOVESEAT';
userObjectID = userObject.attr('data-info');
userObjectDeskID = userObject.attr('data-id');
userObjectDeskIDVal = 0;
$('form#logForm').append("<span class='close' style='display:none' id='"+userObjectID+"' desk='"+userObjectDeskID+"' deskval='"+userObjectDeskIDVal+"' changeType='"+userObjectChange+"'></span>");
userObject.attr({"data-id":""}); //remove desk number for new user location
userObject.appendTo('#userPool');
}
$('#submit').click(function(){
// get the form data
// there are many ways to get this data using jQuery (you can use the class or id also)
//var formData = {
// };
// process the form
$.ajax({
type : 'POST', // define the type of HTTP verb we want to use (POST for our form)
url : 'seatingProcess.php', // the url where we want to POST
data : $('#logForm').serialize(), // our data object
})
// using the done promise callback
.done(function(data) {
// log data to the console so we can see
console.log(data);
// here we will handle errors and validation messages
});
// stop the form from submitting the normal way and refreshing the page
event.preventDefault();
});
Thanks in advance
You can iterate through the spans and create an array.
var spans = $('#logForm span');
var data = [];
$.each(spans, function(i, item){
var newItem = {};
newItem.id = $(item).attr('id');
newItem.desk = $(item).attr('desk');
newItem.deskval = $(item).attr('deskval');
newItem.changeType = $(item).attr('changeType');
data.push(newItem);
});

Move Through Object List

<div id="team-name">{{teams[0].name}}</div>
<button id="next">Next</button>
When the "next" button is hit I would like the team-name to be the next team name in the list, i.e. 0 becomes 1?
I have a feeling I need JS to do this - but I am not sure how I would use JS to do this.
Also, the list is generated from the server.
UPDATE
{{ }} is part of a templating system - Jinja2 to be precise.
The teams list is passed into the webpage through Jinja2 - so the webpage has access to the entire teams list - I hope that makes sense.
class Team(db.Model):
__tablename__ = 'Team'
name = db.Column(db.String(21))
matches_total = db.Column(db.Integer())
matches_won = db.Column(db.Integer())
matches_lost = db.Column(db.Integer())
Make a list containing the names available as team_names and update your template like this:
<div id="team-name" data-index="0" data-entries="{{ team_names|tojson }}">{{teams[0].name}}</div>
<button id="next">Next</button>
In case you are using flask which seems to be the case, pass this to your render_template() call:
team_names=[t.name for t in Team.query]
Then you can use the following jQuery snippet to do what you want:
$('#next').on('click', function(e) {
e.preventDefault();
var nameElem = $('#team-name');
var entries = nameElem.data('entries');
var index = (nameElem.data('index') + 1) % entries.length;
nameElem.text(entries[index]).data('index', index);
})
Note: This answer assumes the list is not too big.

Categories