Rendering a text field based on data entered without refreshing the page - javascript

I am trying to display a User's name on top of a box where they enter their Employee # in a form, without having to refresh the page.
For example, they enter their # and then after they click/tab onto the next field, it renders their name on top, which comes from the database, so the user knows they've entered the correct info. This name is stored in a separate model, so I try to retrieve it using the "id/number".
I am not too familiar with AJAX but after reading a few similar questions it seems like an AJAX request would be the most appropriate way to achieve this. I tried to make a function get_employee_name that returns the name of the person based on the way I saw another ajax request worked, but I'm not sure how to implement this so it displays after the # is entered.
My page currently loads, but when I check the network using F12, there is never a call to the function/url that searches for the name to display it on the page. I'm not sure where I might be missing the part that connects these two areas of the code, but I have a feeling it has to do with the html tag where the call is supposed to happen, as I am not too familiar with html and Django.
models.py
class EmployeeWorkAreaLog(TimeStampedModel, SoftDeleteModel, models.Model):
employee_number = models.ForeignKey(Salesman, on_delete=models.SET_NULL, help_text="Employee #", null=True, blank=False)
work_area = models.ForeignKey(WorkArea, on_delete=models.SET_NULL, null=True, blank=False)
station_number = models.ForeignKey(StationNumber, on_delete=models.SET_NULL, null=True, blank=True)
This is the model where the name is stored
alldata/models.py
class Salesman(models.Model):
slsmn_name = models.CharField(max_length=25)
id = models.IntegerField(db_column='number', primary_key=True)
I was reading I can add to the "attrs" in the widget an 'onchange' part, but I am not too familiar with how to approach this and tying it to the ajax request from forms and not the html.
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
views.py
def enter_exit_area(request):
form = WarehouseForm(request.POST or None)
if form.is_valid():
# Submission stuff/rules
return render(request, "operations/enter_exit_area.html", {
'form': form,
})
def get_employee_name(request):
employee_number = request.GET.get('employee_number')
try:
employee = Salesman.objects.get(id=employee_number)
except Salesman.DoesNotExist:
return JsonResponse({'error': 'Employee not found'}, status=404)
employee_name = employee.slsmn_name
return JsonResponse({'employee_name': employee_name})
urls.py
urlpatterns = [
url(r'enter-exit-area/$', EnterExitArea.as_view(), name='enter_exit_area'),
path('get-employee-name/', views.get_employee_name, name='get_employee_name'),
]
The ajax request I tried to create is at the end of this html. I modified a similar request I found, but it does not actually display anything on the screen, not sure if I'm missing an area where the request is actually never being called, as I am not too familiar with how these types of requests work.
enter_exit_area.html
{% extends "base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" novalidate >
{% csrf_token %}
<div>
<h1 id="employee_name">{{ employee_name }}</h1>
<div>
{{ form.employee_number.help_text }}
{{ form.employee_number }}
</div>
<div>
{{ form.work_area.help_text }}
{{ form.work_area }}
</div>
</div>
<div>
<div>
<button type="submit" name="enter_area" value="Enter">Enter Area</button>
</div>
</div>
</form>
<script>
$("#id_employee_number").change(function () {
var employee_number = $(this).val();
var url = $("#warehouseForm").data("employee-name");
$.ajax({
url: url,
type:'GET',
data: {
'id': employee_number
},
success: function (data) {
var employee_name = data['employee_name'];
$('#employee_name').text(employee_name);
},
error : function (data) {
var error_message = data['error'];
$('#employee_name').text(error_message);
}
});
});
</script>
{% endblock main %}
What could be causing nothing to render on the page? Is there a call missing in the html portion? I don't know if <h1 id="employee_name">{{ employee_name }}</h1> is the proper way to call the function for display.

Well, you don't need AJAX for that, if all you want is just sync name on same page, you can do something like this.
// find elements
var banner = $("#employee_detail")
var input = $("#myTextbox")
// handle click and add class
input.keyup(function() {
banner.html(input.val());
})
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#id_employee_number {
background: red;
border-radius: 4px;
padding: 20px;
font-size: 25px;
text-align: center;
transition: all 0.2s;
margin: 0 auto;
width: 300px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="id_employee_number">
<p>Employee name</p>
<input type="text" id="myTextbox" />
<br />
<span id="employee_detail">
</span>
</div>
I hope that helps.
Update
I was just trying to explain how AJAX could work out for you. However, given your circumstances, in stead of doing.
input.keyup(function() {
banner.html(input.val());
})
you can do something like this.
input.keyup(function() {
$.get("/get-employee-name", function(data, status) {
employee = JSON.parse(data);
banner.html(employee.employee_name);
});
})
Lemme know, if that helps. and my apologies for responding very late, hope am not too late.

Related

"This field is required" error for FormSet when requesting payload, but works fine with Form

Edit: I seem to have figured out the next step in fixing this, as the actual name for each variable in the payload needs to be how I set it to be in forms. Will update if I solve it. Additionally, if I change it to a GET request, it works fine, but I have no idea why Django does not work with the POST request.
Need some help with this. If I pass a formset to a html template and request its payload with a POST request, I get this error
Field Error: primary <ul class="errorlist"><li>This field is required.</li></ul> Field Error: symbol <ul class="errorlist"><li>This field is required.</li></ul> Field Error: secondary <ul class="errorlist"><li>This field is required.</li></ul>
For the formset, the forms are dynamically added or deleted onto the page, but there will always be a single form on the page when the page is loaded. And for the other dynamically added forms, they get the same error as well.
But when I pass a single form to the html template, I get the POST payload just fine.
views.py
def advanced(request):
form = formset_factory(Search)
if request.method == 'POST':
formset = Search(request.POST)
for field in formset:
print("Field Error:", field.name, field.errors)
return render(request,"advancedsearch.html", {"formset":form})
forms.py
indicator = [
('', ''),
('high', 'Estimated high'),
('low', 'Estimated low'),
('median', 'Estimated median'),
('realprice', 'Real Price'),
]
symbol= [
('', ''),
('>', 'higher than'),
('<', 'less than'),
('=', 'equal to'),
]
class Search(forms.Form):
primary = forms.CharField(label='a', widget=forms.Select(choices=indicator))
symbol = forms.CharField(label='b', widget=forms.Select(choices=symbol))
secondary = forms.CharField(label='c', widget=forms.Select(choices=indicator))
advancedsearch.html
<form method="POST" action="">{% csrf_token %}
{% for x in formset %}
<div class = "d-flex flex-row justify-content-center bd-highlight mb-5">
{{ x.as_p }}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary" >Search</button>
</form>
Form Data example
csrfmiddlewaretoken: Sc2bMfDJr2qQ9rqeOxd3YnVpB37d36ZkQ85OfGaUL7vD61IyGzNiVDn6c5vydKSX
form-0-primary: low
form-0-symbol: >
form-0-secondary: low
Two ways came up in mind for this.
Switch form.CharField to form.ChoiceField
# forms.py
...
class Search(forms.Form):
primary = forms.CharField(label='a', widget=forms.Select(choices=indicator))
symbol = forms.CharField(label='b', widget=forms.Select(choices=symbol))
secondary = forms.CharField(label='c', widget=forms.Select(choices=indicator))
Ture form field required to False, check this question

Implement jQuery in a slider that uses dynamic data

I am trying to add a slider with dynamic data to my django project using jQuery and ajax to do it, I received help to make the previous and next button which gives the ability to swipe through profiles, but I am now focusing on the previous one, in the process I realized that there is a NoReverseMatch error in the code and I don't know how to fix them because I am very new at jQuery and ajax.
views.py
def matesmain(request):
contents = Mates.objects.all()
context = {
'contents': contents,
'form_mates': MatesForm(),
}
print("nice3")
return render(request, 'mates.html', context)
def previous(request):
id= request.GET.get("id", None)
if id != 1:
previous_id= id-1
prev_user= Mates.objects.filter(user= previous_id)
data={
"username": prev_user.user,
"req_bio": prev_user.req_bio,
"req_image": prev_user.req_image,
}
return JsonResponse(data)
models.py
class Mates(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='usermates')
users_requests = models.ManyToManyField(User, related_name="users_requests")
req_bio = models.CharField(max_length=400)
req_image = models.ImageField(upload_to='requestmates_pics', null=True, blank=True, default=False)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_pic = models.ImageField(upload_to='profile_pics', null=True, blank=True, default='default.png')
bio = models.CharField(max_length=400, default=1, null=True)
connection = models.CharField(max_length = 100, blank=True)
follower = models.IntegerField(default=0)
following = models.IntegerField(default=0)
urls.py
urlpatterns = [
path('mates', views.mates, name='mates'),
path('mates-main', views.matesmain, name='mates-main'),
path('previous', views.previous, name='previous'),
]
html
<div class="mates-grid-1-1-content">
<div class="mates-grid-2-content">
<button type="button" onclick="previous({{user.id}})" id="prev-button">prev</button>
</div>
<div class="mates-grid-1-content">
<div class="mates-item-content">
<img class="mate-pic" src="{{ user.profile.profile_pic.url }}" >
</div>
<div class="mates-item-content">
{{ content.user }}
</div>
<div class="mates-item-content">
<div class="responses">
<div class="response-item-img">
<img class="mates-image" src="{{ content.req_image.url }}" width="400px">
</div>
<div class="response-item-bio">
<p>{{ content.req_bio }}</p>
</div>
<div class="response-item-button">
<button type="submit">Submit</button>
</div>
</div>
</div>
</div>
<div class="mates-grid-3-content">
<button type="button" onclick="next({{user.id}})" id="next-button">next</button>
</div>
</div>
script
function previous(id){
$.ajax({
url: '{% url 'previous' %}',
type: "get",
data: {
'id': id,
},
dataType: 'json',
success: function (data) {
$(".mates-item-content").empty();
$(".mates-item-content").append("<img class="mate-pic" src="{{ user.profile.profile_pic.url }}" >");
$(".mates-item-content").empty();
$(".mates-item-content").append("{{ content.user }}");
$(".mates-item-content").empty();
$(".mates-item-content").append("<img class="mates-image" src="{{ content.req_image.url }}" width="400px">");
$(".mates-item-content").empty();
$(".mates-item-content").append("<p>{{ content.req_bio }}</p>");
},
error: function (data) {
alert('nope');
}
});
};
}
You're getting a NoReverseMatch exception because you're not using the {% url &} tag properly inside the $.ajax() function. Since you have that JavaScript code within the HTML itself, then you can replace what you have with the following:
url: "{% url 'previous' %}"
Also, in the success property's callback function, you're not using the data you're getting from the view how it is supposed to be used. Note that you won't be able to get that data using the Django template language inside this callback, but you need to use JavaScript to access that data. For example:
success: function(data){
console.log(data.req_bio);
}
That is because when the HTML gets rendered by Django, the information that you might want to use inside the callback function (e. g.: {{ req.bio }}), will not be there yet.
Nevertheless, there's a way to keep using the Django template language when handling an AJAX response, using the context data returned by the view. Please, refer to this answer for further information.

