Introduction
The Trailblazer Widget library allows you to create fully custom search experiences that leverage the Trailblazer API for finding the perfect rental for your users. From there, transactions can be completed either from yoru own site, or by directing users through to the Outdoorsy checkout flow for processing.
Authentication
In order to leverage the widget library, you'll need to have a Partner ID provided from your account rep.
Getting Started
Widgets work by embedding themselves into elements that you provide on the page. To give you a quick example of how this works, we'll create an example project. We'll use Bootstrap for some basic styling, but this is NOT required to use the library - we're just using it for the grids as a demonstration.
Load Dependencies
You can drop these into the footer of your application
<link rel="stylesheet" href="https://d1o5877uy6tsnd.cloudfront.net/sdk/v2/trailblazer-widget.css">
<script type="text/javascript" src="https://d1o5877uy6tsnd.cloudfront.net/sdk/v2/trailblazer-widget.js"></script>
To get started, you'll need to load the Trailblazer JS SDK and some base styles. The stylesheet provides some lightweight styling for core components like sliders and date pickers. You'll want to create a custom stylesheet to provide your own styling. Here are some skeleton stylesheets you can use to get started:
Creating Widget Containers
<div class="container">
<div class="row">
<div class="col-sm-12">
<div id="location-search"></div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<div id="date-container"></div>
<div id="vehicle-price"></div>
</div>
<div class="col-sm-8">
<div id="rental-results"></div>
</div>
</div>
</div>
Widgets need to be given containers that they can be created inside of, so the first step is going to be creating a basic site structure for widgets to use. Here's an example, but you're welcome to do this however you'd like. We're going to assume you're using the bootstrap grid system for the demo, but it's not required.
Initializing your App
var trailblazer = Trailblazer({
partnerId: {{yourID}},
urlParams: true,
currency: 'USD', //optional, controls pricing currency you get back
locale: 'en-us', // optional, controls which checkout flow users are directed to for translations
filtersChangedCallback: function(currentFilters) {
// optional callback, if you provide one it allows
// you to listen externally for filter changes
// useful when creating your own custom components
}
});
// Now that we've initialized the app, we can begin enabling the widgets we want
// first we add the widget for displaying results
trailblazer.addWidget(trailblazer.widgets.RentalList, "rental-results");
// now lets add the search widgets
trailblazer.addWidget(trailblazer.widgets.LocationSearch, "location-search");
trailblazer.addWidget(trailblazer.widgets.PriceRange, "vehicle-price");
trailblazer.addWidget(trailblazer.widgets.DatePicker, "date-container");
trailblazer.start();
Now that you've created a basic site skeleton, we'll include a new JS file where we add your widgets to the page
It's a good idea to enable the urlParams
feature, as that'll allow your renters to share links with friends to search results and also keep things like dates in sync between pages.
Adding Widgets To The Page
Once you've added your widgets, you'll just need to start up your app
To finish adding widgets to your page, you can call the addWidget
function on your app.
Custom Views & Styling
trailblazer.addWidget(widget, container-el, {
cssClasses: {
root: "custom-class",
etc...
}
});
<script type="text/html" id="result-template">
<div class="col-sm-4">
<a href="/rental/{{id}}">
<div class="rental-result">
<div class="rv-img">
<div class="rv-image-container">
<img src="{{primary_image_url}}" alt="{{name}} in {{home.city}}, {{home.state}}">
</div>
<div class="price">${{active_options.price_day}}<span class="price-info">/night</span></div>
</div>
<div class="rv-info">
<div class="rv-title">{{name}}</div>
<div class="rv-subtitle">
<span class="pull-right">
<i class='fa fa-heart favorites-count'></i> {{score}} / 5
</span>
{{location.city}}, {{location.state}}
</div>
</div>
</div>
</a>
</div>
</script>
var resultsTemplate = document.getElementById("result-template").innerHTML;
trailblazer.addWidget(trailblazer.widgets.RentalList, "rental-container", {
formatItem: function(singleItem) { return singleItem; },
templates: {
loading: 'loading now!',
available: resultsTemplate
},
});
You have access to all of the data we have stored for each rental, including:
{
"id": 1,
"name": "2012 Forest River Primetime Tracer",
"type": "trailer",
"description": "description here",
"vehicle_class": "",
"vehicle_make": "Forest River",
"vehicle_model": "Tracer",
"slug": "/rv-rental/burrton_ks/2012_forest-river_tracer_1-listing",
"partner_url": "https://redirect.outdoorsy.com?partner_id={{yourID}}&url=1"
"vehicle_year": 2012,
"vehicle_length": 34,
"availability_set": false,
"created": "2018-07-23T04:11:50.845213Z",
"updated": "2018-08-01T02:16:38.659458Z",
"owner_user_id": 394213,
"published": true,
"features": {
"air_conditioner": true,
"audio_inputs": true,
"awning": true,
"backup_camera": false,
"beds_full": 0,
"beds_king": 0,
"beds_queen": 0,
"beds_twin": 0,
"bike_rack": false,
"burning_man_friendly": true,
"cd_player": true,
"ceiling_fan": false,
"dining_table": true,
"extra_storage": true,
"fuel_tank": 0,
"fuel_type": "",
"generator": false,
"gray_tank": 0,
"handicap_accessible": false,
"heater": true,
"hitch_weight": 0, ...
},
"sleeps": 10,
"seatbelts": 0,
"sleeps_adults": 0,
"sleeps_kids": 0,
"images": [{
"id": 499049,
"rental_id": 74746,
"primary": true,
"position": 0,
"tags": "exterior",
"description": "",
"skip_enhance": false,
"video": false,
"urlSmall" : "https://link-here",
"urlMedium" : "https://link-here",
"urlLarge" : "https://link-here"}, ...
],
"position": 0,
"score": 0,
"reviews_num": 0,
"group_reviews_score": 0,
"group_reviews_num": 0,
"favorite_count": 0,
"location": {
"city": "Burrton",
"state": "KS",
"county": "",
"country": "US",
"zip": "67020",
"lat": 38,
"lng": -97.73
},
"locale": {
"base_currency": "USD",
"distance_unit": "miles",
"weight_unit": "lbs",
"length_unit": "feet",
"liquid_unit": "gallons"
},
"security_deposit": 500,
"use_day_pricing": false,
"cancel_policy": "flexible",
"minimum_days": 2,
"active_options": {
"date": "",
"day_price": 150,
"week_price": 770,
"month_price": 3136,
"cancel_policy": "flexible",
"minimum_days": 0,
"use_day_pricing": false,
"instant_book": false
},
"cancel_text": "",
"house_rules": "rules go here!",
"mileage_usage_item": {
"id": 65001,
"name": "mileage",
"unit": "mile",
"included": 200,
"included_period": "mile",
"unlimited": true,
"archived": false,
"tax_rate_id": 0
},
"pickup_unavailable": {
"sunday": false,
"monday": false,
"tuesday": false,
"wednesday": false,
"thursday": false,
"friday": false,
"saturday": false
},
"dropoff_unavailable": {
"sunday": false,
"monday": false,
"tuesday": false,
"wednesday": false,
"thursday": false,
"friday": false,
"saturday": false
},
"primary_image": { image object ...},
}
One of the best features of the widget system is the ability to customize how search results are displayed. By default, Trailblazer provides a simple template to get you started, but you're welcome to override it with your own. The templating system uses Hogan.js for templates. Here are some basic examples to get you started.
Customizing widgets
All widgets provide at least some level of customization, either via CSS or custom templates.
For CSS, you have the ability of using the provided trailblazer
classes, or you can pass in your own. The basic structure for passing in your own classes is shown here.
Each widget will specify the classes that are available for passing in. If you're looking for a starting point for creating your CSS changes, here are a few skeleton options you can leverage.
Custom Templates
Most widgets also allow you to pass in a template to take even more control over the widget interface. Templates must be a string, but it's common practice to define them in your HTML, and then pull them in as a var. Templates are written using Hogan.js, which provide all the functionality of Mustache templates.
If you need to manipulate any data before it gets passed into your template, you'll also have access to a param called formatItem
. formatItem
takes in a single data object and returns a transformed one with any changes you'd like. We'll then pass that new data object into the template for rendering.
Checkout the example to see how templates work.
Custom Select or Checkbox filters
Our select and checkbox filters provide a fairly robust level of customization out of the box. For most use cases, you should be able to work within those conventions without any trouble. If you need to create more custom components, then you always have the option of taking full control. To learn more, check out the Custom Widgets section of the documentation.
Search Widgets
Trailblazer provides several widgets out of the box, but also gives you the ability to define your own. We'll walk you through each of the options in this section.
Search Results
trailblazer.addWidget(trailblazer.widgets.RentalList, "rental-container", {
formatItem: function(singleItem) { return singleItem; },
templates: {
available: "{{name}} - {{id}} -- {{currentPrice.day}}",
loading: "Loading..."
},
filters: {},
cssClasses: {
root: ["test", "here"],
singleItem: "rental-item"
}
});
This is one of the main elements for a search interface, it allows you to set the templates for search results as well as triggers the initial rentals load. Without this widget, rentals will never display on your page.
Options
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplates.available
- string, optional This is the main tempalet for displaying resultsfilters
- optional by default, all rentals are loaded. If you include filters, then we'll only load matching rentals.- There are a ton of rental filters you can use, please ask for specific directions. Note that most filtering will take place locally, yo should only add filters here if you want some rentals to never show in results.
location_ids
- specify a list of locations to include, format: "1,2,3"
Search Metadata
trailblazer.addWidget(trailblazer.widgets.Metadata, "metadata-container", {
formatItem: function(singleItem) {return singleItem;}, // optional
template: "Showing {{totalVisible}} of {{totalFiltered}} matching rentals"
});
Show metadata on the current set of search results, for example: "Showing 17 of 35 available rentals".
Available Template Data
currentPage
- Current page you're looking atperPage
- how many results per pagetotalRentals
- total vehiclestotalFiltered
- total vehicles matching current filterstotalVisible
- number of vehicles currently on the page including pagination
Customization
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplate
- template string
Date Picker
trailblazer.addWidget(trailblazer.widgets.DateRange, "dates-container", {
rentalId: 1426 // optional, only include if this is for a specific vehicle
});
The date picker allows your renters to select a date range they're interested in renting a vehicle for. If you include a rental ID when initializing, then we'll also load availability information so that renters know when the unit is free.
Customization
The main point of customization for the datepicker is going to be through CSS for adjusting colors. Check out the starter CSS file to learn more.
Price Slider
trailblazer.addWidget(trailblazer.widgets.PriceRange, "price-container", {
cssClasses: {
root: "rental-price",
selectedValues: '',
selectedMin: '',
selectedMax: ''
}
});
Allows you to filter results by price using a slider
Customization
cssClasses
- Pass in custom CSS classes for styling.
Pagination
trailblazer.addWidget(trailblazer.widgets.Pagination, "pagination-container", {
perPage: 10,
cssClasses: {
container: "container",
subcontainer: "subcontainer",
activePage: "active-page"
}
});
The pagination component adds a pagination bar to the page
Customization
The main point of customization for the pagination component is going to be through CSS.
Checkbox Filters
trailblazer.addWidget(trailblazer.widgets.CheckboxFilter, "checkbox-container", {
apiFilters: function(selectedOptions, allOptions, apiObject) {
return apiObject;
},
template: '{{label}}',
selected: function(item, apiObject) {
return true;
},
formatItem: function(singleItem) { return singleItem; },
cssClasses: {
root: "fuel-type-container",
listItem: "fuel-type",
listItemLabel: "fuel-type-label"
},
options: function(currentRentals, rentalMeta) {
return [
{
label: "Label here",
value: "Value here"
}
]
}
});
This widget allows you to use either a dynamic or static list of checklist items to filter results.
formatItem
- optional, takes in a single item from the options list and returns a final datastructure. not often used, but useful if you need to transform data before it hits the template phasetemplate
- optional. Pass in a template as a stringoptions
- function that accepts both acurrentRentals
array and arentalMeta
object. The function should return an array that represents the options given to a shopper.currentRentals
- An array of currently visible rentals on the page, including other filtersrentalMeta
- All the data we have, including all rentals, totals, max prices, min prices, etc...- Returns an array containing objects with the following fields:
label
- What the shopper seesvalue
- The value used for filtering
selected
- function that takes in a single option and an apiObject and returns if the option is selected or not given current stateapiFilters
- function that takes in the currently selected options, a list of all options, and an apiObject. It should return a full apiObject that's the results of the currently selected options. Gets called anytime the user changes their selection on the current widget
Select Tag Filters
trailblazer.addWidget(trailblazer.widgets.SelectFilter, "select-container", {
multi: false, // indicates if multiple values can be selected
apiFilters: function(selectedOptions, allOptions, apiObject) {
return apiObject;
},
template: '{{label}}',
selected: function(item, apiObject) {
return true;
},
formatItem: function(singleItem) { return singleItem; },
cssClasses: {
root: "fuel-type-container",
listItem: "fuel-type",
listItemLabel: "fuel-type-label"
},
options: function(currentRentals, rentalMeta) {
return [
{
label: "Label here",
value: "Value here"
}
]
}
});
Provides a drop-down select tag with full text search and optional multiselect.
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplate
- optional. Pass in a template as a stringoptions
- function that accepts both acurrentRentals
array and arentalMeta
object. The function should return an array that represents the options given to a shopper.currentRentals
- An array of currently visible rentals on the page, including other filtersrentalMeta
- All the data we have, including all rentals, totals, max prices, min prices, etc...- Returns an array containing objects with the following fields:
label
- What the shopper seesvalue
- The value used for filtering
selected
- function that takes in a single option and an apiObject and returns if the option is selected or not given current stateapiFilters
- function that takes in the currently selected options, a list of all options, and an apiObject. It should return a full apiObject that's the results of the currently selected options. Gets called anytime the user changes their selection on the current widgetmulti
- Default false. If true, users can select multiple values from the dropdown
Clear Filters
trailblazer.clearFilters()
For example, you could do this on a button click
<a href="" id="clear-filters">Clear Filters</a>
<script>
$("#clear-filters").click(function(e) {
e.preventDefault();
trailblazer.clearFilters();
});
</script>
At any time, you have the ability to clear the existing filters on the search results. To do so, you can call clearFilters
on the Trailblazer library.
Rental Widgets
The following widgets are available fore specific rentals on the platform. They're useful when building out a landing page for a specific vehicle, perhaps after the user has already gone through search and is looking to make a booking.
Quote Widget
trailblazer.addWidget(trailblazer.widgets.Quote, "quote-container", {
rentalId: 1426, //your rental ID
formatItem: function(quoteResponse) { return quoteResponse; },
template: {
error: "{{error}}",
noQuote: "before-quote-entered",
quote: "see notes for quote templates"
},
cssClasses: {
root: "base-class",
lineItem: "trailblazer-line-item",
lineItemDescription: "trailblazer-line-item-description",
lineItemPrice: "trailblazer-line-item-price",
lineTotal: "trailblazer-line-total",
lineSubtotal: "trailblazer-line-subtotal",
lineTax: "trailblazer-line-tax",
lineServiceFee: "trailblazer-line-service-fee",
error: "trailblazer-quote-error"
}
});
Provides a quote widget that shows pricing information for selected dates
Customization
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplate.error
- string for what to display when an error is received from the backend.template.noQuote
- string for what to display when no dates have been entered yet.template.quote
- string for what to display when a quote has been successfully receieved.rentalId
- rental ID for the vehicle being quotedcssClasses
- custom classes
Date Picker
trailblazer.addWidget(trailblazer.widgets.DateRange, "dates-container", {
rentalId: 1426 // optional, only include if this is for a specific vehicle
});
The date picker allows your renters to select a date range they're interested in renting a vehicle for. If you include a rental ID when initializing, then we'll also load availability information so that renters know when the unit is free.
Customization
The main point of customization for the datepicker is going to be through CSS for adjusting colors. Check out the starter CSS file to learn more.
Availability Calendar
trailblazer.addWidget(trailblazer.widgets.DateRange, "availability-calendar-container", {
rentalId: 1426,
embedded: true,
numberOfMonths: 2 // optional
});
You can also use the date picker to display an embedded availability calendar for your vehicles. Using the embedded availability calendar is very similar to the above date picker
Customization
In addition to the same CSS customization above for the date picker, you can also indicate the number of months you'd like to show by including a numberOfMonths
config option. If you don't include one, we'll default to either 1 or 2 months depending on the screen size of the user. We recommend that you do something similar if you'd like to customize this. If you only indicate 3 months for example, there's a large likelihood that the calendar won't display correctly on mobile devices.
List Add-Ons
trailblazer.addWidget(trailblazer.widgets.AddonList, "addons-container", {
rentalId: 1426, //your rental ID, required
formatItem: function(singleAddon) { return singleAddon; },
template: 'template string',
filter: function(addOn) {
// optional, allows you to filter add-ons so you only display the ones that match any criteria.
// this will only show the add-on with an ID of 1, for example.
return addOn.id === 1;
}
});
Allows you to list all the available add-ons for this vehicle based on your Account settings.
Customization
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplate
- optional, template stringrentalId
- rental ID for the vehicle being quotedfilter
- limit the add-ons that display for this widget
Review List
trailblazer.addWidget(trailblazer.widgets.ReviewList, "reviews-container", {
rentalId: 1426, //your rental ID, this is optional. if omitted we'll load the user reviews for all rentals
formatItem: function(singleReview) { return singleReview; },
template: "review template",
filters: {
limit: 4, // return only 4 reviews
score_gte: 4 // return only 4 and 5 star reviews
}
});
Displays a list of reviews for a single rental or all rentals.
Customization
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplate
- optional, template stringrentalId
- rental ID for the vehicle you want reviews for. Can be ommitted to view reviews of all vehicles
Account Widgets
These widgets work on an individual account level. They're useful for building up a search page for your own rental company.
Review List
trailblazer.addWidget(trailblazer.widgets.ReviewList, "reviews-container", {
rentalId: 1426, //your rental ID, this is optional. if omitted we'll load the user reviews for all rentals
formatItem: function(singleReview) { return singleReview; },
template: "review template",
filters: {
limit: 4, // return only 4 reviews
score_gte: 4 // return only 4 and 5 star reviews
}
});
Displays a list of reviews for a single rental or all rentals.
Customization
formatItem
- function, optional, called before the template renders, giving you a chance to change the data passed into your templatetemplate
- optional, template stringrentalId
- rental ID for the vehicle you want reviews for. Can be ommitted to view reviews of all vehicles
Location Filter
trailblazer.addWidget(trailblazer.widgets.LocationList, "locations-container", {
cssClasses: {
root: "rental-location-container",
listItem: "rental-location",
listItemLabel: "rental-location-label"
},
formatItem: function(singleLocation) { return singleLocation; },
template: 'template string'
});
Automatically loads available locations and allows filtering by one or many. Locations can be added/removed in your Wheelbase dashboard.
Customization
The cssClasses
above are an optional way for you to take control over the classes used on the widget.
You can also pass in a template
as a string.
Custom Widgets
It's easy to extend the Trailbase widget system by hooking into some provided functionality to listen for data changes or emit new filter requests on rentals. Using these hooks, users have created custom date pickers, custom filter options for things like ratings/reviews and more.
Filters changed callback
var trailblazer = Trailblazer({
partnerId: {{yourID}},
urlParams: true,
filtersChangedCallback: function(currentFilters) {
// optional callback, if you provide one it allows
// you to listen externally for filter changes
// useful when creating your own custom components
}
});
When initializing your Trailblazer app, you have the ability to specify a callback we'll use to notify you of any changes to the filter params. This could be useful for updating interfaces external to Trailblazer, tracking purposes, or anyting else you can imagine.
Viewing current filters
var currentFilters = trailblazer.getFilters();
At any time, you have the ability to pull the current API filters stored within the Trailblazer library. This is often useful when paired with the ability to emit filter changes using a similar method documented below.
Changing current filters
var trailblazer = Trailblazer({partnerId: 123});
var currentFilters = trailblazer.getFilters();
currentFilters['length[gte]'] = 25;
trailblazer.changeFilters(currentFilters);
calling changeFilters
on your trailblazer instance allows you to overwrite any filters currently in place in the library. Note that the existing state will be replaced by your new one, so it's useful to request the current filter state if you intend to preserve the majority of the current filters. Once you call the changeFilters
function, all widgets within the interface will automatically reload to match the new state and new search results will be loaded from the server.
Creating Quotes & Bookings
The Trailblazer JS SDK provides the option to create custom quotes for your shoppers and initiate bookings without leaving your site. The bulk of the checkout process wills till be handled by Trailblazer, but the first step can be handled on your end to provide a more seamless interaction.
Adding add-ons to a quote
let addonId = 1; // the add-on's ID
let addonCount = 1; // how many of this add-on we want
trailblazer.addAddon(addonId, addonCount);
For example, one HTML use case may be
<div id="addon-1"></div>
<div class="addon-1-button">
<a href="" id="add-addon-1">Add to Trip</a>
</div>
<script>
$("#add-addon-1").click(function(e) {
e.preventDefault();
trailblazer.addAddon(1, 1);
});
</script>
If you'd like to create a shopping experience on your site for handling Add-ons, you can do so leveraging the addAddon
API provided by Trailblazer. Here's a quick example of how you might accomplish that. Many users will pair this functionlaity with the AddonList
widget covered above to create a dynamic experience for renters.
Initializing bookings from your site
Once you have your renters booking info and contact details, you can initiate a quote with the following command, which returns a promise:
trailblazer.startBooking({
firstName: "tim",
lastName: "smith",
email: "email@provider.com",
phone: "555-555-5555",
rentalId: 7,
campaign: '',
source: '',
medium: ''
}).then(function(url, bookingDetails) {
console.log("booking details if you need them", bookingDetails);
window.location.href = url;
}).catch(function(error) {
alert("There was an error creating your booking: " + error);
});
If you'd like to pass in your own add-ons and dates when creating a booking, you can do so as follows:
trailblazer.startBooking({
firstName: "tim",
lastName: "smith",
email: "email@provider.com",
phone: "555-555-5555",
rentalId: 7,
from: 'YYYY-MM-DD',
to: 'YYYY-MM-DD',
items: [{id: 1, count: 2}],
campaign: '',
source: '',
medium: ''
}).then(function(url, bookingDetails) {
console.log("booking details if you need them", bookingDetails);
window.location.href = url;
}).catch(function(error) {
alert("There was an error creating your booking: " + error);
});
Once a user has customized their booking with add-ons, you have the ability to initiate a booking from your site. We won't dive into the specifics on how to start this process, as the options would be up to your team to decide on. You could create a form on your page to collect renter information, initiate a modal, have users already logged in, etc...
We'll pull the information from the users cart to collect start date, end date and selected add-ons and create the quote. It'll then be up to you to forward the renter into the remainder of the checkout flow using the URL provided.
If you don't want to go through the process of building up a quote using the addAddons
functionality, you can also pass in any desired add-ons directly into the startBooking
call.