Embedding multiple levels collection of forms in Symfony2 - javascript

I have the following situation: the CVCFormType is a collection of BenefiItemsFormType. Each BenefitItemFormType has one field that is a collection of BenefitGroupFormType.
I want to be able to dynamically add and remove elements.
I followed the instructions here. Of course they must be tweaked as we talk about nested collections.
On the "fixed" side everything is ok. On the dynamic side (to add and remove elements) so far I've implemented only the inner side (adding BenefitGroups) and only for adding fields.
Here is what I get (which is not right). I have a double link on the top Benefit Item (I should have only one), plus the two group of links (of the first benefit
item and of the second one) are not independent (I click on the second of the one above and it adds a field to the one below). I think I'll have to dynamically change the ul class name.
Any help?
Here is a screenshot:
And here is the code:
{% extends "internal.html.twig" %}
{% block content %}
{{ form_start(form) }}
<br><b>CVC</b>
{% for benefititem in form.benefititems %}
<br><b>Benefit Item</b>
{{ form_row(benefititem.comment) }}
<br><b>Benefit Groups</b>
{# <ul class="benefitgroups"> #}
<ul class="benefitgroups" data-prototype="{{ form_widget(benefititem.benefitgroups.vars.prototype)|e }}">
{% for benefitgroup in benefititem.benefitgroups %}
<li>{{ form_row(benefitgroup.name) }}</li>
{% endfor %}
</ul>
{% endfor %}
{{ form_end(form) }}
{% block javascripts %}
<script>
var $collectionHolder;
// setup an "add a benefitgroup" link
var $addBenefitGroupLink = $('Add a Group');
var $newLinkLi = $('<li></li>').append($addBenefitGroupLink);
jQuery(document).ready(function() {
// Get the ul that holds the collection of benefit groups
$collectionHolder = $('ul.benefitgroups');
// add the "add a benefitgroup" anchor and li to the benefitgroups ul
$collectionHolder.append($newLinkLi);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);
$addBenefitGroupLink.on('click', function(e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addBenefitGroupForm($collectionHolder, $newLinkLi);
});
});
function addBenefitGroupForm($collectionHolder, $newLinkLi) {
// Get the data-prototype explained earlier
var prototype = $collectionHolder.data('prototype');
// get the new index
var index = $collectionHolder.data('index');
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, index);
// increase the index with one for the next item
$collectionHolder.data('index', index + 1);
// Display the form in the page in an li, before the "Add a BenefitGroup" link li
var $newFormLi = $('<li></li>').append(newForm);
$newLinkLi.before($newFormLi);
}
</script>
{% endblock %}
{% endblock content %}
If it can help here is the generated HTML:
<html>
<head>
<meta charset="UTF-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<title>Welcome!</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
</head>
<body>
<h1>Here you are inside</h1>
<form name="CVC" method="post" action="">
<br><b>CVC</b>
<br><b>Benefit Item</b>
<div> <label for="CVC_benefititems_0_comment" class="required">Comment</label> <input type="text" id="CVC_benefititems_0_comment" name="CVC[benefititems][0][comment]" required="required" maxlength="400" value="b1" /></div>
<br><b>Benefit Groups</b>
<ul class="benefitgroups" data-prototype="<div id="CVC_benefititems_0_benefitgroups___name__"><div> <label for="CVC_benefititems_0_benefitgroups___name___name" class="required">Name</label> <input type="text" id="CVC_benefititems_0_benefitgroups___name___name" name="CVC[benefititems][0][benefitgroups][__name__][name]" required="required" maxlength="100" /></div></div>">
<li><div> <label for="CVC_benefititems_0_benefitgroups_0_name" class="required">Name</label> <input type="text" id="CVC_benefititems_0_benefitgroups_0_name" name="CVC[benefititems][0][benefitgroups][0][name]" required="required" maxlength="100" value="c1b1" /></div></li>
<li><div> <label for="CVC_benefititems_0_benefitgroups_1_name" class="required">Name</label> <input type="text" id="CVC_benefititems_0_benefitgroups_1_name" name="CVC[benefititems][0][benefitgroups][3][name]" required="required" maxlength="100" value="c2b1" /></div></li>
</ul>
<br><b>Benefit Item</b>
<div> <label for="CVC_benefititems_1_comment" class="required">Comment</label> <input type="text" id="CVC_benefititems_1_comment" name="CVC[benefititems][4][comment]" required="required" maxlength="400" value="b2" /></div>
<br><b>Benefit Groups</b>
<ul class="benefitgroups" data-prototype="<div id="CVC_benefititems_1_benefitgroups___name__"><div> <label for="CVC_benefititems_1_benefitgroups___name___name" class="required">Name</label> <input type="text" id="CVC_benefititems_1_benefitgroups___name___name" name="CVC[benefititems][5][benefitgroups][__name__][name]" required="required" maxlength="100" /></div></div>">
<li><div> <label for="CVC_benefititems_1_benefitgroups_0_name" class="required">Name</label> <input type="text" id="CVC_benefititems_1_benefitgroups_0_name" name="CVC[benefititems][6][benefitgroups][0][name]" required="required" maxlength="100" value="c2b2" /></div></li>
</ul>
<div><button type="submit" id="CVC_submit" name="CVC[submit]">Do Something</button></div><input type="hidden" id="CVC__token" name="CVC[_token]" value="MEUAU3VawkCDJ5jTHo5hSTGrgrWS6XUm-UXeEI9onT8" /></form>
Instead of a list I wish to do all this with tables (so adding and removing rows from the table).
The final goal (adding an additional layer) will be the following:

Well, as you say, your problem is on the dynamic side, aka, Client Side.
I gonna post my shot on what you're trying to do here.
But before, a pro-tip: NEVER EVER print html on an attribute, use another technique, like the one I'm using for example, templates (to replace your prototype attr). There are a ton more techniques to do this, I'll just explain mine.
var $outerTemplate;
var $innerTemplate;
var $outerContainer;
jQuery(document).ready(function($) {
$outerTemplate = $('#top-form-template');
$innerTemplate = $('#inner-form-template');
$outerContainer = $("#row-container");
$("#addRow").on('click', function(e){
addOuterForm();
});
$outerContainer.on('click', '.addItem', function (e) {
addInnerForm(e.target.dataset.rowId);
})
$outerContainer.on('click', '.destroy-row', function (e) {
destroyRow(e.target.dataset.rowId);
});
$outerContainer.on('click', '.destroy-item', function (e) {
destroyItem(e.target.dataset.itemId);
});
});
function addOuterForm () {
var compiled = _.template($outerTemplate.html());
var html = compiled({
outerId: _.uniqueId(),
innerId: _.uniqueId()
});
$outerContainer.append(html);
}
function addInnerForm (outerId) {
var compiled = _.template($innerTemplate.html());
var html = compiled({
outerId: outerId,
innerId: _.uniqueId()
});
$outerContainer.find('#row-'+outerId).find('.benefitgroups').append(html);
}
function destroyRow(id){
$("#row-"+id).remove();
}
function destroyItem(id){
$("#item-"+id).remove();
}
What I did is create 2 templates, one for the outer form (the one with benefit item & group) and other with an inner form (the extra benefit items). Then attach & removing them using some buttons. I encorage you to take a look at templating engines on client side (I notice you know how to use Twig templating engine maybe Handlebars will be easy to catch for you).
I'm using lodash templating engine, because its quite simple and lodash tools are very powerful and useful.

To embed multiple collection of forms usually i use self-widget to control templating. For example:
{{form.name}}
<ul id="benefit-items" data-prototype="{{_self.widget_prototype(form.benefitItems.vars.prototype}|e}}">
{% for benefitItem in form.benefitItems %}
{{_self.widget_prototype(benefitItem)}}
{% endfor %}
<li id="add-benefit-item" onclick="addBenefitItem(this);">add benefit</li>
</ul>
{% macro widget_prototype(form) %}
<li class="benefitItem">
{{form.title}}
<ul class="benefit-group" data-prototype="{{form_widget(form.benefitGroups.vars.prototype)|e}}">
</ul>
</li>
{% endmacro %}
It's how i'm used to create multiple collection forms.

Related

Refresh (variable) part of an html page with hotwire (Flask app)

