Is there any way to include a calendar of events in my Phoenix Project?

Is there any way to include a calendar of events in my Phoenix Project?

I would like to show these fields (with dates) in a calendar:

I would like to make new fields through a calendar as well. Is it possible? Please help me :cry:

1 Like

you can achieve that via javascript, ajax and fullcalendar library.
https://fullcalendar.io

or you can create simple weekly view like that and you can generate hardcoded fields dynamically

  dates= ["2018-06-01", "2018-06-02", "2018-06-03", "2018-06-04", "2018-06-05", "2018-06-06"],
  fake_database_appointmens = [
    %{date: "2018-06-01"},
    %{date: "2018-06-01"},
    %{date: "2018-06-02"},
    %{date: "2018-06-03"}
  ]
render(conn, "whatever.hmtl", dates: dates, fake_database_appointmens: fake_database_appointmens)

and in whatever.html.eex

<div class="container">
<div class="row">

<%= for date <- @dates do %>

<div class="col"><h6><%= date %></h6> 

<%= for appointment <- Enum.filter(@fake_database_appointmens, fn x -> x.date == date end ) do %>
<div class="btn btn-primary"><%= appointment.date %></div>
<% end %>

</div>
<% end %>

</div>
</div>
4 Likes

+1 for full calendar.io here :slight_smile: I have used it for my master thesis project and it worked pretty well for me. I have a demo here if you want to have a look and see if that suits your needs: https://youtu.be/pH58U4tmZoU.
Let me know in case you need some help, although I should say I am a noob with Phoenix and Elixir so don’t rely too much on the way I implemented the solution it might not follow best practices :wink:

5 Likes

I’m a noob as well hahaha. Can you please tell me how did you implemented it on Phoenix? :slight_smile:

Not on my computer right now but as soon I have some time I’ll send you some stuff :slight_smile:

1 Like

Thank you!

As the guys pointed out, fullcalendar is the best to deal with that kind of requirement, here are some steps to help you figure it all out.

  • You need to add fullcalendar on your project by either using a script tag or using brunch and npm
  • On your current template used to render the table of appointments change it to include a simple div <div id="calendar" style="width:600px"></div>.
  • On a javascript file include the following snippet to render the calendar on the div you placed: (Events Docs)
// get the calendar element with jquery
 const calendarDiv = $('#calendar');
// check if the div is on the page
 if (calendarDiv) {
   // load the fullcalendar on div
   calendarDiv.fullCalendar({
     // events is a url to your controller that respond with json
     events: "/appointments/calendar"
   });
 }
  • Now you need to handle the events path you just added on js side. On the router.ex, add a route next to the already present resources("/appointments", AppointmentController): get("/appointments/calendar", AppointmentController, :calendar), which will add the /appointment/calendar.
  • Go to your AppointmentController. Now you need to create an action named calendar, there you will handle the data fetching for the current view of calendar such as the date range, you will get a url /appointiment/calendar?start=2013-12-01&end=2014-01-12&_=1386054751381, then you can parse the parameters, convert to a date time format and pass it to the Ecto query. Here is an example:

defmodule MyApp.AppointmentsController do
  # ... other actions
  
  def calendar(conn, %{"start" => start_date, "end" => end_date} = _params) do
    data = 
      AppointmentsContext.fetch_appointments(start_date, end_date)
      |> AppointmentsConverter.convert()
    # instead of render and a view, use json to a quick json serializing and return
    json(conn, data)
    
  end
end

Here some notes on the implementation on the example:

  • The functoin fetch_appointments needs to convert the start_date and end_date into a date time format to be usable on ecto such as Ecto.DateTime. Next we pipe the list resulting of the query to a function that convert the structs of Appointments to an map or new struct that conforms with fullcalendar’s Event Object that will return as json.

Then its done.
Hope this helps you

4 Likes

It looks great! I’ll give it a try. Thanks a lot! <3

Sorry for the late reply, it’s been a couple of hectic days :slight_smile:
To implement full calendar I have made a js file in which I implemented the calendar to then render it in the pages I wanted it to be. in the templates where I render the calendar I added a span with and id and a data attribute which corresponds to the data to be displayed on the calendar passed from the controller as so:
<span id="appointments" data-events="<%= @appointments %>" />
So the calendar js implementation reads the data from there.

In the models for the calendar event I added two functions which convert a given event model from a list into a usable formta to then be encoded in the controller and passed to the view:

def to_events_data_format(appointments) when is_list(appointments) do
  Enum.reduce(appointments, [], fn(appointment, acc) -> acc ++ [to_event_data_format(appointment)] end)
end

defp to_event_data_format(appointment) when is_map(appointment) do
  %{title: appointment.title, start: appointment.start_date <> " " <> appointment.start_time, end: 
   appointment.end_date <> " " <> appointment.end_time, url: "/appointments/#{appointment.id}"}
 end

In the controller i then query the db, call these functions to put the data in the format I want, encode it with poison and pass it to the view.

Sorry if it isn’t too clear… I hope it helps and in case ask any questions and I’ll try to answer them :slight_smile:
Best of luck

1 Like