Getting Flask to alter selected value in HTML drop down - python-3.x

I'm writing a prototype front end for a home heating control system and when the user selects the room and day of the week from the drop down box, I want to be able to query the Redis database and retrieve the currently set temperatures for each hour and use this to set the selected value in the temperature drop down boxes. The user can then change a value or values and submit them. I have the submit and write working, just trying to get my head around setting the selected.
Here's the HTML:
{% extends "bootstrap/base.html" %}
{% block title %}Setting Room Temperatures{% endblock %}
{% block navbar %}
<div class="navbar navbar-fixed-top">
<!-- ... -->
</div>
{% endblock %}
{% block content %}
</head>
<body>
<h1>Room temperature configuration</h1>
<form action="{{ url_for('heating_config_form')}}" method="post" class="form-inline">
<div class="form-group">
<label for="roomselect">Room:</label>
<select class="form-control" name="roomselect">
<option value="dining">Dining</option>
<option value="kitchen">Kitchen</option>
<option value="lounge">Lounge</option>
<option value="study">Study</option>
</select>
</div>
<br>
<br>
<div class="form-group">
<label for="dayselect">Day :</label>
<select class="form-control" name="dayselect">
<option value="monday">Monday</option>
<option value="tuesday">Tuesday</option>
<option value="wednesday">Wednesday</option>
<option value="thursday">Thursday</option>
<option value="friday">Friday</option>
<option value="saturday">Saturday</option>
<option value="sunday">Sunday</option>
</select>
</div>
<br>
<br>
<div class="form-group">
<label for="1AM">1AM :</label>
<select class="form-control" name="1AM">
<option value="5">5</option>
<option value="6">6</option>
</select>
<label for="2AM">2AM :</label>
<select class="form-control" name="2AM">
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
<br>
<br>
<button type="submit" value="submitted" name="update">Update temperature </button><br>
<br>
</form>
<br>
</body></html>
{% endblock %}<html><head>
And here's the Python Flask code
from flask import Flask
from flask import request
from flask import render_template
from flask_bootstrap import Bootstrap
import redis
app = Flask(__name__)
app.debug = True
bootstrap = Bootstrap(app)
db = redis.Redis('localhost')
#app.route('/', methods=['GET','POST'])
def heating_config_form():
error=""
if request.method == 'POST' and request.form['update'] == 'submitted':
Room = (request.form.get('roomselect'))
Day = (request.form.get('dayselect'))
AM1 = (request.form.get('1AM'))
AM2 = (request.form.get('2AM'))
key = (Room + "_" + Day)
db.hset(key, "1:00", AM1)
db.hset(key, "2:00", AM2)
return render_template('set-room-tempv5.html')
else :
return render_template('set-room-tempv5.html')
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
I'm aware there are probably better ways of approaching this (WTForms?) but wanted to get a simple setting interface that I understand so I can prototype the system while developing a better front end, as I'm new to Flask and don't want the whole heating system to be dependent on my ability to pick this up :)
Adding the temperature to the database works fine, this is a typical monitor output from the redis-cli monitor
127.0.0.1:6379> monitor
OK
1482336847.287342 [0 127.0.0.1:34180] "HSET" "kitchen_tuesday" "1:00" "5"
1482336847.288042 [0 127.0.0.1:34180] "HSET" "kitchen_tuesday" "2:00" "5"
I was thinking that maybe something like the following context processor could help?
#app.context_processor
def utility_processor():
def retrieve_temp(sroom, sday, stime):
skey= (sroom + "_" + sday)
stemp = db.hget(skey, stime)
return stemp
return dict(retrieve_temp=retrieve_temp)
which would make the function retrieve_temp available to all templates - I think!
Then somehow, once the template is rendered, the default room and day is used to set the "selected" option on the time drop downs, and then every time the room and day drop downs are moved, the same happens again.
So if the default is dining and Monday given they are the first in the select options, the temperatures for 1AM and 2AM are retrieved and set as selected for these drop downs (there are more times and more temps, these have been deleted for brevity). If the room and/or the day is changed, it happens again.

