Retool Custom Component using jquery and bootstrap $ is not defined

I am building a custom retool component using jquery and bootstrap.
When it loads my code, they said $ is not defined.
I think all existing UIs are broken at first. Even I loaded bootstrap, all UI tags are loaded without bootstrap and after some time period, it loaded bootstrap and displayed correctly what I expec

Hi @Alex_Napoles

Try to add source code, it'd be helpful for those that are willing to help you.

Hope this help.

.opt-tools { height: 100%; width: 25px; right: -21px; position: absolute; border: 1px solid #e8e8e8; border-top-right-radius: 8px; border-bottom-right-radius: 8px; } .opt-edit, .opt-trash { height: 50%; line-height: 25px; text-align: center; font-size: 13px; cursor: pointer; } .opt-edit { border-bottom: 1px solid #e8e8e8; border-top-right-radius: 8px; background-color: #fff; color: #ff8e00; }
      .opt-edit:hover {
        color: #fff;
        background-color: #ff8e00;
      }
      .opt-trash {
        border-bottom-right-radius: 8px;
        background-color: #fff;
        color: #cc0000;
      }
      .opt-trash:hover {
        color: #fff;
        background-color: #cc0000;
      }
      
      /* LOCK HEADER AND FIRST COLUMN */
      .table td {
        padding: 5px !important;
      }
      th:first-child {
        position: sticky;
        left: 0;
        z-index: 1030;
      }
      td:first-child {
        position: sticky;
        left: 0;
        z-index: 1010;
      }
      thead th {
        position: sticky;
        top: 0;
        z-index: 1020;
      }
      /* ---------- END (LOCK HEADER AND FIRST COLUMN ) --------- */
      
      .datetimepicker {
        width: 70% !important;
        height: 70% !important;
      }
      
      .cal-container {
        max-width: 900px;
        max-height: 500px;
        overflow: auto;
        margin: auto;
      }
      .cal-table {
        position: relative;
        border: solid #ebebeb;
        border-width: 0 1px 0 0;
        overscroll-behavior: contain;
      }
      .cal-thead {
        top: 0px;
        box-shadow: 0 10px 50px rgba(0, 0, 0, 0.04);
      }
      
      .cal-viewmonth {
        font-size: 16px;
        background: #fdfdfd;
        width: 150px;
        height: 50px;
      }
    
     
      .cal-toprow {
        
        width: 182px;
        min-width: 182px;
        color: #3e5569;
        background-color: #f7f9fb !important;
        border: 1px solid #ebebeb !important;
      }
      
      .cal-viewmonth,
      .cal-toprow {
        font-weight: 700 !important;
        text-align: center;
        vertical-align: middle !important;
      }
      
      .cal-userinfo {
        height: 80px;
        box-shadow: 20px 0 50px rgba(0, 0, 0, 0.05);
      }
      
      .cal-tbody .cal-userinfo {
        min-width: 200 !important;
        text-align: right;
        color: hsla(210, 5%, 40%, 1);
        font-weight: 600;
        font-size: 12px;
        letter-spacing: 0.03em;
        background: #fdfdfd;
        padding: 0.3em;
        border: 1px solid #ebebeb;
      }
      
      .cal-usersheader {
        height: 20px;
        box-shadow: 20px 0 50px rgba(0, 0, 0, 0.05);
        min-width: 150px !important;
        text-align: center;
        font-weight: bold;
        font-size: 15px;
        letter-spacing: 0.03em;
        padding: 0.3em;
      }
      
      .weekend {
        background-color: #b5b5b5;
      }
      .drag {
        z-index: 10;
        cursor: all-scroll;
      }
      .span-info {
        display: inline-block;
        padding: 0.25em 0.4em;
        text-align: center;
        white-space: nowrap;
        vertical-align: baseline;
        width: 25px;
        background-color: red;
        cursor: pointer;
      }
      
      .ui-draggable-dragging {
        z-index: 9999 !important;
      }
      
      .cal-usercounter {
        bottom: 0;
        right: 0;
        position: absolute;
        text-align: right;
        border-radius: 0px;
        border: 2px thick red;
      }
      
      .cal-userbadge {
        border-radius: 0;
        font-weight: 600;
        font-size: 11px;
      }
      
      /* USER TASKS */
      .details {
        border-radius: 4px;
        background: #fff;
        box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
        border: 1px solid #ebecee;
        padding: 0px 0px 5px 10px;
        margin: 2px;
        z-index: 1;
      }
      
      .details-uren {
        font-size: 12px;
        color: #333;
        font-weight: 500;
        margin: 0;
        right: 0px;
        text-align: right;
        padding-right: 10px;
      }
      
      .details-task {
        margin-top: 5px;
        margin-bottom: 2px;
        font-size: 12px;
        padding: 2px 5px;
        font-weight: 600;
        line-height: 1.4;
        border-radius: 2px;
        width: 94%;
      }
      
      .user-name {
        font-size: 16px;
      }
      .user-desc {
        font-size: 14px;
      }
      .is-today {
        display: flex;
        align-item: center;
        justify-content: center;
        color: #22357d
      }
      .is-today a {
        background: #22357d;
        color: white;
        border-radius: 100%;
        padding-top: 6px;
        aspect-ratio: 1;
        display: block;
        width: 30px;
        height: 30px;
        line-height: 1;
      }
    </style>
</head>
Prev Jul 10 - Jul 17 Next Today
        </thead>
        <tbody class="cal-tbody">

        </tbody>
      </table>
    </div>
  </div>
</div>

<!-- DISPLAY MODAL: EDIT -->
<div class="modal fade" id="edittask" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Edit Task</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div id="modal-edit" class="modal-body">
        <div class="input-group mb-2 text-center">
          <i style="color:red">Edit box is only for preview purposes and does not save any data.</i>
        </div>
        <div class="input-group mb-2">
          <label for="cono1" class="col-sm-2 text-left control-label col-form-label">Task:</label>
          <input type="text" class="form-control" id="taak" placeholder="Taak">
        </div>
        <div class="input-group mb-2">
          <label for="cono1" class="col-sm-2 text-left control-label col-form-label">Date:</label>
          <input id="date" class="form-control taskstart" placeholder="dd/mm/yyyy" type="text">
        </div>
        <div class="input-group">
          <div class="form-group" style="width:125px; margin-left:15px; margin-right:5px;">
            <label for="cono1" class="col-sm-3 text-left control-label col-form-label" style="padding-left: 0px;">Text:</label>
            <input type="text" id="ktxt" data-jscolor="" class="form-control" name="ctxt" value="" onchange="changeColor('ctxt', this.value);">
          </div>
          <div class="form-group" style="width:125px; margin-left:5px; margin-right:5px;">
            <label for="cono1" class="col-sm-3 text-left control-label col-form-label" style="padding-left: 0px;">Background:</label>
            <input type="text" id="kbg" data-jscolor="" class="form-control" name="cbg" value="" onchange="changeColor('cbg', this.value);">
          </div>
          <div class="form-group" style="width:175px; margin-left:5px;">
            <label for="cono1" class="col-sm-5 text-left control-label col-form-label" style="padding-left: 0px;">Preview:</label>
            <div id="demotaak1" data-taskid="3" class="form-control details" style="border-left:5px solid #959595; position:relative; height: 50px;">
              <h3 id="demotaak2" class="details-task" style="background:#959595; color:#FFFFFF">Example</h3>
              <p class="details-uren">08:00 - 16:30</p>
            </div>
          </div>
        </div>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

<!-- DISPLAY MODAL: DELETE -->
<div class="modal fade" id="deletetask" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">

      <div id="modal-delete" class="modal-body" style="text-align: center;">
      </div>
      <div class="modal-footer">
        <input id="taskdelid" type="hidden" value="">
        <button type="button" class="btn btn-warning" data-dismiss="modal">Cancel</button>
        <button id="confdelete" type="button" class="btn btn-danger">Yes</button>

      </div>
    </div>
  </div>
</div>
<script>
  var users;
  var tasks;
  let currentWeekNumber = 0;
  let year = 0;
  let today = new Date();
  const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  const WEEKDAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
  
  Date.prototype.addDays = function (days) {
    this.setDate(this.getDate() + parseInt(days));
      return this;
  }

  function getWeekNumberFromDate(date) {
    let d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
    let dayNum = d.getUTCDay() || 7;
    d.setUTCDate(d.getUTCDate() + 4 - dayNum);
    let yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
    return Math.ceil(((d - yearStart) / 86400000 + 1) / 7);
  }

  function getStartAndEndDateFromWeekNumber(year, weekNumber) {
    let janFirst = new Date(year, 0, 1);
    let firstDay = janFirst.getDay();
    let weekStart = 7 * (weekNumber - 1) - firstDay + 1 + 1;
    let startDate = new Date(year, 0, weekStart);
    let endDate = new Date(year, 0, weekStart + 6);
  
    // Adjust for years where the week does not start on January 1st
    if (firstDay > 4) {
      startDate.setDate(weekStart + 8 - firstDay);
      endDate.setDate(weekStart + 14 - firstDay);
    }
    //remove this +1 if you want to start week from Sunday
    
    return {
      start: startDate,
      end: endDate
    };
  }

  function initiate() {
    today = new Date();
    let day = today.getDate();
    let month = today.getMonth() + 1;
    year = today.getFullYear();
    currentWeekNumber = getWeekNumberFromDate(today);
    setCurrentWeekText();  
  }

  function setCurrentWeekText() {
    let dates = getStartAndEndDateFromWeekNumber(year, currentWeekNumber);
    console.log('dates>>>>>>>>>>>>>>>', dates);
    const startDate = dates.start;
    const endDate = dates.end;
    let text = '' + MONTHS[startDate.getMonth()] + ' ' + startDate.getDate() + ' - ';
    text += '' + MONTHS[endDate.getMonth()] + ' ' + endDate.getDate() + ' ';
    $('#current_week').text(text);
    console.log('start date', startDate);
    console.log('end date', endDate);
  
    generateHeaderWeek(startDate, endDate);
    startDate.addDays(-7);
    generateBodyData(startDate, endDate);
    importTasks(startDate, endDate);
  }

  function importTasks() {
    for(let i = 0; i < tasks.length; i++) {
      console.log('task data', tasks[i].date);
      var day = tasks[i].date.split('-')[2];
      var month = tasks[i].date.split('-')[1];
      var year = tasks[i].date.split('-')[0];
      var user = tasks[i].user_id;
      $('#' + user + '_' + day + '_' + month + '_' + year).html(
          '<div class=" details ui-draggable ui-draggable-handle" data-taskid="'+ tasks[i].id +'"  data-timespan="' + tasks[i].timespan + '" data-userid="'+ user +'" style="border-left: 5px solid '+tasks[i].color+'; position: relative;">' +
                            '<h3 class="details-task" style=" background: '+ tasks[i].color +'; color: #FFFFFF">' + tasks[i].name+ '</h3>' +
                            '<div class="details-uren">' +
                            '' +tasks[i].starttime.split(":").slice(0, 2).join(":")+ ' - '+tasks[i].endtime.split(":").slice(0, 2).join(":")+'' +
                            '</div>' +
                          '</div>'
      );
      //12:15:00.000 -> 12:15
    }
    //update hours per week for users.
    for(let i = 0; i < users.length; i++)
    {
      let elements = document.querySelectorAll("div[data-userid='" + users[i].id + "']");
      let total = 0;
      for(let j = 0; j < elements.length; j++) 
      {
        let timespan = elements[j].getAttribute('data-timespan');
        total += parseInt(timespan);
      }
      let text = "";
      let hour = total / 60;
      let min = total % 60;

      text += hour + 'hr';
      if(min != 0) {
        text += ' ' + min + 'm';
      }
      $('#week_hour_' + users[i].id).text(text);
    }
  }

  function checkIfToday(date)
  {
     return (
      date.getFullYear() === new Date().getFullYear() &&
      date.getMonth() === new Date().getMonth() &&
      date.getDate() === new Date().getDate()
    );
  }

  function generateHeaderWeek(s, e) {
    let originalDate = s;
    originalDate.addDays(-1);
    //sub a day because it is added in following iteration
    let res = [];
    for(let i = 0; i < 7; i++) {
      originalDate.addDays(1);
      let temp = {};
      temp.day = originalDate.getDate();
      temp.weekday = WEEKDAYS[originalDate.getDay()];
      if(checkIfToday(originalDate)) {
        temp.isToday = true;
      } else {
        temp.isToday = false;
      }
      res.push(temp);
    }
  
    //generate table header week(Monday 1, Tuesday 2, ...)
    let text = `<th class="cal-viewmonth" id="changemonth"><input type='text' placeholder='Search schedule'></th>`;
    for(let n = 0; n < res.length; n++) {
      text += `<th class="cal-toprow p-3"><div class='mb-2'>` + res[n].weekday + `</div><div class=${res[n].isToday ? 'is-today' : ''}><a>` +res[n].day+ `</a></div></th>`;
    } 
  
    $('#week_header tr').html(text);
  
  }

  function generateBodyData(start, end) { 
    let originDate = start;
    var text = ""
    users.map((user, index) => {
      text += "<tr id='u" + (index + 1 )+ "'>";
      //first td (user avatar and user info cell)
      text += "<td class='cal-userinfo'>" +
                "<div class='px-1 d-flex align-items-center justify-content-start h-100 gap-3'>" +
                  "<img src='" + user.avatar + "' width='50' height='50' class='rounded-pill border border-1'>" +
                  "<div class='d-flex align-items-start justify-content-center flex-column ml-3'>" +
                    "<strong><p class='mb-0 user-name'>" + user.name + "</p></strong>" +
                    "<p class='mb-0 user-desc'><span id='week_hour_" + user.id + "'></span>| " + user.job + "</p>" +
                  "</div>" +
                "</div>" +
                "</td>";
      // week cell
      for(let i = 0; i < 7; i++) {
        originDate.setDate(originDate.getDate() + 1);
        text += "<td id='"+user.id+"_"+originDate.getDate()+"_"+(originDate.getMonth() + 1)+"_"+originDate.getFullYear()+"' class='ui-droppable' data-date='" + originDate.getDate() + "/" + (originDate.getMonth() + 1) + "/" + originDate.getFullYear() + "' data-userid='" + user.id + "'></td>";
      }
      originDate.setDate(originDate.getDate() - 7);
      text += "</tr>";
    })
    console.log('text', text);
    $('.cal-tbody').html(text);
  }

  function setTodayWeek() {
    initiate();
  }
  
  function setNextWeek() {
    currentWeekNumber++;
    setCurrentWeekText();
  }
  
  function setPrevWeek() {
    currentWeekNumber--;
    setCurrentWeekText();
  }

  $(document).ready(function() {
    console.log('users', users);
    initiate();
  })

// // Show modal to edit task
// $(document).on("click", ".opt-edit", function () {
// // Get task ID and DATE from DATA attribute
// var taskid = $(this).parent().parent().data("taskid"),
// userid = $(this).parent().parent().data("userid");
// // Get DATE
// var date = $(this).closest("td").data("date");
// // insert data to Modal
// $("#ktxt")[0].jscolor.fromString("FFFFFF");
// $("#kbg")[0].jscolor.fromString("8E8E8E");
// $("#demotaak2").css("color", "#FFFFFF");
// $("#demotaak1").css("border-left-color", "#8E8E8E");
// $("#demotaak2").css("background-color", "#8E8E8E");
// $("#edittask").modal("show");
// });

// // Modal remove task ?
// $(document).on("click", ".opt-trash", function () {
// var taskid = $(this).parent().parent().data("taskid");

// $("#taskdelid").val(taskid);
// $("#modal-delete").html(
// "Are you sure you want to delete task ID " + taskid + "?"
// );
// $("#deletetask").modal("show");
// });

// // Remove task after conformation
// $(document).on("click", "#confdelete", function () {
// var taskid = $("#taskdelid").val();
// $("div")
// .find("[data-taskid=" + taskid + "]")
// .remove();
// $("#deletetask").modal("hide");
// });

  // function changeColor(id, c) {
  //   if (id == "ctxt") {
  //     $("#demotaak2").css("color", "#" + c);
  //   } else if (id == "cbg") {
  //     $("#demotaak1").css("border-left-color", "#" + c);
  //     $("#demotaak2").css("background-color", "#" + c);
  //   }
  //   return false;
  // }

// window.Retool.subscribe(function (model) {
// users = model.users;
// tasks = model.tasks;
// // subscribes to model updates
// // all model values can be accessed here

// });

</script>

Hi,

better to attach a Retool exported json app, the code you pasted is unreadable.

Hope this help.

Hello @Alex_Napoles ,

Thank you for your question:

It appears there might be several possible issues causing your problems. Firstly, you should ensure that your jQuery and Bootstrap libraries are correctly loaded and imported within the CustomComponent. Libraries can also be preloaded on an app or organization level as detailed in the Retool documentation (https://docs.retool.com/docs/custom-js-code).

You might be experiencing a scope issue with jQuery ($) not being recognized when your component loads. If jQuery does not load prior to your component, it might not be defined when your component attempts to use it. You can use window.jQuery or window.$ instead of just $ in your component to access the jQuery loaded in your window object.

For UI elements not displaying properly, this too could be a timing issue where the component is rendered before Bootstrap has finished loading. Make sure Bootstrap is fully loaded before initializing dependent components.

Remember, browser compatibilities, site settings, or CDN policies could affect the rendering of your components. If you observe errors in your browser console related to missing source maps, this is an issue with some browsers' handling of devtools, but it should not impede the functionality of the component.

Due to security precautions against cross-site scripting (XSS) attacks, Retool runs preloaded JavaScript code in a sandboxed environment, which may affect your use of global objects and certain third-party libraries. As an alternative, try utilizing Retool's named components and built-in library features, such as direct property modification, conditional and transformational operators, and Moment.js for date and time manipulation.

I know I haven't provided you a definitive answer to your issue, but I hope if you are still experiencing trouble that this gets you a bit closer to a solution.
-Brett