Fullcalendar Event save on click - Django - javascript
I suppose that this is pretty simple to someone who knows JavaScript, but I just can't figure it out on my own.
I've implemented FullCalendar into my Django application, and what I want is simple Event save on users click(saving events to the database after user's action, after the click release and drawn event).
I can add an event using Django form, and have no problem with that, but I want to save the event on click, as I said before. So the user clicks on the desired time, draw the event 'bubble' and let the click off. So when that event is drawn, it needs to be saved directly to the database or passed to the Django form.
I don't have much experience in JS so please don't mind me.
Thank you in advance.
PS: I repeat, everything works fine, I can add an event, delete it, etc, but only with my form.
Let's start with the zakazi.html (page where all the events are created) :
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% load static %}
{% block title %} Zakaži {% endblock title %}
{% block content_row %}
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
crossorigin="anonymous"></script>
{{ form.media }}
{% include 'main/partials/_link_fullcalendar.html' %} # all the cdn's for fullcalendar(link tags)
<div style="display: flex; width: 100%; margin: 0 auto;text-align: center">
<div class="container" style="width: 350px;">
<div class="row">
<div class="col">
<form method="post" action="{% url 'main:add_event' opp.pk %}">
{{ form|crispy }}
{% csrf_token %}
<button type="submit" class="btn btn-primary">Potvrdi</button>
</form>
</div>
</div>
</div>
<div class="container"
style="display: flex; overflow-x: scroll; height: 750px; width: max-content;">
{% if calendars %}
{% for cal in calendars %}
<script>
document.addEventListener('DOMContentLoaded', function () {
let calendarEl = document.getElementById('{{ cal.id }}');
//////////////////////////////////////////////////////////////////////////////////////////////
let calendar1 = new FullCalendar.Calendar(calendarEl, {
minTime: "07:00:00",
maxTime: "22:00:00",
businessHours: {
startTime: '09:00', // a start time (10am in this example)
endTime: '21:00', // an end time (6pm in this example)
},
height: 'auto',
locale: 'sr',
plugins: ['dayGrid', 'timeGrid', 'list', 'interaction'],
defaultView: 'timeGridDay',
header: {
left: 'today',
center: '{{ cal.name|title }}',
right: 'timeGridDay,dayGridWeek,timeGridThreeDay'
},
views: {
timeGridThreeDay: {
type: 'timeGrid',
duration: {days: 3},
buttonText: '3 Dana'
}
},
navLinks: false, // can click day/week names to navigate views
select: function (arg) {
let title = prompt('Naziv posla');
if (title) {
calendar.addEvent({
title: title,
start: arg.start,
end: arg.end,
allDay: arg.allDay
})
}
calendar.unselect()
},
editable: true,
selectable: true,
selectMirror: true,
eventLimit: true, // allow "more" link when too many events
eventTextColor: 'black',
events: [
{% for i in events %}
{% if i.calendar_id == cal.id %}
{
id: "{{ i.event_id }}",
calendar: "{{ i.calendar }}",
calendar_id: "{{ i.calendar_id }}",
title: "{{ i.event_name}}",
start: '{{ i.start_date|date:"Y-m-d" }}T{{ i.start_date|time:"H:i" }}',
end: '{{ i.end_date|date:"Y-m-d" }}T{{ i.end_date|time:"H:i" }}',
},
{% endif %}
{% endfor %}
]
});
//////////////////////////////////////////////////////////////////////////////////////////////
calendar1.render();
//////////////////////////////////////////////////////////////////////////////////////////////
})
;
</script>
{% endfor %}
{% endif %}
<div style="display: flex; height: 1000px" class="container">
{% for cal in calendars %}
<p>{{ cal.name|title }}{{ cal.id }}</p>
<div class="container" id='{{ cal.id }}'></div>
{% endfor %}
</div>
</div>
</div>
<!---------------------------------------------- FULLCALENDAR SCRIPT----------------------------------------------->
{% include 'main/partials/_fullcalendar_script.html' %}
<!---------------------------------------------- FULLCALENDAR SCRIPT END ------------------------------------------>
{% endblock content_row %}
My views.py (all_events and add_events functions):
#################################################### EVENTS #########################################################
def events(request):
all_events = Events.objects.all()
get_event_types = Events.objects.only('event_type')
calendars = Calendar.objects.all()
if request.GET:
event_arr = []
if request.GET.get('event_type') == "all":
all_events = Events.objects.all()
else:
all_events = Events.objects.filter(event_type__icontains=request.GET.get('event_type'))
for i in all_events:
event_sub_arr = {}
event_sub_arr['id'] = i.event_id
event_sub_arr['calendar'] = i.calendar
event_sub_arr['calendar_id'] = i.calendar.id
event_sub_arr['title'] = i.event_name
start_date = datetime.strptime(str(i.start_date.date()), "%Y-%m-%dT%H:%M:%S").strftime("%Y-%m-%dT%H:%M:%S")
end_date = datetime.strptime(str(i.end_date.date()), "%Y-%m-%dT%H:%M:%S").strftime("%Y-%m-%dT%H:%M:%S")
event_sub_arr['start'] = start_date
event_sub_arr['end'] = end_date
event_arr.append(event_sub_arr)
return HttpResponse(json.dumps(event_arr))
context = {
"calendars": calendars,
"events": all_events,
"get_event_types": get_event_types,
}
return render(request, 'main/selectable.html', context)
################################################## ADD EVENTS #########################################################
def add_event(request, pk):
opp = get_object_or_404(OpportunityList, pk=pk)
events_all = Events.objects.all()
calendars = Calendar.objects.all()
opp_locked = get_object_or_404(Locked, pk=pk)
user = User.objects.get(username=request.user.username)
form = AddEventForm()
if request.method == 'POST':
form = AddEventForm(request.POST or None)
if form.is_valid():
event = Events.objects.create(
event_name=form.cleaned_data['event_name'],
event_comment=form.cleaned_data['event_comment'],
status=form.cleaned_data['status'],
start_date=form.cleaned_data['start_date'],
end_date=form.cleaned_data['end_date'],
calendar=form.cleaned_data['calendar'],
opp_eluid=int(opp_locked.locked_eluid.eluid),
zakazan=True,
added_by=user,
)
opp_locked.opp_comment = form.cleaned_data['event_comment']
opp_locked.is_locked = False
opp_locked.zakazan = True
opp_locked.save()
event.save()
messages.success(request, '...' + opp_locked.opp_comment)
return redirect('opportunity:optika')
context = {
'form': form,
'opp': opp,
'events': events_all,
"calendars": calendars
}
return render(request, 'opportunity/detalji/zakazi.html', context) # html for adding events to calendar, where the form is
And the form :
class ZakaziForma(forms.ModelForm):
class Meta:
model = Events
fields = ['event_name', 'event_comment', 'status', 'start_date', 'end_date', 'calendar',
'opp_eluid']
labels = {
'event_name': 'Naziv Posla:',
'event_comment': 'Komentar:',
'status': 'Status:',
'start_date': 'Početak:',
'end_date': 'Završetak:',
'calendar': 'Kalendar',
}
widgets = {
'start_date': DateTimePicker(options={'useCurrent': True, 'collapse': False},
attrs={'icon-toggle': True, }),
'end_date': DateTimePicker(options={'useCurrent': True, 'collapse': False}),
'opp_eluid': forms.HiddenInput(),
}
My urls :
...
path('calendar/dodaj/<int:pk>', add_event, name='add_event'),
...
I think you are trying this in a too complex way. If you use fullcalender and jquery you can just add something like the following in you script code:
$(document).ready(function() {
$('#calendar').fullCalendar({
header: {
left: 'prev,next today',
center: 'title',
right: 'month,agendaWeek,agendaDay',
},
defaultView: 'agendaWeek',
navLinks: true,
selectable: true,
selectHelper: true,
select: function(start, end) {
title='new';
if (title) {
eventData = {
title: title,
start: start,
end: end,
backgroundColor: '#00ff00',
};
$('#calendar').fullCalendar('renderEvent', eventData, true); // stick? = true
}
$.ajax({ url: '/addevent/',
data: {
'start': start,
'end': end,
},
});
;
...here you need to addd the code that draws the events
So what happens is that JS renders the event in the browser and sends the data via an ajax call to the django server. Of course you need to add the ajax view in your urls and views code.
Related
Get relative path in Django
I've created a map where I want to implement some kml files into it . If i harcode the url it works but im trying to pass it through a variable because Im dealing with many kml files in a for loop. Even though the url path im getting in console is right i dont get the result i need.Any idea how to fix that? view: def map(request): field_list = models.Field.objects.all() context = { "title": "Map", "field_list": field_list, } template = 'agriculture/map.html' return render(request, template, context) If i hardcode the url it goes like this : var polygon = omnivore.kml("{% static '../media/kml/user_admin/2022-04-07-2-Arnissa_cherry.kml' %}", ... ); I've tried doing it like this but even though the path Im getting is correct it seems django does not read the path(kml is the FileField in my model): map.html {% for field in field_list %} $(".search_area").append(new Option("{{field.friendly_name}}")); //friendly name var kmldir = "../media/" + "{{field.kml.name}}" console.log(kmldir) // ../media/kml/user_admin/2022-04-07-2-Arnissa_cherry.kml var polygon = omnivore.kml("{% static 'kmldir' %}", null, new L.GeoJSON(null, { //file url style: function() { return { color: 'red', transparent: true, opacity: 1, fillOpacity: 0.05 }} })); kml_arr.push([polygon, "{% static 'kmldir' %}"]); //file url {% endfor %}
I tried a different approach and it worked {% for field in field_list %} {% with "../media/"|add:field.kml.name as fieldname %} $(".search_area").append(new Option("{{ field.friendly_name }}")); //friendly name var polygon = omnivore.kml('{% static fieldname %}', null, new L.GeoJSON(null, { //file url style: function() { return { color: 'red', transparent: true, opacity: 1, fillOpacity: 0.05 } } })); kml_arr.push([polygon, '{% static fieldname %}']); //file url {% endwith %} {% endfor %}
Visual Studio Code: Jinja Curly Braces inline for Javascript variable
I am trying to get Jinja braces to stay in line for a javascript variable used in an apexchart.js call for a flask HTML page. labels = JSON.parse({{ labels | tojson}}) data = JSON.parse({{ data | tojson}}) The problem I am having is that when I save the html the braces get formatted apart. labels = JSON.parse({ { labels | tojson } }) data = JSON.parse({ { data | tojson } }) Is there away to stop Visual Studio Code from splitting the Jinja code apart? Current Code Below: <!-- language: lang-html --> <!-- templates/index.html --> {% extends 'base.html' %} {% block content %} <title>Dashboard | {{title}}</title> <div class="flex h-screen justify-center"> <div id="chart" class="h-1/4 w-1/2 content-center"></div> </div> <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script> <script> labels = JSON.parse({ { labels | tojson } }) data = JSON.parse({ { data | tojson } }) var options = { colors: ['#01ca95'], chart: { type: 'bar' }, dataLabels: { enabled: false }, series: [{ name: 'sales', data: data }], xaxis: { categories: labels }, yaxis: { labels: { formatter: function(data) { //Setup new Formatting class variable var Formatter = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }); return Formatter.format(data); } } }, } var chart = new ApexCharts(document.querySelector("#chart"), options); chart.render(); </script> {% endblock content %}
How to pre-populate the chosen box of the FilteredSelectMultiple in Django for user side?
I am trying to have the user's chosen item be shown in the FilteredSelectMultiple chosen box when he/she revisits the page.I've tried appending options via javascript but that didn't help. I have seen a stackoverflow question on how to pre-populate the same widget as well but that seems to be for the Admin side only. My FilteredSelectMultiple widget is on the user side. Here is the code I used: models.py: class Roles(models.Model): role_id = models.AutoField(primary_key=True) role_name = models.CharField(max_length=18, unique=True, validators=[letters_only]) class Staffs(models.Model): staff_id = models.AutoField(primary_key=True) admission_number = models.CharField(max_length=5, unique=True, help_text="Enter 5 digits", validators=[numeric_only, MinLengthValidator(5)]) staff_full_name = models.CharField(max_length=70, help_text="Enter staff's full name", validators=[letters_only]) created_by = UserForeignKey(auto_user_add=True, editable=False, related_name="staff_created_by", db_column="created_by") created_at = models.DateTimeField(auto_now_add=True, editable=False) updated_by = UserForeignKey(auto_user=True, editable=False, related_name="staff_updated_by", db_column="updated_by") updated_at = models.DateTimeField(auto_now=True, editable=False) roles = models.ManyToManyField(Roles, through='Designations') class Designations(models.Model): designation_id = models.AutoField(primary_key=True) curriculum = models.ForeignKey(Curricula, on_delete=models.CASCADE) role = models.ForeignKey(Roles, on_delete=models.CASCADE) staff = models.ForeignKey(Staffs, on_delete=models.CASCADE) forms.py: class ModuleCoordinatorForm(forms.ModelForm): class Media: css = { 'all': ('admin/css/responsive.css', 'admin/css/base.css', 'admin/css/widgets.css',) } js = ('/admin/jsi18n',) class Meta: model = Designations fields = ['module_coordinator', 'role_mc'] module_coordinator = forms.ModelMultipleChoiceField(queryset=Staffs.objects.all(), widget=FilteredSelectMultiple("Module Coordinator", is_stacked=False), required=False, label="") role_mc = forms.ModelChoiceField(queryset=Roles.objects.all(), widget=forms.HiddenInput, initial=Roles.objects.get(role_name='Module Coordinator')) views.py: def module_coordinator(request): c = request.GET.get('curriculum') c = int(c) mc = request.GET.get('mc') staffs = request.GET.get('staffs') staffs = staffs.replace('[', '') staffs = staffs.replace(']', '') staffs = staffs.split(",") staffs = [i.replace('"', '') for i in staffs] staffs = [ast.literal_eval(i) for i in staffs] # code for saving into db if request.is_ajax: for staff in staffs: if Designations.objects.filter(curriculum_id=c, role_id=mc, staff_id=staff).exists(): pass else: Designations(curriculum_id=c, role_id=mc, staff_id=staff).save() data = { 'result': 'Module Coordinators Assigned Successfully' } return JsonResponse(data) class DesignationsView(TemplateView): template_name = 'curricula/designations.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['mc_form'] = ModuleCoordinatorForm(self.request.GET or None) context['curriculum'] = Curricula.objects.get( course_period__course__course_abbreviation=self.kwargs.get('course_abbr'), module_period__module__module_abbreviation=self.kwargs.get('module_abbr'), course_period__period__academic_year=self.kwargs.get('year'), course_period__period__semester=self.kwargs.get('semester'), stage=self.kwargs.get('stage')).pk mc_qs = Designations.objects.filter(role__role_name='Module Coordinator', curriculum__course_period__course__course_abbreviation=self.kwargs.get( 'course_abbr'), curriculum__module_period__module__module_abbreviation=self.kwargs.get( 'module_abbr'), curriculum__course_period__period__academic_year=self.kwargs.get('year'), curriculum__course_period__period__semester=self.kwargs.get('semester'), curriculum__stage=self.kwargs.get('stage') ).values_list('staff', flat=True) chosen_mcs_name = [] chosen_mcs_id = [] for mc in mc_qs: chosen_mc = Staffs.objects.get(pk=mc) chosen_mcs_name.append(chosen_mc) chosen_mcs_id.append(mc) # print(chosen_mcs_name) print(chosen_mcs_id) # chosen_mcs_name_json = json.dumps(chosen_mcs_name) # print(chosen_mcs_name_json) context['chosen_mc_name'] = chosen_mcs_name context['chosen_mc_value'] = chosen_mcs_id return context urls.py: path('courses/<str:course_abbr>/<str:module_abbr>/<int:year>/<int:semester>/<int:stage>/designations', views.DesignationsView.as_view(), name='designations'), path('ajax/module_coordinator/', views.module_coordinator, name="module_coordinator") designations.html: {% extends "main_template.html" %} {% load static %} {% block title %}Designations{% endblock %} {% block css %} <style> hr { display: block; margin: 0.5em auto; border: 1px inset; } </style> {% endblock %} {% block content %} {% include 'navbar_architect.html' %} {{ mc_form.media }} <div id="content" class="colM"> <div id="content-main"> <h2>Assign Module Coordinator(s)</h2> <form enctype="multipart/form-data" method="post" novalidate> {% csrf_token %} {{ mc_form.non_field_errors }} <input type="hidden" value="{{ curriculum }}" id="id_curriculum_mc"> {{ mc_form.role_mc }} <div> <fieldset class="module aligned"> <div class="form-row"> <div> <div class="fieldWrapper related-widget-wrapper"> <div class="selector"> {{ mc_form.module_coordinator.errors }} {{ mc_form.module_coordinator }} </div> </div> </div> </div> <button type="button" class="btn btn-primary float-right" id="mc_btn">Assign</button> </fieldset> </div> </form> <hr> </div> </div> {% endblock %} {% block javascript %} <script> $(document).ready(() => { let value = "{{ chosen_mc_value }}"; value = value.replace('[', ''); value = value.replace(']', ''); value = value.split(",").map(Number); console.log(value); let name = "{{ chosen_mc_name }}".replace(/<Staffs: /g, ''); name = name.replace(/>/g, ''); name = name.replace('[', ''); name = name.replace(']', ''); name = name.split(", "); console.log(name); for (let i = 0; i < value.length; i++) { $("#id_module_coordinator_to").append(`<option value="${value[i]}">${name[i]}</option>`); //doesn't work } $('#mc_btn').click(function () { $('#id_module_coordinator_to option').prop('selected', true); let curriculum = $("#id_curriculum_mc").val(); let mc = $("#id_role_mc").val(); let staffs = $("#id_module_coordinator_to").val(); if ($("#id_module_coordinator_to").has('option').length === 0) { return false; } $.ajax({ url: '{% url 'module_coordinator' %}', data: { 'curriculum': curriculum, 'mc': mc, 'staffs': JSON.stringify(staffs) }, dataType: 'json', success: function (data) { swal({ title: data.result, toast: true, position: 'top-end', type: 'success', showConfirmButton: false, allowEscapeKey: false, allowOutsideClick: false, backdrop: false, timer: 1500, }) }, error: function () { swal({ title: "Unable to assign", toast: true, position: 'top-end', type: 'error', showConfirmButton: false, allowEscapeKey: false, allowOutsideClick: false, backdrop: false, timer: 1500, }) } }); }); }); </script> {% endblock %} Note: swal is a Javascript alert notification library
Try this in forms class ModuleCoordinatorForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(ModuleCoordinatorForm, self).__init__(*args, **kwargs) if self.instance.pk: self.fields['module_coordinator'].initial = self.instance.staff.all() self.fields['role_mc'].initial = self.instance.role.all() or this in views def edit_designations(request, id_designation): if request.method == 'GET': designation = get_object_or_404(Designations, pk=id_designation) form = ModuleCoordinatorForm(instance=designation) form.fields['module_coordinator'].initial = form.instance.staff.all() form.fields['role_mc'].initial = form.instance.role.all() return render(request, 'designation.html', {'form': form})
Symfony 3 Infinite Scrolling Ajax
I'm trying to make an infinite Scrolling in Ajax. I really don't know what is missing. Html Index: <div class="container"> <!-- Menu --> {% include '::announce/menu.html.twig' %} {% for announce in announces|slice(0, 4) %} <!-- Announce Preview --> {% include '::announce/' ~ game ~ '/preview.html.twig' %} {% endfor %} </div> <div id="content"> loop content </div> <nav id="pagination"> <p>Page 2</p> </nav> {% endblock %} {% block javascripts %} <script src="{{ asset('js/jquery-ias.min.js') }}"></script> {% endblock %} My controller index: public function indexAction(Request $request, $game) { $em = $this->getDoctrine()->getManager(); $announces $em->getRepository('PlatformBundle:Announce')->byGame($game); return $this->render('announce/index.html.twig', array( 'announces' => $announces, 'game' => $game )); } With this i have my index page with 4 announce Now i show you my code for scrolling and adding more announce Ajax File: $(window).scroll(function () { if($(window).scrollTop() + $(window).height()>= $(document).height()){ getmoredata(); } }) function getmoredata() { $.ajax({ type: "GET", url: "{{ path('announce_page', {'id': '2', 'game': game}) }}", dataType: "json", cache: false, success: function (response) { $("#content").append(response.classifiedList); $('#spinner').hide(); console.log(response); }, error: function (response) { console.log(response); } }); } And this is the controller for page: public function pageAction(Request $request, $game) { $em = $this->getDoctrine()->getManager(); $announces = $em->getRepository('PlatformBundle:Announce')->byGame($game); $list = $this->renderView('announce/result.html.twig', array( 'announces' => $announces, 'game' => $game )); $response = new JsonResponse(); $response->setData(array('classifiedList' => $list)); return $response; } Last code, it's the html content for ajax (result.html.twig) {% for announce in announces|slice(4, 4) %} <!-- Affichage Annonce --> {% include '::announce/' ~ game ~ '/preview.html.twig' %} {% endfor %} For resume, i have my index with 4 announce, when i scroll in bottom, i have a positive ajax request without error. But nothing appear after the 4 announce. If i go to announce page 2 directly with pagination, i can see 4 other announce. So my routing is working but something don't works in ajax code i think. Any idea? :) Thanks
Cannot format nested date field in keystone.js
I am extending keystone.js to support multiple languages in the models. The original model looks like this: Post.add({ title: { type: String, required: true }, publishedDate: { type: Types.Date, index: true }, //... more irrelevant fields }); My extended model looks like this: Post.add({ title: { type: String, required: true }, en: { title: { type: String }, publishedDate: { type: Types.Date, index: true }, //... more irrelevant fields }, es: { title: { type: String }, publishedDate: { type: Types.Date, index: true }, //... more irrelevant fields }, //... more languages following the same pattern }); I am having an issue using the nested publishedDate properties within the view templates. Original view code that works with the original model: {% if post.publishedDate %} <br>on {{ post._.publishedDate.format('MMMM DD, YYYY') }} {% endif %} Adjusted view code for my new model (this is where I am having the issue): {% if post[lang].publishedDate %} <br>on {{ post[lang]._.publishedDate.format('MMMM DD, YYYY') }} {% endif %} Using post[lang].xxxxxx I can refer to all of the other items in my post (for example post[lang].title. Am I missing something that is causing these nested underscore functions to fail with the following error? non_object_property_load Error thrown for request" /en/blog TypeError: Cannot read property 'publishedDate' of undefined EDIT: It gets more peculiar... below seems to work: {% if post[lang].publishedDate %} <br>on {{ post._[lang].publishedDate.format('MMMM DD, YYYY') }} {% endif %} If anyone can offer up an explanation of why this works (or a better way to accomplish this), I would really appreciate it.
#JRulle, I assume the error is on the following line: <br>on {{ post[lang]._.publishedDate.format('MMMM DD, YYYY') }} The issue is that the underscore methods belong to the List object. For example, to access the underscore methods for post.en.publishedDate you would use the following syntax: post._.en.publishedDate.format('MMMM DD, YYYY') You should change the above line to: <br>on {{ post._[lang].publishedDate.format('MMMM DD, YYYY') }}