0

I am struggling with ics.js (https://github.com/nwcell/ics.js/)

  1. It is ignoring rules for repeat events set up in rrules

  2. It is not allowing me to add another event from another .ics file after I have added a first one. However, if I delete the first one, I can add the second one.

  3. I am unable to add a calendar event using a JavaScript object.

    var myfunc = function(e) {
        let target = e.currentTarget;
        if (target.tagName != 'X-EVENT') {
          throw "downloadEvent is for use on x-events only";
        }
        let form = target.querySelector("form");
        let fd = new FormData(form);
        //console.log(fd.has('subject'));
        let recur = fd.get("freq") != "none";
        let rRule = recur ? {"freq" : fd.get("freq"), "interval" : fd.get("interval"), "byday" : fd.getAll("byday"), "until" : fd.get('until') } : null;
    
        let cal  = ics();
    //    cal.addEvent(eventData);
        cal.addEvent(fd.get("subject"),fd.get("description"), fd.get("location"),       fd.get("start"), fd.get("end"), rRule);
        cal.download(fd.get("subject"));
    }
    

As you can infer from the code snippet above, I create adding an event as an object but that failed.

How can I fix this code so that the reoccurrence instructions in rRule are obeyed?

-- edit --

The code for how I am setting up the controls is:

 let form = document.createElement("FORM");
  element.appendChild(form);
  let h = document.createElement("H1");
  h.textContent = "Event";
  form.appendChild(h);
  let evtTable = document.createElement("TABLE");
  evtTable.setAttribute("contentEditable", "false");

  form.appendChild(evtTable);

  let createRow = function(name) {
    let row = evtTable.insertRow();
    let cell = row.insertCell();
    cell.textContent = name;
    cell = row.insertCell();
    let input = document.createElement("Input");
    input.name = name.replace(/\s/,"").toLowerCase();
    if (element.hasAttribute(input.name)) {
      input.value = element.getAttribute(input.name);
    }
    cell.appendChild(input);
    return row; 
  }
    
  createRow("Subject");
  createRow("Description");
  createRow("Location");

  row = createRow("Start");
  input = row.cells[1].querySelector("input");
  console.log("input.name: " + input.name);
  input.type = "datetime-local";

  row = createRow("End");
  input = row.cells[1].querySelector("input");
  input.type = "datetime-local";
  
  row = createRow("Repeat");
  row.cells[0].title = "Only available for single day events";
  input = row.cells[1].querySelector("input");
  input.type = "radio";
  input.name = "freq";
  input.value = "none";
  input.checked = true;
  row.cells[1].insertBefore(document.createTextNode("None"), input);

  row.cells[1].appendChild(document.createTextNode("Day"));
  let input2 = document.createElement("input");
  input2.name = "freq";
  input2.value = "DAILY";
  input2.type = "radio";
  row.cells[1].appendChild(input2);

  row.cells[1].appendChild(document.createTextNode("Week"));
  input2 = document.createElement("input");
  input2.name = "freq";
  input2.value = "WEEKLY";
  input2.type = "radio";
  row.cells[1].appendChild(input2);

  row.cells[1].appendChild(document.createTextNode("Month"));
  input2 = document.createElement("input");
  input2.name = "freq";
  input2.value = "MONTHLY";
  input2.type = "radio";
  row.cells[1].appendChild(input2);

  row = createRow("Weekly Days");
  row.cells[0].title = "Days of the week for weekly reoccurance";
  input = row.cells[1].querySelector("input");

  let cell = row.cells[1];

  cell.insertBefore(document.createTextNode("Su"), input);
  input.type = "checkbox";
  input.name = "byday";
  input.value = "SU";

  cell.appendChild(document.createTextNode("M"));
  input2 = document.createElement("INPUT");
  input2.type = "checkbox";
  input2.name = "byday";
  input2.value = "MO";
  cell.appendChild(input2);

  cell.appendChild(document.createTextNode("Tu"));
  input2 = document.createElement("INPUT");
  input2.type = "checkbox";
  input2.name = "byday";
  input2.value = "TU";
  cell.appendChild(input2);

  cell.appendChild(document.createTextNode("We"));
  input2 = document.createElement("INPUT");
  input2.type = "checkbox";
  input2.name = "byday";
  input2.value = "WE";
  cell.appendChild(input2);

  cell.appendChild(document.createTextNode("Th"));
  input2 = document.createElement("INPUT");
  input2.type = "checkbox";
  input2.name = "byday";
  input2.value = "TH";
  cell.appendChild(input2);

  cell.appendChild(document.createTextNode("Fr"));
  input2 = document.createElement("INPUT");
  input2.type = "checkbox";
  input2.name = "byday";
  input2.value = "FR";
  cell.appendChild(input2);

  cell.appendChild(document.createTextNode("Sa"));
  input2 = document.createElement("INPUT");
  input2.type = "checkbox";
  input2.name = "byday";
  input2.value = "SA";
  cell.appendChild(input2);

  row.querySelectorAll("select, input, textarea").forEach ((input) => {
    input.disabled = true;
  });

  row = createRow("Interval");
  row.cells[0].title = "1 = every week, 2 = every 2 weeks";
  input = row.cells[1].querySelector("input");
  input.type = "number";
  input.name = "interval";
  input.min = 1;
  input.max = 12;
  input.value = 1;

  row.querySelectorAll("select, input, textarea").forEach ((input) => {
    input.disabled = true;
  });

  row = createRow("Until");
  row.cells[0].title = "Reoccur until this date";
  input = row.cells[1].querySelector("input");  
  input.type = "date";
  input.name = "until";


  Sections.calTableInputFunc(element, true);

  row.querySelectorAll("select, input, textarea").forEach ((input) => {
    input.disabled = true;
  });

    //console.log(JSON.stringify(formValues));
    //console.log("element html: " + element.outerHTML);
  //}

}


