Advertisement
  1. Code
  2. PHP
  3. Yii

Building Your Startup: Leveraging Bootstrap, Ajax, and jQuery

Scroll to top
Read Time: 9 min
This post is part of a series called Building Your Startup With PHP.
Building Your Startup With PHP: Bootstrap Your Home Page
Building Your Startup: Using Routes for Schedule With Me
Final product imageFinal product imageFinal product image
What You'll Be Creating

This tutorial is part of the Building Your Startup With PHP series on Envato Tuts+. In this series, I'm guiding you through launching a startup from concept to reality using my Meeting Planner app as a real-life example. Every step along the way, I'll release the Meeting Planner code as open-source examples you can learn from. I'll also address startup-related business issues as they arise.

Leveraging Bootstrap, Ajax, and jQuery

Through our startup seriesMeeting Planner and Simple Planner have evolved an incredibly long way. Recently, I've been trying to tune into detailed areas to make using the service to schedule meetings an even higher degree of easy.

If you remember our recent episode Building Your Startup: Dynamic Ajax Forms for Scheduling (Envato Tuts+), you know how helpful Ajax and jQuery can be to usability. Making scheduling interactive with Ajax has transformed the usability of the site.

Next, I wanted to improve one pain point that I've run into using the service. Frankly, it's been time-consuming when sending out invitations to suggest multiple options for dates and times. Every time I send a meeting invitation for my own startup, I had to manually create two or three date/time options—and it was kind of annoying. 

In today's episode, I'm going to guide you through how I made it simple to schedule a meeting with several related dates and times in a single step. Specifically, I'll describe how I used Bootstrap, Ajax and jQuery to solve the problem of choosing dates and times.

Bootstrap made it easy to design the feature for desktop, tablet and mobile devices, and Ajax and jQuery made it fast and interactive.

If you haven't tried out Meeting Planner or Simple Planner yet, go ahead and schedule your first meeting. Look for the topic of this tutorial as you choose your date and time options. 

I do participate in the comment threads below, so tell me what you think! You can also reach me on Twitter @lookahead_io. I'm especially interested if you want to suggest new features or topics for future tutorials.

As a reminder, all of the code for Meeting Planner is written in the Yii2 Framework for PHP. If you'd like to learn more about Yii2, check out our parallel series Programming With Yii2.

Designing the Solution

Building Your Startup - My Design Sketch for Date Time RepetitionBuilding Your Startup - My Design Sketch for Date Time RepetitionBuilding Your Startup - My Design Sketch for Date Time Repetition

Using Meeting Planner over time, I'd regularly wanted a way to create a series of dates and times in a row, like the next three days at 8:30 am or the next three weeks on Wednesday at 7 pm. It just makes it easier to schedule with people when you have multiple options for when you're going to meet.

As I delved into deeper polishing of the user interface, I finally had my own time to focus on this issue. Before I wrote any code, I decided to loosely sketch above what I wanted.

I decided to create a repeat quantity, such as the next three or five, and a repeat unit, such as hours, days, or weeks. 

In other words, let's say I'm inviting the editorial droid assistant Tom McFarlin to coffee and want to offer any of the next three mornings, then I choose two and days to repeat after my chosen day.

Keeping It Simple

I didn't want people to always be confronted with a complex form just to schedule a meeting, so I separated the date time repetition feature with an advanced options link shown below. Touching or clicking this link opens the form shown below:

Building Your Startup - Advanced Date Time Repetition in MobileBuilding Your Startup - Advanced Date Time Repetition in MobileBuilding Your Startup - Advanced Date Time Repetition in Mobile

Getting Started Writing Code

To design the form to work with both desktop and mobile devices, I leveraged Bootstrap. Essentially, I created multiple rows for the form with various column widths that collapse on mobile. Let's look.

Most of the HTML magic happens here, in /frontend/views/meeting-time/_form.php. First, here's the row with the Date, Time, Duration and advanced options link:

1
<div class="meeting-time-form">
2
  <div class="row">
3
    <div class="col-xs-12 col-md-4 col-lg-3">
4
    <?php $form = ActiveForm::begin();?>
