Creating/Editing Events on the Front End
Solspace Calendar allows you to create and edit events on the front end as well. Due to the complexity of the Event element UI, it's best to observe its functionality and example code in the Demo Templates. Simply install the Demo Templates that come included with Calendar, and click to the Create tab.
Overview
A few important things to note are that:
- All Calendar Event fields are part of the
calendarEvent[]
array. - Title and slug fields are standalone fields.
- Custom fields are all part of
fields[]
array. So if your field handle is location, your input name value would befields[location]
Example Code from Demo Templates
{% extends "demo/layouts/_layout.html" %}
{% set pageTitle = "Event" %}
{% set page = "edit" %}
{% set dateFormat = craft.app.locale.getDateFormat('short', 'php') %}
{% set dateFormatJs = craft.app.locale.getDateFormat('short', 'jui') %}
{% set timeFormat = craft.app.locale.getTimeFormat('short', 'php') %}
{% set timeFormatJs = craft.app.locale.getTimeFormat('short', 'jui') %}
{% block content %}
{% set eventId = segment4 == "" ? "new" : segment4 %}
<h1>Event Edit</h1>
<p class="lead">
Below is an individual event edit form, which allows you to edit core event details.
</p>
<hr /><br />
<div class="row">
<div class="col-md-9">
<div id="events_calendar">
{% set event = craft.calendar.event(eventId, {
loadOccurrences: false
}) %}
{% if event %}
{% requireEventEditPermissions event %}
<form method="post">
{{ csrfInput() }}
<input type="hidden" name="action" value="calendar/legacy-events/save-event" />
<input type="hidden" name="redirect" value="{{ (siteUrl~"demo/event/{id}")|hash }}" />
<input type="hidden" name="eventId" value="{{ event.id }}" />
<input type="hidden" name="slug" value="{{ event.slug }}" />
{% if errors is defined %}
<div class="form-group row">
<div class="col-sm-5 col-sm-offset-2">
{% for fieldErrors in errors %}
{% for error in fieldErrors %}
<div style="color: darkred;">· {{ error }}</div>
{% endfor %}
{% endfor %}
</div>
</div>
{% endif %}
<div class="form-group row">
<label class="col-sm-2 col-form-label">{{ "Title"|t }}</label>
<div class="col-sm-10">
<input type="text"
name="title"
value="{{ event.title }}"
placeholder="{{ "Title"|t }}"
class="form-control" />
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">{{ "Calendar"|t }}</label>
<div class="col-sm-10 col-md-5">
<select name="calendarEvent[calendarId]" class="form-control">
{% for calendar in craft.calendar.allowedCalendars %}
<option value="{{ calendar.id }}" {{ calendar.id == event.calendar.id ? "selected" }}>
{{ calendar.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">{{ "Start Date"|t }}</label>
<div class="col-sm-10">
<div class="row">
<div class="col-md-4 col-xs-8">
<input type="text"
name="calendarEvent[startDate][date]"
value="{{ event.startDate.format(dateFormat)|default }}"
placeholder="{{ dateFormat }}"
class="form-control date-picker" />
</div>
<div class="col-md-2 col-xs-4" data-not-all-day>
<input type="text"
value="{{ event.startDate.format(timeFormat)|default }}"
name="calendarEvent[startDate][time]"
placeholder="{{ timeFormat }}"
class="form-control time-picker" />
</div>
</div>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">{{ "End Date"|t }}</label>
<div class="col-sm-10">
<div class="row">
<div class="col-md-4 col-xs-8">
<input type="text"
value="{{ event.endDate.format(dateFormat)|default }}"
name="calendarEvent[endDate][date]"
placeholder="{{ dateFormat }}"
class="form-control date-picker" />
</div>
<div class="col-md-2 col-xs-4" data-not-all-day>
<input type="text"
value="{{ event.endDate.format(timeFormat)|default }}"
name="calendarEvent[endDate][time]"
placeholder="{{ timeFormat }}"
class="form-control time-picker" />
</div>
</div>
</div>
</div>
<div class="form-group row">
<div class="col-2 col-form-label">{{ "All Day"|t }}</div>
<div class="col-8">
<input type="hidden"
id="allDay"
name="calendarEvent[allDay]"
value="{{ event.allDay }}"
data-toggle-button
/>
</div>
</div>
<div class="form-group row">
<div class="col-2 col-form-label">{{ "Multi Day"|t }}</div>
<div class="col-8">
<input type="hidden"
id="multiDay"
name="calendarEvent[multiDay]"
value="{{ event.isMultiDay }}"
data-toggle-button
/>
</div>
</div>
{# =============== #}
{# Repeating rules #}
{# =============== #}
<div class="form-group row">
<label class="col-2 col-form-label">
{{ "Repeats"|t }}
</label>
<div class="col-10" id="repeats-switch-wrapper">
<input type="hidden"
id="repeats" name="calendarEvent[repeats]"
value="{{ event.repeating }}"
data-label="Repeats"
data-toggle-button
/>
</div>
</div>
<div class="form-group row">
<label class="col-2 col-form-label"
data-repeats>
{{ "Every"|t }}
</label>
<div class="col-sm-2 col-3" data-repeats data-invisible-for="select_dates">
<input type="text"
name="calendarEvent[interval]"
value="{{ event.interval|default(1) }}"
class="form-control" />
</div>
<div class="col-sm-5 col-7 offset-sm-0" data-repeats>
<select name="calendarEvent[frequency]"
id="freq-selector"
class="form-control">
{% for freq, freqTitle in craft.calendar.frequencyOptions %}
<option value="{{ freq }}"
{% if freq == event.freq %} selected{% endif %}>
{{ freqTitle|t }}
</option>
{% endfor %}
</select>
</div>
</div>
{# ======================== #}
{# Frequency-specific rules #}
{# ======================== #}
<div class="form-group row freq-items freq-list" data-weekly data-repeats>
<div class="col-sm-10 offset-sm-2">
{% for day, dayName in craft.calendar.weekDaysShort %}
<input type="checkbox"
id="repeats-by-weekday-{{ day }}"
value="{{ day }}"
{{ day in event.byDay ? "checked" }}
name="calendarEvent[weekly][repeatsByWeekDay][]" />
<label for="repeats-by-weekday-{{ day }}">{{ dayName }}</label>
{% endfor %}
</div>
</div>
<div class="form-group row freq-items" data-monthly data-repeats>
<div class="col-4 col-sm-2 offset-sm-2" style="padding-top: 6px;">
<label>
{% set checked = event.byMonthDay or not event.byDay ? ' checked' : '' %}
<input type="radio"
name="calendarEvent[monthly][repeatsBy]"
data-toggle-switch
data-toggle-target="monthly"
{{ checked }}
value="byMonthDay">
{{ "Each"|t }}
</label>
</div>
<div class="col-4 col-sm-2" style="padding-top: 6px; text-align: right;">
<label>
{% set checked = event.byDay ? ' checked' : '' %}
<input type="radio"
name="calendarEvent[monthly][repeatsBy]"
data-toggle-switch
data-toggle-target="monthly"
{{ checked }}
value="byDay">
{{ "On the"|t }}
</label>
</div>
<div class="col-4 col-sm-2">
<select name="calendarEvent[monthly][repeatsByDayInterval]" class="form-control">
{% for interval, intervalTitle in craft.calendar.repeatsByOptions %}
<option value="{{ interval }}" {{ interval == event.repeatsOnRule ? "selected" }}>
{{ intervalTitle|t }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row freq-items freq-list"
data-monthly
data-toggled-by="byDay"
data-repeats>
<div class="col-sm-10 offset-sm-2">
{% for day, dayName in craft.calendar.weekDaysShort %}
<input type="checkbox"
id="repeats-by-month-week-day-{{ day }}"
value="{{ day }}"
{{ day in event.byDay ? "checked" }}
name="calendarEvent[monthly][repeatsByWeekDay][]" />
<label for="repeats-by-month-week-day-{{ day }}">{{ dayName }}</label>
{% endfor %}
</div>
</div>
<div class="form-group row freq-items freq-list"
data-monthly
data-toggled-by="byMonthDay"
data-repeats>
<div class="col-sm-10 offset-sm-2">
{% for day, dayName in craft.calendar.monthDays %}
<input type="checkbox"
id="repeats-by-month-day-{{ day }}"
value="{{ day }}"
{{ day in event.byMonthDay ? "checked" }}
name="calendarEvent[monthly][repeatsByMonthDay][]" />
<label for="repeats-by-month-day-{{ day }}">{{ dayName }}</label>
{% endfor %}
</div>
</div>
<div class="form-group row freq-items" data-yearly data-repeats>
<div class="col-5 col-sm-2 offset-sm-2" style="padding-top: 6px;">
<label>
<input type="checkbox"
name="calendarEvent[yearly][repeatsBy]"
data-toggle-switch
data-toggle-target="yearly"
{{ event.repeatsOnRule != 0 ? 'checked' : '' }}
value="byDay">
{{ "On the"|t }}
</label>
</div>
<div class="col-7 col-sm-2">
<select name="calendarEvent[yearly][repeatsByDayInterval]" class="form-control">
{% for interval, intervalTitle in craft.calendar.repeatsByOptions %}
<option value="{{ interval }}" {{ interval == event.repeatsOnRule ? "selected" }}>
{{ intervalTitle|t }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group row freq-items freq-list"
data-yearly
data-toggled-by="byDay"
data-repeats>
<div class="col-sm-10 offset-sm-2">
{% for day, dayName in craft.calendar.weekDaysShort %}
<input type="checkbox"
id="repeats-by-year-week-day-{{ day }}"
value="{{ day }}"
{{ day in event.byDay ? "checked" }}
name="calendarEvent[yearly][repeatsByWeekDay][]" />
<label for="repeats-by-year-week-day-{{ day }}">{{ dayName }}</label>
{% endfor %}
</div>
</div>
<div class="form-group row freq-items freq-list freq-list-large"
data-yearly
data-toggled-by="byDay"
data-repeats>
<div class="col-sm-6 offset-sm-2 clearfix">
<div class="row">
{% for month, monthName in craft.calendar.monthNames %}
<div class="col-xs-4">
<input type="checkbox"
id="repeats-by-month-{{ month }}"
value="{{ month }}"
{{ month in event.byMonth ? "checked" }}
name="calendarEvent[yearly][repeatsByMonth][]" />
<label for="repeats-by-month-{{ month }}">{{ monthName }}</label>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="form-group row"
data-hide-for="select_dates"
data-repeats>
<label class="col-12 col-sm-2 col-form-label">
End Repeat
</label>
<div class="col-6 col-sm-2">
<select name="calendarEvent[untilType]"
data-toggle-switch
data-toggle-target="until"
class="form-control">
{% set untilTypes = {
'forever': "Never ends"|t,
'until': "On date"|t,
'count': "After"|t
} %}
{% for key, value in untilTypes %}
<option value="{{ key }}" {{ key == event.untilType ? "selected" }}>
{{ value }}
</option>
{% endfor %}
</select>
</div>
<div class="col-6 col-sm-2" data-toggled-by="until" data-until>
<input type="text"
name="calendarEvent[untilDate][date]"
value="{{ event.until.format(dateFormat)|default }}"
placeholder="{{ dateFormat }}"
class="date-picker form-control" />
</div>
<div class="col-6 col-sm-2" data-toggled-by="count" data-until>
<div class="row">
<div class="col-6">
<input type="text"
name="calendarEvent[count]"
value="{{ event.count ? event.count : 1 }}"
placeholder="1"
class="form-control" />
</div>
<div class="col-6" style="padding-top: 6px;">
times
</div>
</div>
</div>
</div>
<div class="form-group row" data-repeats data-hide-for="select_dates">
<label class="col-sm-2 col-form-label">
Except on
</label>
<div class="col-sm-2">
<input type="text"
name="calendarEvent[exceptionDate]"
class="form-control date-picker exception-date-picker"
placeholder="{{ dateFormat }}" />
</div>
</div>
<div class="form-group row exception-list" data-repeats data-hide-for="select_dates">
{% for exception in event.exceptions %}
<div class="col-12 col-sm-10 offset-sm-2">
<input type="hidden" name="calendarEvent[exceptions][]"
value="{{ exception.date.toDateTimeString }}" />
<a href="javascript:;"
class="remove-exception"
style="color: darkred; text-decoration: none; position: relative; top: 1px;">
<span class="far fa-times-circle"></span>
</a>
{{ exception.date.format('l, F j, Y') }}
</div>
{% endfor %}
</div>
<div class="form-group row" data-repeats data-show-for="select_dates">
<label class="col-sm-2 col-form-label">
On select dates
</label>
<div class="col-sm-2">
<input type="text"
name="calendarEvent[selectDate]"
class="form-control date-picker select-date-picker"
placeholder="{{ dateFormat }}" />
</div>
</div>
<div class="form-group row select-dates-list" data-repeats data-show-for="select_dates">
{% for selectDate in event.selectDatesAsDates %}
<div class="col-12 col-sm-10 offset-sm-2">
<input type="hidden" name="calendarEvent[selectDates][]" value="{{ selectDate.toDateTimeString }}" />
<a href="javascript:;"
class="remove-select-date"
style="color: darkred; text-decoration: none; position: relative; top: 1px;">
<span class="far fa-times-circle"></span>
</a>
{{ selectDate.format('l, F j, Y') }}
</div>
{% endfor %}
</div>
{% if event['location'] is defined %}
<div class="form-group row">
<label class="col-sm-2 col-form-label">{{ "Location"|t }}</label>
<div class="col-sm-10">
<input type="text"
name="fields[location]"
value="{{ event.location }}"
placeholder="{{ "Location"|t }}"
class="form-control" />
</div>
</div>
{% endif %}
{% if event['description'] is defined %}
<div class="form-group row">
<label class="col-sm-2 col-form-label">{{ "Description"|t }}</label>
<div class="col-sm-10">
<textarea
name="fields[description]"
placeholder="{{ "Description"|t }}"
class="form-control">{{ event.description }}</textarea>
</div>
</div>
{% endif %}
<div class="form-group row">
<div class="col-12 col-sm-10 offset-sm-2">
<button type="submit" class="btn btn-success">
Submit
</button>
</div>
</div>
</form>
{% else %}
<div class="alert alert-danger" role="alert">
<p class="lead">
Sorry, no event was found.
</p>
</div>
{% endif %}
</div>
</div>
<div class="col-md-3">
{% include "demo/layouts/_sidebar.html" %}
</div>
</div>
{% endblock %}
{% block footerJs %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/timepicker@1.11.12/jquery.timepicker.min.css">
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
<script src="//code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/timepicker@1.11.12/jquery.timepicker.min.js"></script>
<script>
var dateFormat = "{{ dateFormatJs }}";
var timeFormat = "{{ timeFormat }}";
$(function () {
var $allDay = $("#allDay");
var $repeats = $("#repeats");
var $freq = $("select#freq-selector");
var $toggleSwitch = $("*[data-toggle-switch]");
$(".date-picker").datepicker({
dateFormat: dateFormat,
firstDay: {{ craft.calendar.settings.firstDayOfWeek }},
});
$(".time-picker").timepicker({
timeFormat: timeFormat,
});
$("*[data-toggle-button]").each(function() {
var self = $(this);
var button = $("<button />", {
class: "btn btn-" + (self.val() == 0 ? "secondary" : "primary"),
type: "button",
text: self.val() == 0 ? "Off" : "On",
});
button.on({
click: function () {
self.val(1 - self.val());
$(this)
.removeClass("btn-primary btn-secondary")
.addClass(self.val() == 0 ? "btn-secondary" : "btn-primary")
.text(self.val() == 0 ? "Off" : "On");
self.trigger("change");
}
});
self.after(button);
});
$toggleSwitch.on({
click: function () {
$(this).trigger("change");
},
change: function () {
var value = $(this).val();
var isSelected = $(this).prop("checked");
var targetId = $(this).data("toggle-target");
if ($(this).is(":radio")) {
isSelected = true;
var input = $("input:radio[name='" + $(this).attr("name") + "']:checked");
value = input.val();
targetId = input.data("toggle-target");
}
var $target = $("*[data-" + targetId + "][data-toggled-by=" + value + "]");
var $hideTarget = $("*[data-" + targetId + "][data-not-toggled-by=" + value + "]");
var $siblings = $("*[data-" + targetId + "][data-toggled-by]");
if ($(this).is("select")) {
isSelected = true;
}
$siblings.addClass("d-none");
if (isSelected) {
$target.removeClass("d-none");
$hideTarget.addClass("d-none");
}
},
});
$(".exception-list, .select-dates-list").on({
click: function () {
$(this).parent().remove();
},
}, "a.remove-exception, a.remove-select-date");
$allDay.on({
change: function () {
var isChecked = $(this).val() == 1;
if (isChecked) {
$("*[data-not-all-day]").addClass("d-none");
} else {
$("*[data-not-all-day]").removeClass("d-none");
}
},
});
$repeats.on({
change: function () {
var isChecked = $(this).val() == 1;
if (isChecked) {
$("*[data-repeats]:not(.freq-list)").removeClass("d-none");
$("*[data-repeats].freq-list[data-weekly]").removeClass("d-none");
$("#repeats-switch-wrapper")
.removeClass("col-sm-offset-1 col-xs-12")
.addClass("col-xs-2");
} else {
$("*[data-repeats]").addClass("d-none");
$("#repeats-switch-wrapper")
.addClass("col-sm-offset-1 col-xs-12")
.removeClass("col-xs-2");
}
},
});
$freq.on({
change: function () {
var val = $(this).val().toLowerCase();
$(".freq-items").hide();
$(".freq-items[data-" + val + "]").show();
if (val == "select_dates") {
$("*[data-hide-for=select_dates]").hide();
$("*[data-invisible-for=select_dates]").css({ visibility: "hidden" });
$("*[data-show-for=select_dates]").show();
} else {
$("*[data-hide-for=select_dates]").show();
$("*[data-invisible-for=select_dates]").css({ visibility: "visible" });
$("*[data-show-for=select_dates]").hide();
}
},
});
$allDay.trigger("change");
$repeats.trigger("change");
$freq.trigger("change");
$toggleSwitch.trigger("change");
var $exceptionDate = $(".exception-date-picker");
$exceptionDate.datepicker("option", "onSelect", function (dateText) {
var date = new moment(dateText, convertDateFormatToMomentFormat(dateFormat));
var $div = $("<div class=\"col-12 col-sm-10 offset-sm-2\" />");
var $input = $("<input />");
$input
.attr("type", "hidden")
.attr("name", "calendarEvent[exceptions][]")
.val(date.format("YYYY-MM-DD"));
$div
.append("<a href=\"javascript:;\" class=\"remove-exception\" style=\"color: darkred; text-decoration: none; position: relative; top: 1px;\"><span class=\"far fa-times-circle\"></span></a>")
.append(" " + date.format("dddd, MMMM D, YYYY"))
.append($input);
$(".exception-list").append($div);
$exceptionDate.val("");
});
var $selectDate = $(".select-date-picker");
$selectDate.datepicker("option", "onSelect", function (dateText) {
var date = new moment(dateText, convertDateFormatToMomentFormat(dateFormat));
var $div = $("<div class=\"col-12 col-sm-10 offset-sm-2\" />");
var $input = $("<input />");
$input
.attr("type", "hidden")
.attr("name", "calendarEvent[selectDates][]")
.val(date.format("YYYY-MM-DD"));
$div
.append("<a href=\"javascript:;\" class=\"remove-select-date\" style=\"color: darkred; text-decoration: none; position: relative; top: 1px;\"><span class=\"far fa-times-circle\"></span></a>")
.append(" " + date.format("dddd, MMMM D, YYYY"))
.append($input);
$(".select-dates-list").append($div);
$selectDate.val("");
});
});
function convertDateFormatToMomentFormat(format) {
format = format
.replace(/m/g, "M")
.replace(/d/g, "D")
.replace(/yy/g, "YYYY");
return format;
}
</script>
{% endblock %}