How to "load" dependent drop down upon page load?

I have a form with a dependent drop-down. This secondary drop-down is hidden whenever the primary option selected does not have any secondary options, and when the page first loads. Whenever the form is submitted, only the first field gets cleared out, since most of the time the drop-downs remain the same, however, since the script works whenever there is a change in the primary drop-down, since the load upon does not constitute a change, it just keeps the selected/submitted option on the primary drop-down, and will just display an empty secondary drop-down, even when the primary option selected does have secondary options. I got most of the JS from the drop-down from a tutorial, as I am not very familiar with it. For a more visual understanding:
This is the form when the page first loads
When you select an option that has secondary options, the other dropdown appears
After you select a Station and submit, the Employee # clears, but the other two are supposed to remain, however, when the page reloads upon submission, it looks like this, and the station has been cleared according to the debugger since there are none technically. I don't care so much about the station clearing, but more about not having an empty drop-down that should not be empty.
And when I look at the data that remained in the form, only the work area stayed, because the dependent dropdown does not load until you select another option from the drop down, and if you wanted to be able to see the Box Assembly options again, you'd have to click another option and then go back to Box Assembly (for example)
How could I fix this issue? Is there a way to force the javascript to attempt to load first so that it checks if the option that remained does have the secondary options, whether it has been triggered or not?
forms.py
class WarehouseForm(AppsModelForm):
class Meta:
model = EmployeeWorkAreaLog
widgets = {
'employee_number': ForeignKeyRawIdWidget(EmployeeWorkAreaLog._meta.get_field('employee_number').remote_field, site, attrs={'id':'employee_number_field'}),
}
fields = ('employee_number', 'work_area', 'station_number')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['station_number'].queryset = Station.objects.none()
if 'work_area' in self.data:
try:
work_area_id = int(self.data.get('work_area'))
self.fields['station_number'].queryset = Station.objects.filter(work_area_id=work_area_id).order_by('name')
except (ValueError, TypeError):
pass
elif self.instance.pk:
self.fields['station_number'].queryset = self.instance.work_area.stations.order_by('name')
views.py
def enter_exit_area(request):
enter_without_exit = None
exit_without_enter = None
if request.method == 'POST':
form = WarehouseForm(request.POST)
if form.is_valid():
emp_num = form.cleaned_data['employee_number']
area = form.cleaned_data['work_area']
station = form.cleaned_data['station_number']
# Submission logic
form = WarehouseForm(initial={'employee_number': '', 'work_area': area, 'station_number': station})
else:
form = WarehouseForm()
return render(request, "operations/enter_exit_area.html", {
'form': form,
'enter_without_exit': enter_without_exit,
'exit_without_enter': exit_without_enter,
})
urls.py
urlpatterns = [
url(r'enter-exit-area/$', views.enter_exit_area, name='enter_exit_area'),
path('ajax/load-stations/', views.load_stations, name='ajax_load_stations'),
]
At the end of this html is the script that handles the dependent drop-down
enter_exit_area.html
{% extends "operations/base.html" %}
{% block main %}
<form id="warehouseForm" action="" method="POST" data-stations-url="{% url 'operations:ajax_load_stations' %}" novalidate >
{% csrf_token %}
<div>
<div>
<label>Employee #</label>
{{ form.employee_number }}
</div>
<div>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div class="col-xs-8" id="my-hidden-div">
<label>Station</label>
{{ form.station_number }}
</div>
</div>
</form>
<script>
function loadStations() {
var url = $("#warehouseForm").attr("data-stations-url");
var workAreaId = $(this).val();
var $stationNumberField = $("#{{ form.station_number.id_for_label }}");
$.ajax({
url: url,
data: {
'work_area': workAreaId
},
success: function (data) {
$("#my-hidden-div").show(); // show it
$stationNumberField.html(data);
// Check the length of the options child elements of the select
if ($stationNumberField.find("option").length === 1) {
$stationNumberField.parent().hide(); // Hide parent of the select node
} else {
// If any option, ensure the select is shown
$stationNumberField.parent().show();
}
}
});
}
$("#id_work_area").change(loadStations);
$(document).ready(loadStations);
</script>
{% endblock main %}
station_number_dropdown_options.html
<option value="">---------</option>
{% for station in stations %}
<option value="{{ station.pk }}">{{ station.name }}</option>
{% endfor %}
I see that you have $(document).ready(loadStations);.
But the problem is that in loadStations, you do var workAreaId = $(this).val();.
this will be document, and $(document).val() is an empty string.
Either hardcode the selector in loadStations:
// var workAreaId = $(this).val();
var workAreaId = $("#id_work_area").val();
Or trigger the change from the element instead:
$("#id_work_area").change(loadStations);
// $(document).ready(loadStations);
$("#id_work_area").change();