Sections.calTableInputFunc = (xEvent, enable) => {
  let changeFunc = (ev) => {
    ev.target.closest("x-event").setAttribute(ev.target.name, ev.target.value);
    let evtTable = xEvent.querySelector("table");
    let startRow = 6;
    if (ev.target.getAttribute("name") == "freq") {
      evtTable.rows[startRow].querySelectorAll("select, input, textarea").forEach ((input) => {
        input.disabled = ev.target.value == "none";
      });
      evtTable.rows[startRow + 1].querySelectorAll("select, input, textarea").forEach ((input) => {
        input.disabled = ev.target.value == "none";
      });
      evtTable.rows[startRow + 2].querySelectorAll("select, input, textarea").forEach ((input) => {
        input.disabled = ev.target.value == "none";
      });
    }
  }
  if (enable) {
     xEvent.querySelector("table").querySelectorAll("INPUT, TEXTAREA").forEach((elem) => elem.addEventListener("change", changeFunc));
  } else {
     xEvent.querySelector("table").querySelectorAll("INPUT, TEXTAREA").forEach((elem) => elem.removeEventListener("change", changeFunc));
   }
}

-- edit 2 --

I got the following example from Google AI that uses an r(ecurrent) rule. Seeing this fail has me thinking that this could very well be an issue with calendars not supporting the "rrule" protocol. I am going to vote to close this question as not being the correct source of my problem.

// Initialize the ics.js object
var cal = ics();

// Define the recurrence rule (rrule) for a weekly event
var rrule = {
  freq: 'WEEKLY', // Event repeats weekly
  interval: 1,    // Every week
  byday: ['MO', 'WE', 'FR'], // On Mondays, Wednesdays, and Fridays
  until: new Date(2026, 0, 31) // Repeats until January 31, 2026
};

// Add the recurring event
cal.addEvent(
  'Weekly Team Meeting', // Subject
  'Discussion on project progress', // Description
  'Conference Room A', // Location
  '2025-09-22 09:00', // Begin date and time (first occurrence)
  '2025-09-22 10:00', // End date and time (first occurrence)
  rrule // The recurrence rule object
);

// Download the generated ICS file
cal.download('my_recurring_events');
0

2 Answers 2

0

To fix recurrence rules in ics.js, update your rRule object to use uppercase freq values (e.g., 'WEEKLY', 'MONTHLY') as required by the library. So: fd.get("freq").toUpperCase()

Sign up to request clarification or add additional context in comments.

3 Comments

be a bit more strict on the others too: let rRule = recur ? { freq: fd.get("freq").toUpperCase(), // Convert to uppercase (e.g., "WEEKLY") interval: parseInt(fd.get("interval"), 10), // Ensure integer byday: fd.getAll("byday"), // Array of days (e.g., ["MO", "WE"]) until: new Date(fd.get("until")) // Parse to Date } : null;
Thank you for your suggestion! I have updated my post with the code for the form. As you can see, all of my values are already uppercase. Is there anything else I can try?
In all honesty, I just need an example with an rrule that works. If I had that, I could match it in my code.
0

The solution was to go into the ics.js library itself and replace the text "rrule with "RRULE (as per the ICS specifications) I believe that on the ics.js GitHub website (https://github.com/nwcell/ics.js/issues), they mention this issue as well as the fix.

A related issue I ran into testing on a Mac was that I couldn't load an ICS event from a file if I already had one in my calendar. The answer to this is to not double click the .ics file, but rather to use the "import" menu item in Calendar.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.