5
    <?= Html::activeHiddenInput($model, 'url_prefix',['value'=>MiscHelpers::getUrlPrefix(),'id'=>'url_prefix']); ?>
6
    <?= Html::activeHiddenInput($model, 'tz_dynamic',['id'=>'tz_dynamic']); ?>
7
    <?= Html::activeHiddenInput($model, 'tz_current',['id'=>'tz_current']); ?>
8
    <strong><?php echo Yii::t('frontend','Date') ?></strong>
9
    <div class="datetimepicker-width">
10
    <?= DateTimePicker::widget([
11
        'model' => $model,
12
        'attribute' => 'start',
13
        'template' => '{input}{button}',
14
        //'language' => 'en',

15
        'size' => 'ms',
16
        'clientOptions' => [
17
            'autoclose' => true,
18
            'format' => 'M d, yyyy',
19
            'todayBtn' => true,
20
            //'pickerPosition' => 'bottom-left',

21
            'startView'=>2,
22
            'minView'=>2,
23
            // to do - format three day ahead

24
            'initialDate'=> Date('Y-m-d',time()+3600*72),
25
        ]
26
    ]);?></div>
27
    <p></p>
28
  </div>
29
  <div class="col-xs-12 col-md-4 col-lg-3">
30
    <strong><?php echo Yii::t('frontend','Time') ?></strong>
31
    <div class="datetimepicker-width">
32
    <?= DateTimePicker::widget([
33
        'model' => $model,
34
        'attribute' => 'start_time',
35
        'template' => '{input}{button}',
36
        //'language' => 'en',

37
        'size' => 'ms',
38
        'clientOptions' => [
39
            'autoclose' => true,
40
            'format' => 'H:ii p',
41
            'todayBtn' => false,
42
            'minuteStep'=> 15,
43
            'showMeridian'=>true,
44
            //'pickerPosition' => 'bottom-left',

45
            'startView'=>1,
46
            'minView'=>0,
47
            'maxView'=>1,
48
            // to do - format one day ahead

49
            //'initialDate'=> Date('Y-m-d'),

50
            // $( "th.switch" ).text( "Pick the time" );

51
        ]
52
    ]);?>
53
    </div>
54
    <p></p>
55
    </div>
56
    <div class="col-xs-6 col-md-2 col-lg-2">
57
      <?php
58
      $durationList = [1=>'1 hour',2=>'2 hours',3=>'3 hours',4=>'4 hours',5=>'5 hours',6=>'6 hours',12=>'12 hours',24=>'24 hours',48=>'48 hours',72=>'72 hours'];
59
      echo $form->field($model, 'duration',['options' => ['id'=>'duration','class' => 'duration-width' ]])
60
        ->dropDownList(
61
            $durationList,           // Flat array ('id'=>'label')

62
            ['prompt'=>'select a duration']    // options

63
        );
64
        ?>
65
    </div>
66
      <div class="col-xs-6 col-md-2 col-lg-2" style="margin-top:3em;">
67
      <?= Html::a(Yii::t('frontend','advanced options'),'javascript:void(0);', ['onclick'=>'toggleTimeAdvanced();']);?>
68
    </div>
69
  </div>

By using successful column dimensions in Bootstrap like this, the row spreads out on desktop (shown below) and collapses on itself into three rows on mobile (shown above):

1
<div class="col-xs-12 col-md-4 col-lg-3">
2
<!-- Date -->
3
...
4
<div class="col-xs-12 col-md-4 col-lg-3">
5
<!-- Time -->
6
...
7
<div class="col-xs-6 col-md-2 col-lg-2">
8
<!-- Duration -->
9
...
10
<div class="col-xs-6 col-md-2 col-lg-2" style="margin-top:3em;">
11
<!-- Advanced options -->
12
...
Building Your Startup - Advanced Date Time Repetition on DesktopBuilding Your Startup - Advanced Date Time Repetition on DesktopBuilding Your Startup - Advanced Date Time Repetition on Desktop

The jQuery toggleTimeAdvanced() for the advanced options link opens the repetition form by removing the hidden class:

1
 function toggleTimeAdvanced() {
2
   if ($('#timeAdvanced').hasClass('hidden')) {
3
      $('#timeAdvanced').removeClass('hidden');
4
   } else {
5
     $('#timeAdvanced').addClass('hidden');
6
     $("select#meetingtime-repeat_quantity").prop('selectedIndex', 0);
7
   }

Note: All the jQuery can be found in /frontend/web/js/meeting.js.

It also resets the repetition setting to zero when you close it—that was a design decision to prevent duplicates from being created if people closed the advanced form.

Here's the timeAdvanced sub-form:

1
<div class="row hidden" id="timeAdvanced">
2
    <div class="col-xs-12 col-md-2 col-lg-2">
3
      <?php
4
      $repeat_quantity = [0=>'no repeating',1=>'1 additional option',
5
      2=>'2 additional options',3=>'3 additional options',
6
      4=>'4 additional options',5=>'5 additional options'];
7
      echo $form->field($model, 'repeat_quantity',['options' => ['id'=>'repeat_quantity','class' => 'repeat-width' ]])->label('Add')
8
        ->dropDownList(
9
            $repeat_quantity
10
            ,
11
            ['options'=>['1'=>['Selected'=>true]]]
12
        );
13
        ?>
14
      </div>
15
        <div class="col-xs-12 col-md-6 col-lg-6">
16
        <?php
17
        $repeat_unit = ['hour'=>'successive hour e.g. 9 am, 10 am and 11 am',
18
        'day'=>'successive day e.g. Monday, Tuesday & Wednesday',
19
        'week'=>'successive week e.g. next Friday & Friday after'];
20
        echo $form->field($model, 'repeat_unit',['options' => ['id'=>'repeat_unit','class' => 'repeat-width' ]])->label('On each')
21
          ->dropDownList(
22
              $repeat_unit
23
          );
24
        ?>
25
    </div>
26
  </div>

The Bootstrap I used appears in one row on desktops and two rows on mobile devices:

1
<div class="col-xs-12 col-md-2 col-lg-2">
2
<!-- repeat quantity -->
3
<div class="col-xs-12 col-md-6 col-lg-6">
4
<!-- repeat unit -->

Here's what it looks like adding 3 additional options each successive day at 9 am:

Building Your Startup - Advanced Date Time Repetition FormBuilding Your Startup - Advanced Date Time Repetition FormBuilding Your Startup - Advanced Date Time Repetition Form

Next, I updated the addTime() function to capture and submit the repeat_quantity and repeat_unit fields to the PHP-based controller:

1
function addTime(id) {
2
    start_time = $('#meetingtime-start_time').val();
3
    start = $('#meetingtime-start').val();
4
    duration = $('#meetingtime-duration').val();
5
    repeat_quantity = $('#meetingtime-repeat_quantity').val();
6
    repeat_unit = $('#meetingtime-repeat_unit').val();    
7
    if (start_time =='' || start=='') {
8
      displayAlert('timeMessage','timeMsg2');
9
      return false;
10
    }
11
    // ajax submit subject and message

12
    $.ajax({
13
       url: $('#url_prefix').val()+'/meeting-time/add',
14
       data: {
15
         id: id,
16
        start_time: encodeURIComponent(start_time),
17
        start:encodeURIComponent(start),
18
        duration:encodeURIComponent(duration),
19
        repeat_quantity:encodeURIComponent(repeat_quantity),
20
        repeat_unit:encodeURIComponent(repeat_unit),
21
      },
22
       success: function(data) {
23
         loadTimeChoices(id);
24
         insertTime(id);
25
         displayAlert('timeMessage','timeMsg1');
26
         return true;
27
       }
28
    });

Startups are hard in that you're always rushing to get new features done. For example, someone (likely me since I'm the only coder) had never transferred the chosen duration; so, I added that too. Up until today, all the meetings were 1 hour despite what users requested. Enough said. #startuplife.

Then, I switched over to the MVC code in my Yii Framework-based /frontend/controllers/MeetingTimeController.php. Below, you can see the actionAdd AJAX method that responds to the jQuery submission:

1
public function actionAdd($id,$start,$start_time,$duration=1,$repeat_quantity=0,$repeat_unit='hour') {
2
      Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
3
      $timezone = MiscHelpers::fetchUserTimezone(Yii::$app->user->getId());
4
      date_default_timezone_set($timezone);
5
      $cnt=0;
6
      while ($cnt<=$repeat_quantity) {
7
        $model = new MeetingTime();
8
        $model->start = urldecode($start);
9
        $model->start_time = urldecode($start_time);
10
        if (empty($model->start)) {
11
          $model->start = Date('M d, Y',time()+3*24*3600);
12
        }
13
        $model->tz_current = $timezone;
14
        $model->duration = $duration;
15
        $model->meeting_id= $id;
16
        $model->suggested_by= Yii::$app->user->getId();
17
        $model->status = MeetingTime::STATUS_SUGGESTED;
18
        $selected_time = date_parse($model->start_time);
19
        if ($selected_time['hour'] === false) {
20
          $selected_time['hour'] =9;
21
          $selected_time['minute'] =0;
22
        }
23
        // convert date time to timestamp

24
        $model->start = strtotime($model->start) +  $selected_time['hour']*3600+ $selected_time['minute']*60;
25
        if ($cnt>0) {
26
          switch ($repeat_unit) {
27
            case 'hour':
28
              $model->start+=($cnt*3600);
29
            break;
30
            case 'day':
31
              $model->start+=($cnt*24*3600);
32
            break;
33
            case 'week':
34
              $model->start+=($cnt*7*24*3600);
35
            break;
36
          }
37
        }
38
        $model->end = $model->start + (3600*$model->duration);
39
        $model->save();
40
        $cnt+=1;
41
      }
42
      return true;
43
    }

Basically, I created a loop using a counter, $cnt, to increment the MeetingTime start and end time choices by the $repeat_unit, e.g. hours, days, or weeks:

1
if ($cnt>0) {
2
  switch ($repeat_unit) {
3
    case 'hour':
4
      $model->start+=($cnt*3600);
5
    break;
6
    case 'day':
7
      $model->start+=($cnt*24*3600);
8
    break;
9
    case 'week':
10
      $model->start+=($cnt*7*24*3600);
11
    break;
12
  }
13
}
14
$model->end = $model->start + (3600*$model->duration);

So here are the results of me adding three additional timeslots each day at 9:00 AM:

Building Your Startup - Results of Date Repitition Over Four DaysBuilding Your Startup - Results of Date Repitition Over Four DaysBuilding Your Startup - Results of Date Repitition Over Four Days

So now, it's easier to schedule meetings with people and offer them several successive dates and times as options for getting together.

In Closing

I hope this has been helpful to you seeing how Bootstrap can be used to create better forms and can be combined with Ajax and jQuery to build a simple interactive experience for your users.

If you didn't earlier, try scheduling a meeting at Meeting Planner with repeating date/time options and let me know what you think. 

Have your own thoughts? Ideas? Feedback? You can always reach me on Twitter @lookahead_io directly. Watch for upcoming tutorials here in the Building Your Startup With PHP series.

Over the next few weeks, I'm going to continue polishing the user experience to make the service as easy as possible to use. For example, you might notice the meeting notes are now on their own tab:

Building Your Startup - The New Discussion TabBuilding Your Startup - The New Discussion TabBuilding Your Startup - The New Discussion Tab

And, to eliminate the confusion people were having between the availability column of yes/no switches and the second column of choosing the final place, I separated this into a lower sub-panel of buttons, Finalize the Time. Only organizers and participants designated as organizers see this lower panel, simplifying the common view for typical participants:

Building Your Startup - Finalize the Time via Buttons instead of Choose SwitchesBuilding Your Startup - Finalize the Time via Buttons instead of Choose SwitchesBuilding Your Startup - Finalize the Time via Buttons instead of Choose Switches

Bootstrap, jQuery and Ajax tied partly or wholly into building both of these features as well.

I hope by now in the series, you're having your own startup ideas and thinking about writing some code. Stay tuned to learn more about how I'm building and launching mine.

Related Links

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.