HTML Dropdown Menu / Typeaheads (From Python to JavaScript)

My plan is to create a website where people can write diaries and read others' diaries and I am currently in the middle of implementing a search box where the user may search usernames to read others' diaries (like how you may look up a friend on Facebook), so I wrote the following code for the search box, with the Python code (contains SQLite statements) extracting search results from the database and with the JavaScript constantly performing the search action behind the scene, to output to search.html as a dynamic dropdown menu. However, the drop-down part is not working.
Application.py
#app.route("/search", methods=["GET", "POST"])
#login_required
def search():
"""Search published diaries from others."""
# if user reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# ensure friends' usernames were submitted
if not request.form.get("search"):
return dumbo("No username entered")
# search user's friends from database
# append % to q so that user doesn't have to input city/state/postal code in the URL completely/accurately
# https://www.tutorialspoint.com/sqlite/sqlite_like_clause.htm
friend = request.form.get("search") + "%"
searches = db.execute("SELECT username FROM writers WHERE username LIKE :friend", friend=friend)
# output a JSON array of objects, each represents a row from TABLE writers that matches what the user looks for
return jsonify(searches)
# else if user reached route via GET (as by clicking a link or via redirect)
else:
return render_template("search.html")
scripts.js
$("#friend").keydown(function() {
// configure typeahead
$("#friend").typeahead({
// If true, when suggestions are rendered, pattern matches for the current query in text nodes
// will be wrapped in a strong element with its class set to {{classNames.highlight}}. Defaults to false.
highlight: false,
// The minimum character length needed before suggestions start getting rendered. Defaults to 1.
minLength: 1
},
{
display: function(suggestion) { return null; },
limit: 5,
source: search,
templates: {
suggestion: Handlebars.compile(
"<div>" +
"<div>{{username}}</div>" +
"</div>"
)
}
});
// action after friend is selected from drop-down
$("#friend").on("typeahead:selected", function(eventObject, suggestion, name) {
// visit friend's diaries (to be continued)
});
});
/**
* Searches database for typeahead's suggestions.
*/
// search calls asyncResults after searching is done (asynchronously)
function search(query, syncResults, asyncResults)
{
// get usernames matching query (asynchronously)
var parameters = {
friend: $("#friend").val()
};
$.getJSON(Flask.url_for("search"), parameters)
.done(function(data, textStatus, jqXHR) {
// call typeahead's callback with search results (i.e., friends)
asyncResults(data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
// log error to browser's console
console.log(errorThrown.toString());
// call typeahead's callback with no results
asyncResults([]);
});
}
search.html
{% extends "layout.html" %}
{% block title %}
Search
{% endblock %}
{% block main %}
<form action="{{ url_for('search') }}" method="post">
<fieldset>
<div class="form-group">
<input autocomplete="off" autofocus class="typeahead" id="friend" name="search" placeholder="Search friends" type="text"/>
</div>
<div class="form-group">
<button class="btn btn-default" type="submit">Search</button>
</div>
<!-- https://www.mkyong.com/javascript/how-to-link-an-external-javascript-file/ -->
<script type="text/javascript" src="static/scripts.js"></script>
</fieldset>
</form>
{% endblock %}
I literally just started learning CS, so please advise as if I am an idiot, thank you!!!!
In your Application.py, POST method returns jsonify(searches) ,
so I think should use $.post instead of $.getJSON in
$.getJSON(Flask.url_for("search"), parameters)
...
Update:
If you get friend from "search" in server,
friend = request.form.get("search") + "%"
then should change friend to search in parameters in search function
var parameters = {
//friend: $("#friend").val()
search: $("#friend").val()
};

Dynamically update Django form field options using Ajax to GET new queryset

I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.
Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.
I have models.py as follows:
class Product(models.Model):
category = models.ForeignKey("Category")
sub_category = models.ForeignKey("SubCategory")
class Category(models.Model):
name = models.CharField(max_length=256)
class SubCategory(models.Model):
category = models.ForeignKey("Category")
name = models.CharField(max_length=256)
And my forms.py includes:
class BasicSearchForm(forms.Form):
category = forms.ModelChoiceField(
label='Category',
queryset=Category.objects.all(),
to_field_name="name",
empty_label=None,
initial="Red")
sub_category = forms.ModelMultipleChoiceField(
required=False,
label='Type',
queryset= SubCategory.objects.all(),
to_field_name="name",
widget=forms.Select)
And my views.py includes:
def search(request):
if request.method == 'POST':
form = BasicSearchForm(request.POST)
if form.is_valid():
category = form.cleaned_data['category']
sub_category = form.cleaned_data['sub_category']
return render(request, 'myapp/search.html', {'form': form})
else:
form = BasicSearchForm()
return render(request, 'myapp/search.html', {'form': form})
And finally the search.html includes:
<form class="search-form" role="search" action="/search/" method="get">
{{ form }}
<input type="submit" value="Search" />
</form>
I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!
Update:
Thanks for the feedback. As a result I updated the following:
In my urls.py:
urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),
And in my views.py:
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(sub_category, safe=False)
And I have this in my myapp/search.html:
{% block javascript %}
<script>
$("#id_category").change(function () {
var category = $(this).val();
$.ajax({
url: '{% url "myapp:update_subcategories" %}',
data: {
'category': category,
},
success: function (response) {
var new_options = response;
alert(new_options[0].name); // works
$('#id_sub_category').empty();
$.each(new_options, function(key, value) {
$('#id_sub_category')
.append($('<option>', { value : key })
.text(value.name));
});
}
});
</script>
{% endblock %}
Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.
Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.
You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.
For a simple example, you could refer
here
How do I POST with jQuery/Ajax in Django?
EDIT
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(dict(sub_category=sub_category))
Then in ajax response you could grab it like response.data.sub_category
Use ajax to send the category and retrieve subcategory elements.
For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.

Categories

Resources