OK - So just wanted to be sure on your question before I gave you information to solve the problem. The approach you've outline as an alternative in the comments is the easiest way to get this to work (e.g. intermediary page to make the first selection).
It's important to remember that any python or jinja2 code is only going to executed 1 time. Jinja2 uses some logic to render the HTML output and then Flask responds with a static HTML page to the browser, so you can use a context_processor to pass specific logic to jinja2, but once Flask renders the page any interactivity would need to be managed by javascript.
It looks like you are using Bootstrap, which includes jQuery javascript library. jQuery is great (in my opinion) because it handles some browser quirks for you in terms of how it interacts with and manipulates the DOM (e.g. the Document Object Model, javascript's representation of your HTML elements).
So, if you want to dynamically populate the second select you can design some code to asynchronously (e.g. without making another HTTP request or "reloading" the page) send data from the browser to a Flask endpoint to fetch the required options then update the front end. I'll give you an example:
HTML looks like this (I'm just adding ID's. The for tag is suppose to reference the ID field, not the name, and it also makes the javascript selection easier):
<div class="form-group">
<label for="roomselect">Room:</label>
<select class="form-control" name="roomselect" id="roomselect">
<option value="dining">Dining</option>
<option value="kitchen">Kitchen</option>
<option value="lounge">Lounge</option>
<option value="study">Study</option>
</select>
</div>
<div class="form-group">
<label for="dayselect">Day:</label>
<select class="form-control" name="dayselect" id="dayselect">
<option value="monday">Monday</option>
<option value="tuesday">Tuesday</option>
<option value="wednesday">Wednesday</option>
<option value="thursday">Thursday</option>
<option value="friday">Friday</option>
<option value="saturday">Saturday</option>
<option value="sunday">Sunday</option>
</select>
</div>
<div class="form-group">
<label for="1AM">1AM:</label>
<select class="form-control" name="1AM" id="1AM">
<option value="5">5</option>
<option value="6">6</option>
</select>
<label for="2AM">2AM:</label>
<select class="form-control" name="2AM" id="2AM" disabled>
<option value="5">5</option>
<option value="6">6</option>
</select>
</div>
Then, you need to add a javascript event listener and AJAX method, usually you do this in the bottom of the body of your HTML or in a linked file:
<script charset="utf-8" type="text/javascript">
// this is called a javascript closure. it also wont run the code until the page is finished loading
// and it protects the scope of your variables from other code you may later add to the page
$(function() {
var select_room = $('#roomselect'),
select_day = $('#dayselect'),
select_1am = $('#1am'),
select_2am = $('#2am');
select_room.on('change', function() {
// fires when room selection changes
getUpdatedSettings();
});
select_day.on('change', function() {
// fires when day selection changes
getUpdatedSettings();
});
function getUpdatedSettings() {
// data to send back to the server
var send = {
room: select_room.val(),
day: select_day.val()
};
// make the selections disabled while fetching new data
select_1am.attr('disabled', true);
select_2am.attr('disabled', true);
$.getJSON("/_get_updated_settings", send, function(response) {
// this send the room and the day select vals to the URL specified
// we will need to add a handler for this in Flask
// for the purpose of the example I am assuming the response will be
// a JSON object that has a dictionary of elements ("am_1" and "am_2")
// each of which is a list of values for the selects....
console.log(response); // good for QA!
// populate 1am
select_1am.empty();
$.each(response.am_1, function (index, value) {
select_1am.append(
$('<option>', {
value: value,
text: value
}, '</option>'))
});
// populate 2am
select_2am.empty();
$.each(response.am_2, function (index, value) {
select_2am.append(
$('<option>', {
value: value,
text: value
}, '</option>'))
});
// remove disabled now
select_1am.removeAttr('disabled');
select_2am.removeAttr('disabled');
});
}
});
</script>
Now, you need to add some Flask logic to handle this AJAX request:
from flask import jsonify
#app.route('/_get_updated_settings')
def get_updated_settings():
# good for debug, make sure args were sent
print request.args
day = request.args.get('day', 'default_if_none')
room = request.args.get('room', 'default_if_none')
key = (room + "_" + day)
output = {}
# I have no idea what this returns...just doing a list generator here assuming we get a list of values
output['am_1'] = [x for x in db.hget(skey, '1am')]
output['am_2'] = [x for x in db.hget(skey, '1am')]
return jsonify(output)
Ha...turned out a bit longer than I expected, but this should at the very least provide you with a straw man to get functionality like this working. Javascript definitely provides for a better user experience, but you can generally accomplish the same thing by having a series of intermediary forms that populate static pages.

Related

onSelect not triggering for React dropdown

I am very new to React so I am still learning a lot. No matter what I do, the onSelect just does not seem to work. I made a simple function that it should call. You can tell it's been called via the console.log but in the browser when I look at the console it just does nothing.
Here is the code for the dropdown:
import React,{useState} from 'react';
import SEPractices from "../dummydata/SEPractices"
const optionItems = SEPractices.map((SEPractice) =>
<option key={SEPractice.practice}>{SEPractice.practice}</option>
);
const Dropdown = () => {
const handleSelect=(e)=>{
console.log("Inside");
}
return (
<div>
<select onSelect={handleSelect}>
<option Default hidden>
Select an SE Practice
</option>
{optionItems}
</select>
</div>
)
}
export default Dropdown;
Try using onChange
<select value={selectValue} onChange={handleSelect}>
<option Default hidden>
Select an SE Practice
</option>
{optionItems}
</select>
And handleSelect like this:
const [selectValue, setValue] = useState("")
cosnt handleSelect = (e) => {
setValue(e.target.value);
}
<select value={selectValue} onChange={handleSelect}>
<option value="dummyData">
Select an SE Practice
</option>
{optionItems}
</select>
and don't use the Default and hidden, React will take care of default value why specifying value={selectValue} in element.
Also, we need to pass the value to the <option>, it will track based on the value.

Checkbox value is refreshed after page reload in node JS

"I am creating TODO list using Node as backend. after adding every new item, a checkbox is also generating in front of them so I can apply "CSS line-through" to let user know that item is done or of no use. But when I add another item, the page refreshes and that checkbox is unchecked as I am not storing that value anywhere. Can you tell me how to store the value of that checkbox in the backend?
HTML -
<div class="box" >
<% for (var i=0; i<newListItems.length; i++) { %>
<div class="item">
<input type="checkbox" id="checkBox">
<p> <%= newListItems[i] %> </p>
</div>
<% } %>
<form action="/" method="post" class="item">
<input class="inputBox" type="text" name="newItem" placeholder="New item" autocomplete="off" required="required">
<button type="submit" name="list" value=<%= listTitle%>> +</button>
</form>
</div>
Node JS -
const items = [];
app.post("/", function(req, res){
let item = req.body.newItem;
items.push(item);
res.redirect("/");
});
The answer involves a lot of code, so I will give you a set of steps that can help in your case.
You need to change your data scheme. Currently looks like you are just storing the string in an array of items. You need to change it to be array of objects. Each object should have the field task and done. So you could know which task is done or not.
app.post("/", function(req, res) {
let item = req.body.newItem;
items.push({ name: item, done: false });
res.redirect("/");
});
Next step will be adding an endpoint that will be changing the done field of an array item to true.
Then on a front-end you will need to write some JS code that will be sending an HTTP request to the endpoint that marks the task as done. You need to use AJAX call for that, for example, NPM package axios.
Change the template to reflect the changes to the data. e.g. instead of <%= newListItems[i] %> do <%= newListItems[i].name %> and add logic to render checked checkbox based on done property.
It worth to mention, that you should not store data in memory, because once the process is done, you will lose your data. It is okay for learning purposes, but in production, you should use a database.

Express: POST does not send data - var is not defined

In a view, i am trying to get the data that a user sent, via a form, from another view.
This is the view with the form (i have omitted some EJS stuff to avoid confusion):
<form action="/renderer" method="POST" id="sc-form">
<label for="model">Choose a model:</label>
<select name="model" id="model">
<% data.forEach(function(dat) { %>
<option value="<%= dat %>"> <%= dat %> </option>
<% }); %>
</select>
<input type="submit" value="Render the model!" />
</form>
As you can see, the user selects from a dropdown, and then data is POSTed on /renderer.
So, let's handle this POST request on routes.js:
app.post('/renderer', function(req, res) {
var myModel = req.body.model;
res.render('renderer.ejs', {data: myModel});
});
Pretty basic, we get the data that the form sent and we send it to renderer.ejs as parameters.
Finally, let's grab the data on /renderer.ejs (this is inside a <script> tag):
var modelName = <%- JSON.stringify(data) %>;
And i get this error:
data is not defined
But why is that? Data is the name of the variable that the router sent to the view, as parameter.
This is the third day that i am trying to make POST data appear on another view. If anyone could help i would really appreciate it.
I finally found this.
The form has to contain role="form", or else it wouldn't work.

How to get and post 2 objects from ng-model?

I have here a sample document from mongodb named chapters:
{_id:5c014c999cc48c3b0057988b, chapter_name:"AB"}
Controller: here is my function to get the chapters, and function to post to another db:
.controller('regCtrl', function($http, $location, $timeout, User, Bloodbankchapter) {
Bloodbankchapter.getBloodbankchapters().then(function(data) {
app.bloodbankchapters = data.data.bloodbankchapters;
});
this.regUser = function(regData) {
User.create(app.regData).then(function(data) {});
};
Then in my front-end to register:
<form name="regForm" ng-submit="register.regUser(regData); ">
<label>Chapter Name</label> <!--After I select the chapter name -->
<select ng-model="register.regData.branch" ng-options ="chapter._id as chapter.chapter_name for chapter in register.bloodbankchapters">
<option ng-repeat="chapter in register.bloodbankchapters" >{{chapter.chapter_name}}</option>
</select>
<label>Chapter ID</label> <!--chapter ID will appear here-->
<input type="text" ng-model="register.regData.branch_id" hidden>
<label>Chapter name</label> <!--How to make chapter name appear here?-->
<input type="text" ng-model="register.regData.branch_name" hidden>
<button ng-disabled="register.disabled" type="submit">Submit</button>
</form>
My problem here is after I selected the chapter name the second input was correct and it post the id into the db, but how can I get the chapter.name and post it to db?
I tried getting the text value from the select box and then appending it to a textbox like here: http://jsfiddle.net/kxqJN/, but when I register, the object id is being registered instead of chapter_name like this:
`{branch_id:5c014c999cc48c3b0057988b, branch_name:"5c014c999cc48c3b0057988b"}`
How can I display the name in the first box and the id in the second?
I bet you haven't tried what i suggested in the comments: chapter.chapter_name for chapter in register.bloodbankchapters track by chapter._id will lead you to have both values in your model:
angular.module('selectExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.register = {
regData: {
branch: {},
},
bloodbankchapters: [
{_id:'5c014c999cc48c3b0057988b', chapter_name:"AB"},
{_id:'5c014c999cc48c3b0057988c', chapter_name:"A"},
],
};
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="selectExample" ng-controller="ExampleController">
<select ng-model="register.regData.branch" ng-options ="chapter.chapter_name for chapter in register.bloodbankchapters track by chapter._id">
<option ng-repeat="chapter in register.bloodbankchapters" >{{chapter.chapter_name}}</option>
</select>
<div>{{register.regData.branch}}</div>
</div>
Try this way, you will get selected chapter object inside register.regData.branch
<select ng-model="register.regData.branch" ng-options ="chapter as chapter.value for chapter in register.bloodbankchapters">

Dynamic Dropdown in Node.js

Newbie to Node.js here:
What is an easy way to make dynamic dropdowns in node.js?
Basically, I have two tables: Skill and Skill_Category .
I want to select from the Skill_Category and be presented with the associated Skill.
I would assume I would need to use some template engine (to render in a webpage).
I tried researching a good amount on google, but didn't turn up anything that helped me.
If anyone can point me in the right direction?
Thank you!
So one dropdown will cause the other to show. Unless the second dropdown has hundreds of options you won't want to make a separate server side request to get it in. This means your logic should all be in the browser/client side.
This means you want your "Skill_Category" select box to have a JS function called to hide the current "Skill" select box and show the newly chosen "Skill" select box. They will all be rendered in html by your templating, but only one will ever be shown (display:block) while the others are hidden (display:none).
Sample HTML:
<select id="skill_category">
<option value="communication">Communication</option>
<option value="teamwork">Team Work</option>
<option value="technical">Technical</option>
</select>
<select class="skill" id="communciation">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select class="skill" id="teamwork">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
<select class="skill" id="technical">
<option value="1">One</option>
<option value="2">Two</option>
<option value="3">Three</option>
</select>
Sample Jquery code:
$('#skill_category').on('change',function(){
$('.skill').hide()
$('#'+this.value).show()
});
Update:
If you have a large list of options for the secondary select box then you can make an ajax (HTTP GET) request, and returning the lsit of dropdowns from your server as JSON.
You will probably have one of two scenarios: all of you skills in a static JSON file, or saved in a database.
Your node code will look like:
app.get('/skill_category/:skill', function(req, res){
//JSON file in the /public/skills directory
res.sendFile(__dirname + '/public/skills/'+req.params.skill+".json");
//or some database lookup followed by a json send:
var skills = someDatabaseLookup();
res.json(skills);
});
HTML
<select id="skill_category">
<option value="communication">Communication</option>
<option value="teamwork">Team Work</option>
<option value="technical">Technical</option>
</select>
<select id="skill">
</select>
while the jquery will now be:
$('#skill_category').on('change',function(){
$.get('skill_category/'+this.value,function(data){
for(var j = 0; j < length; j++)
{
$('#skill').contents().remove();
var newOption = $('<option/>');
newOption.attr('text', data[j].text);
newOption.attr('value', data[j].value);
$('#skill').append(newOption);
}
});
});
Do note this code is untested but should work

Resources