Context
I am building a simple "todo" flask app with an SQLAchemy database.
The tasks are sorted by sections (check the image below to see how it is organized).
Once I implemented all the functionalities I wanted, I ran into the issue which was the whole page
got refreshed each time I triggered a button (add/edit/delete/update_status).
Then I found out hotwire which is amazing to handle this.
This my taskManager.html organization:
<!--New task button-->
<turbo-frame id="data_frame-add">
<button class="btn-add" onclick="openAddForm()">...</button>
<div class="form-popup" id="myForm">
<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST">
<label for="section"><b>Section</b></label>
<input type="text" id="section" name="section" required>
<label for="content"><b>Task</b></label>
<input type="text" id="content" name="content required>
<button type="submit" class="btn">Add</button>
</form>
</div>
</turbo-frame>
<!--Display sections and tasks-->
<div class="flex-row">
{% for section in sections %}
<turbo-frame id="data_frame-{{ section }}">
<div class="flex-column">
<h2>{{ section }}</h2>
{% for task in tasks %}
{% if task.section == section %}
<p>{{ task }}</p>
<button class="satus"></button>
<button class="edit"></button>
<div class="form-popup" id="form-{{ task.id }}">...</div>
<button class="delete"></button>
<div class="form-popup" id="form-{{ task.id }}">...</div>
{% endif %}
{% endfor %}
</div>
</turbo-frame>
{% endfor %}
</div>
Using a turbo frame with id data_frame-{{ section }} (one for each section) allowed to refresh only the concerned section when hitting status, edit and delete buttons (for example, hitting delete button of task 2 of Section 2 will only refresh the turbo frame data_frame-Section 2). However as the New task button is out of theses turbo frames, It works differently and this is a different challenge...
Issue
When adding a new task, I would like the section of the task (entered here <input type="text" id="section" name="section"...>) to be saved in a variable which will be used to target a specific <turbo-frame id="data_frame-{{ section }}"> and refresh it without refreshing the whole page.
At the moment as the New task button is wrapped with <turbo-frame id="data_frame-add"> it is self contained (meaning if I'm adding a task 5 to Section 1 only the turbo frame with id data_frame-add is refreshed not the data_frame-Section 1 so I need to manually refresh the page to see changes)
What I tried
I added data-turbo-frame to the form:
<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST" data-turbo-frame="data_frame-Section 1">
in order to be able to refresh the "data_frame-Section 1" when I add a New task in section Section 1, and it works! But I would like to make this data-turbo-frame="data_frame-<section>" with <section> a variable that get the value of <input type="text" id="section" name="section"...>
To achieve this I removed data-turbo-frame="data_frame-Section 1" in the form:
<form name="AddTaskForm" action="{{ url_for('add_task') }}" class="form-container" method="POST">
and added a Javascript part:
var sectionValVar = document.getElementById("section").value;
const sectionValPref = "data_frame-";
let sectionVal = sectionValPref + sectionValVar;
$(".AddTaskForm").attr("data-turbo-frame", sectionVal);
sectionVal is supposed to get the variable value "data_frame-<section>" and last line add "data-turbo-frame" = "data_frame-<section>" to the <form name="AddTaskForm"...>
But this doesn't work. I'm not sure if this even possible to make as it looks tricky...
But if someone has any hint or fix for this It would be amazing !
Thank you !
Other ressources
This is my add_task route in my python flask app:
#app.route('/add', methods=['GET', 'POST'])
def add_task():
content = request.form.get('content')
section = request.form.get('section')
task = Task(content=content, section=section)
db.session.add(task)
db.session.commit()
return redirect(url_for('taskManager'))
This looks like it would work but I don't see an event listener to set the form data-turbo-frame attribute whenever the input value changes.
You need to update the attribute either before the form submits or whenever the input gets updated.
this is how you could do it with jquery
$("#section").change(function() {
let sectionAndPrefix = "data_frame-" + $("#section").val()
$(".AddTaskForm").attr("data-turbo-frame", sectionAndPrefix);
})
in vanilla javascript
const sectionInput = document.querySelector("#section")
sectionInput.addEventListener("input", function() {
const taskForm = document.querySelector(".AddTaskForm")
const sectionAndPrefix = "data_frame-" + sectionInput.value
taskForm.setAttribute("data-turbo-frame", sectionAndPrefix)
})

How can I modify this JS function so that it runs whenever the field is equal a certain length?

I have a function that currently runs whenever the user clicks/tabs out of the employee_number field. I would like it to run whenever the length of the numbers entered is equal to 6, without having to leave the field, since when I try using the tab, it conflicts with loading the next field which is a drop-down that is part of the function ran.
I tried by running it using .change and putting the constraint within the function, but it did not work and I don't know what else to try.
enter_exit.html
{% extends "base.html" %}
{% load core_tags staticfiles %}
{% block main %}
<form id="warehouseForm" action="" method="POST" data-employee-activity-lookup-url="{% url 'operations:employee_activity_search' %}" novalidate >
{% csrf_token %}
<div>
<div>
<div id="employee-name" style="margin-bottom: 10px"> </div>
<label>Employee #</label>
{{ form.employee_number }}
</div>
<div=>
<label>Work Area</label>
{{ form.work_area }}
</div>
<div style="display: none" id="my-hidden-div">
<label>Station</label>
{{ form.station_number }}
</div>
</div>
<div>
<div>
<button>Enter Area</button>
<button>Exit Area</button>
</div>
</div>
</form>
<script>
// Grab the employee name and their current active work log (if any)
$(document).on('blur', "#{{ form.employee_number.id_for_label }}", function(){
var url = $("#warehouseForm").attr('data-employee-activity-lookup-url');
var employeeId = $(this).val();
# ... more fields ...
if (employeeId !== "") {
# .. Rest of function ...
})
</script>
{% endblock main %}
Instead of using on blur have you tried using .keyup or .keydown?
Here's a simple version of it working in CodePen:
$("#test1").keydown(()=>{
if($("#test1").val().length>6)
console.log('6+');
})
https://codepen.io/orunnals/pen/JjoRGLX

JS code not working on all data retrieved from Django Model

I'm retrieving an endDate field from Django model using forloop on HTML page.
I want to check whether all the endDate coming are less that today's date or not. For that I'm using JS code.
My code is able to perform checking only on first date retrieved but it's not working on other dates.
Here's my HTML code for retrieving data:
{% for p in object_list %}
<h4>{{ p.jobName }}</h4>
<p><b style="color: red">Expires On: </b><b>{{ p.endDate }}</b></p>
View info
<input type="hidden" id="endDate" name="variable" value="{{ p.endDate }}">
<span id="check"></span>
{% endfor %}
JavaScript code to check date:
<script type="text/javascript">
var endDate = document.getElementById("endDate").value;
var ToDate = new Date();
if(new Date(endDate).getTime()<=ToDate.getTime()){
document.getElementById("check").innerHTML = "Expired";
document.getElementById("check").className = "label danger";
}
else{
document.getElementById("check").innerHTML = "Active";
document.getElementById("check").className = "label success";
}
</script>
I have 2 {{ p.jobName }} right now. First one should show Expired while second should show Active.
My code is working only for first date i.e., for Computer Admin only.
Here's what I'm getting the output:
Can anybody tell me what's the issue?
Thanks
To expand on what I've already said in the comments:
The essence of your problem is here:
{% for p in object_list %}
...
<input type="hidden" id="endDate" name="variable" value="{{ p.endDate }}">
<span id="check"></span>
...
{% endfor %}
An HTML document can only ever have one element of a particular ID (otherwise it doesn't "ID" the element very well - this is part of the HTML spec). In your case, whenever the loop has more than one element, you will end up with multiple elements, all with the endDate ID, as well as multiples with the check ID.
Repeating an ID like this, as well as being wrong in itself, can have a major functional impact - as you're seeing in your code. Namely that since there is only ever supposed to be (at most) one element of a given ID, any Javascript code that runs on the page is entitled to assume that this is the case. And in particular, document.getElementById returns an object representing a single HTML element, as opposed to similar methods such as document.getElementsByClassName which return a "collection" of DOM nodes. (Because a class, unlike an ID, can be applied to multiple elements in the same document.)
So that, in a nutshell, is how to solve the problem you're having. Simply replace the id="endDate" in the above to class="endDate", similarly with check, and alter your javascript to take this into account. Not only do you have to change the getElementById method to getElementsByClassName, you also have to take into account that this returns a collection (which you can loop over with .forEach), and that you need to target the neighbouring .check span rather than the first, or any old random one. The following code is one way to make it work (note the use of ES6 Array.from to convert the collection to a genuine Array so you can use .forEach):
var endDateElts = Array.from(document.getElementsByClassName("endDate"));
var ToDate = new Date();
endDateElts.forEach(function(dateElt) {
var endDate = dateElt.value;
var check = dateElt.nextSibling;
if(new Date(endDate).getTime()<=ToDate.getTime()){
check.innerHTML = "Expired";
check.className = "label danger";
}
else{
check.innerHTML = "Active";
check.className = "label success";
}
});
Based on
{% for p in object_list %}
<h4>{{ p.jobName }}</h4>
<p><b style="color: red">Expires On: </b><b>{{ p.endDate }}</b></p>
View info
<input type="hidden" id="endDate" name="variable" value="{{ p.endDate }}">
<span id="check"></span>
{% endfor %}
I would assume that you have multiple objects with the same ID hence the reason why the JS is only working on the first endDate and check object.
<input type="hidden" id="endDate" name="variable" value="{{ p.endDate }}">
<span id="check"></span>
What you should do:
Use document.querySelectorAll() to get multiple DOM elements
with the same class name.
Iterate through the NodeList and perform the operations that you wish to perform.
const endDates = document.querySelectorAll('.endDate');
const checks = document.querySelectorAll('.check');
let ToDate = new Date();
let i = 0;
for (const endDate of endDates){
if(new Date(endDate.value).getTime()<=ToDate){
checks[i].innerHTML = "Expired";
checks[i].className = "label danger";
}
else{
checks[i].innerHTML = "Active";
checks[i].className = "label";
}
i++;
}
<div class="container-1">
<input type="text" class="endDate" name="variable" value="2019-11-29">
<span class="check"></span>
<br>
</div>
<div class="container-2">
<input type="text" class="endDate" name="variable" value="2019-11-24">
<span class="check"></span>
<br>
</div>
<div class="container-3">
<input type="text" class="endDate" name="variable" value="2019-11-27">
<span class="check"></span>
</div>
Notes
Not very sure why you would want to do the validation in the front-end if you can do it on your views.py. #Robin Zigmond gave a very good suggestion of just doing it on the backend and populate the template with the result.

Django: How can I create a dynamic form that changes on user click?

I'm making a workout calendar website where a user can add workouts with varying amounts of lift, sets and reps, etc. Thus, I need a form that adds a field when a user clicks a button. I've made a template and some javascript to describe what it is I want to achieve exactly:
url:
url(r'^add/(?P<year>[0-9]+)/(?P<month>[0-9]+)/(?P<day>[0-9]+)/$', views.add_workout, name = 'add_workout')
template:
{% block hidden %}
{% include "workoutcal/liftrow.html" %} {# To be used by Javascript #}
{% include "workoutcal/cardiorow.html" %}
{% endblock %}
<form action="{% url 'add_workout' date.year date.month date.day %}" method="post">
<div class="row">
<div class="col-xs-2">
<p id="date">{{ date.year }}-{{ date.month }}-{{ date.day }}</p>
<input type="hidden" name="date" value="{{ date }}">
</div>
</div>
<h2 class="col-xs-12">Lifts</h2>
<div id="liftrows">
{% for i in range %}
{% include "workoutcal/liftrow.html" %}
{% endblock %}
</div>
<div class="row">
<div class="col-xs-0"></div>
<label class="col-xs-2"><button type="button" id="addliftbutton">One more lift</button></label>
</div>
<h2 class="col-xs-12">Cardio</h2>
<div id="cardiorows">
{% include "workoutcal/cardiorow.html" %}
</div>
<div class="row">
<label class="col-xs-2"><button type="button" id="addcardiobutton">One more cardio</button></label>
</div>
<div class="row">
<div class="col-xs-10"></div>
<label class="col-xs-2"><input type="submit" id="submitbutton" value="Save Workout"></label>
</div>
</form>
javascript:
//Adding onclick to buttons
document.getElementById('addliftbutton').onclick = addLiftRow;
document.getElementById('addcardiobutton').onclick = addCardioRow;
for (var i=0; i<setsBoxes.length; i++){
setsBox = setsBoxes[i];
setsBox.onchange = insertRepFields;
}
function addLiftRow(){
var liftRowElements = document.getElementById('liftrows');
var hidden_liftrow = document.getElementById('hidden').getElementsByClassName('lift')[0];
var new_liftrow = hidden_liftrow.cloneNode(true);
liftRowElements.appendChild(new_liftrow);
}
function addCardioRow(){
var cardiorows = document.getElementById('cardiorows');
var hidden_cardiorow = document.getElementById('hidden').getElementsByClassName('cardio')[0];
var new_cardiorow = hidden_cardiorow.cloneNode(true);
cardiorows.appendChild(new_cardiorow);
}
function insertRepFields(){} // big function that inserts as many input fields as the number inside the box whose event called the function.
2 questions:
1. Is there a better way to do this in Django?
2. If this is the best way, how do I go about sending the data of my massive form back to django? Since I don't know exactly how many fields there will be, I don't know how to create a form that accepts a variable amount of fields, and fields within fields.
Here's how a filled-in form could look:
The best way to accomplish that is inserting inputs with the same name and then in Django get all those inputs as a list like:
def view(request):
inputs = request.POST.getlist('your_input_name')
for i in inputs:
Model.objects.create() # Save your model

How to write JQuery function to display text in special location of Django template

I'm trying to create my first ajax function in Django.
I want to change my code using JQuery, the idea is pretty simple:
User type a subject name and this name is displayed in subject-list below the form,
The problem is I don't really know what to type in JQuery function.
JQuery:
function create_subject() {
$("input").focus(function(){
var subject = $(this).val();
$(".btn-create").click(function(){
/* What I need to write in here */
});
});
}
In HTML "subjects" refer to database.
HTML
<div id="subjects-list">
{% if user.username %}
<ul>
{% if subjects %}
<form method="post" action=".">{% csrf_token %}
{% for subject in subjects %}
-------- TYPED TEXT SHOULD BE HERE --------> <li>{{ subject.name }}</li>
{% endfor %}
</form>
{% else %}
<p>No Subjects for this user</p>
{% endif %}
</ul>
{% else %}
You are in else
{% endif %}
</div>
That's how HTML looks in "View Page Source"
<div id="create-subject">
<form method="post" action="."> <div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='cfbd1893742c3ab9936bacaae9653051' /></div>
<p><label for="id_name">Subject Name:</label> <input id="id_name" type="text" name="name" size="9" /></p>
<input type="button" name="subject-create-b" value="Create Subject" class="btn-create"/>
</form>
</div>
<div id="subjects-list">
<ul>
<form method="post" action="."><div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='cfbd1893742c3ab9936bacaae9653051' /></div>
<li>Math 140<span id="subject-link"></span></li>
</form>
</ul>
</div>
</div>
And that's my form
forms.py
class SubjectCreationForm(forms.Form):
name = forms.CharField(label="Subject Name", widget=forms.TextInput(attrs={'size':9}))
class Meta:
exclude = ('created_by', 'created_time', 'num_of_followers', 'vote')
def clean_name(self):
name = self.cleaned_data['name']
if len(name)>1:
return name
else:
raise forms.ValidationError("Subject name should be longer")
In order to do what (I think) you want to do which is some basic AJAX using Django as your backend, you'll need the following:
A view which returns the data you want to load
There are a number of ways you can represent the data, but to keep it simple, I'll use HTML.
Javascript to load that view (using JQuery if you like)
Your code might look like this for the first part:
urls.py:
...
(r'^get-subjects/$', 'yourapp.views.get_subjects'),
...
views.py:
...
def get_subjects(request):
subjects = # code to fetch your subjects.
return render_to_response('subjects_template.html', {'subjects': subjects})
...
subjects_template.html:
{% for subject in subjects %}
<li>{{ subject.name }}</li>
{% endfor %}
For the second part, it might look like this:
main_template.html:
...
<ul id="subjects-list"></ul>
<script>
function loadSubjects() {
$.ajax({
url: "/get-subjects",
success: function (data) {
$("#subjects-list").html(data);
}
});
}
</script>
...
[1] render_to_response()
[2] jQuery.ajax()
This will get you most the way there. When you want to reload the list, you call the loadSubjects() function.
As far as creating the subjects go, that is a different thing. What you'll want to look into is how to do an HTML form submission without leaving the page. There are plenty of tools and libraries to do that stuff with a nice api. If you want to stick with JQuery, you might consider this plugin for a nicer api.
function create_subject() {
$("input").focus(function(){
var subject = $(this).val();
$(".btn-create").click(function(){
$('#subjects-list').append(subject);
});
});
}
that said, you probably don't want to assign the click handler every time the input is focused. i'd move that out of the focus handler.

Categories