Bootstrap 4 Lit-Element

Lightbase
LightbaseIO
Published in
7 min readNov 20, 2019
Bootstrap 4 Lit-Element Lightbase Tutorial

A hands-on guide to setting up Bootstrap for a Lit-Element app in native plain vanilla JavaScript (read: no jQuery) and web components. Oh, and no pun intended!

A picture is worth a thousand words, so without further ado, check out what we’ll make today with just a couple of lines of code using web components:

Web Component playing nicely with a bootstrap 4 template.
What we will get at the end of this tutorial — web components in a bootstrap 4 template.

One of the stumbling blocks I encountered with Polymer fairly early on was with CSS. The issue for me was that web components render in Shadow DOM meaning the CSS inside of a web components is “scoped”.

Per Google:

Hands down the most useful feature of shadow DOM is scoped CSS:
- CSS selectors from the outer page don’t apply inside your component.
- Styles defined inside don’t bleed out. They’re scoped to the host element.

This is a really good thing when building components designed to run universally, under a wide set of environments. But if the web component you are writing is meant to only be run inside your app and nowhere else, the “scope-ness” can work against you by preventing your website’s stylesheet from being applied.

So, if you wanted to build your application using a bootstrap template, good luck getting your components to “bootstrap”. Google had work-arounds for this in the previous versions of Polymer. But in Lit-Element, I stumbled on hands down the most natural way of “de-scoping” CSS: createRenderRoot()

Lit-Element’s createRenderRoot() method allows you to render your web component in the LightDOM. It takes your web component’s DOM out of the shadow and into the light.

With the shadow DOM / scoped CSS issue out of the way, we can leverage a bootstrap template along with Lit-Element.

Lit-Element + Bootstrap Native + Bootstrap Template = Winning Combo

At least for people like me who are design challenged and tend to avoid dealing with CSS!

Bootstrap Native is basically:

The jQuery plugins for Bootstrap 4 redeveloped with native JavaScript, providing same basic functionality, but lighter in size and delivering higher performance for your application.

Now we’re all set to git it done…

First, let’s get a free bootstrap template to work with: Light Bootstrap Dashboard by Creative Tim. After exploding the file, I’ll copy ./examples/dashboard.html to ./index.html. Running the basic polymer development web server:

$ polymer serve -p 8083

Generic Bootstrap 4 Template
Generic Bootstrap 4 Template

Step one is to get rid of jQuery. At the bottom of index.html, *delete all* of the “<script>…” tags underneath the Core JS Files comment.

<!--   Core JS Files   -->
<script src="../assets/js/core/jquery.3.2.1.min.js" type="text/javascript"></script>
...
<script src="../assets/js/demo.js"></script>
<script type="text/javascript">
$(document).ready(function() {
// Javascript method's body can be found in assets/js/demos.js
demo.initDashboardPageCharts();
demo.showNotification();
});</script>

We’ll replace the scripts with native javascript libraries. The boostrap-native-v4 library should be placed in the <head> section. We’ll also put back the chartist library (it actually has no jQuery dependency) back just after the body tag:

<head>
...
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/bootstrap.native@2.0.15/dist/bootstrap-native-v4.min.js"></script>
</head>
...
</body>
<script src="../assets/js/plugins/chartist.min.js"></script>

Then we’ll re-create the initDashboardPageCharts() function found in assets/js/demo.js by rewriting it as plain vanilla JavaScript. The only change here is the declaration of the function!

, initDashboardPageCharts: function() { ...
to
function initDashboardPageCharts() { ...

Here is the result…

<script type="text/javascript">
function initDashboardPageCharts() {
var dataPreferences = {
series: [
[25, 30, 20, 25]
]};
var optionsPreferences = {
donut: true,
donutWidth: 40,
startAngle: 0,
total: 100,
showLabel: false,
axisX: {
showGrid: false
}};
Chartist.Pie('#chartPreferences', dataPreferences, optionsPreferences);
Chartist.Pie('#chartPreferences', {
labels: ['53%', '36%', '11%'],
series: [53, 36, 11]
});
var dataSales = {
labels: ['9:00AM', '12:00AM', '3:00PM', '6:00PM', '9:00PM', '12:00PM', '3:00AM', '6:00AM'],
series: [
[287, 385, 490, 492, 554, 586, 698, 695, 752, 788, 846, 944],
[67, 152, 143, 240, 287, 335, 435, 437, 539, 542, 544, 647],
[23, 113, 67, 108, 190, 239, 307, 308, 439, 410, 410, 509]
]
};
var optionsSales = {
lineSmooth: false,
low: 0,
high: 800,
showArea: true,
height: "245px",
axisX: {
showGrid: false,
},
lineSmooth: Chartist.Interpolation.simple({
divisor: 3
}),
showLine: false,
showPoint: false,
fullWidth: false
};
var responsiveSales = [
['screen and (max-width: 640px)', {
axisX: {
labelInterpolationFnc: function(value) {
return value[0];
}}
}]
];
var chartHours = Chartist.Line('#chartHours', dataSales, optionsSales, responsiveSales);
var data = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
series: [
[542, 443, 320, 780, 553, 453, 326, 434, 568, 610, 756, 895],
[412, 243, 280, 580, 453, 353, 300, 364, 368, 410, 636, 695]
]
};
var options = {
seriesBarDistance: 10,
axisX: {
showGrid: false
},
height: "245px"
};
var responsiveOptions = [
['screen and (max-width: 640px)', {
seriesBarDistance: 5,
axisX: {
labelInterpolationFnc: function(value) {
return value[0];
}
}
}]
];
var chartActivity = Chartist.Bar('#chartActivity', data, options, responsiveOptions);
}
initDashboardPageCharts();</script>

At this point, you will find that the dashboard page still functions and charts are still displaying.

Now install Lit-Element using npm:

$ npm install lit-element

As an example, let’s create a dynamic one-way data-binding table of some of my favorite movies as a web component and put it in the “Users Behavior” box on the dashboard.

First create a new web component named: movies-table.js.

import { LitElement,html } from 'lit-element';class MoviesTable extends LitElement {
render() {
return html`
<div class="container-fluid">
<div class="row">
<div class="col-md-12"><div class="card strpied-tabled-with-hover">
<div class="card-header ">
<h4 class="card-title">Movies Table</h4>
<p class="card-category">Striped Table with Hover</p>
</div>
<div class="card-body table-full-width table-responsive">
<table class="table table-hover table-striped">
<thead>
<th>Title</th>
<th>Director</th>
<th>Released</th>
</thead>
<tbody>
${this.movies.map(
(movie) => {
var disp=html`
<tr>
<td>${movie.title}</td>
<td>${movie.director}</td>
<td>${movie.released}</td>
</tr>
`
return disp;
})
}
</tbody></table>
</div>
</div>
</div>
</div> <!-- row -->
</div> <!-- container fluid -->
`
} //render()
constructor() {
super();
this.movies=[];
}
firstUpdated() {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState === 4){
this.movies = JSON.parse(xhr.responseText);
this.requestUpdate();
}
};
xhr.open('GET', 'http://cloudsqlnet.lightbase.io:3100/db_test_test/movies?select=title,%20director,%20released,%20gross&limit=6');
xhr.send();
} //firstUpdated()
} customElements.define('movies-table', MoviesTable);

What we’ve done here is create a lit-element web-component that dynamically gets a JSON payload using standard native JavaScript. The html is borrowed from the file: “/examples/table.html” specifically the “Striped Table with Hover” sample html. The static html data rows were removed and replaced with Lit-Element’s dynamic one-way data binding.

Now we’ll place the web-component inside index.html by invoking the standard ES Module specification.

<script type="module" src="movies-table.js"></script>

We’ll remove the contents of card-body for the “Users Behavior” card and replace it with our movies-table web-component.

...
<div class="card-header ">
<h4 class="card-title">Users Behavior</h4>
<p class="card-category">24 Hours performance</p>
</div>
<div class="card-body ">
<movies-table></movies-table>
</div>

Now our dashboard looks like this:

Bootstrap 4 template with movie listing web component.
Bootstrap 4 template with movie listing web component.

But wait, what happened to the styling for the table? The answer is there is none. We didn’t write any CSS inside our movies-table web-component and the shadow dom prevents any CSS selectors from the outer page from applying inside the component.

Let’s solve this problem by adding the createRenderRoot() method to our movies-table component directly after the firstUpdated() method.

...
} //firstUpdated()
createRenderRoot() {
return this;
}
} customElements.define('movies-table', MoviesTable);

Reloading our page:

Web Component playing nicely with a bootstrap 4 template.
Bootstrap 4 template with movie listing web component automatically styled with bootstrap.

Ta-da! We have a bootstrap styled “striped table with hover table” and the best part is that we didn’t have to write a lick of CSS.

If you are writing a web-component designed to be run universally, rendering to the Light DOM would be a terrible idea. But if the component is meant to run only a single application, then this technique will save you a lot of time and frustration.

If you are wondering what the AJAX call was, it’s to a Postgrest server aimed at a PostgreSQL backend.

PostgREST is a standalone web server that turns your PostgreSQL database directly into a RESTful API

PostgREST allows you to send SQL statements via JavaScript and get a JSON payload back with the query results.

http://cloudsqlnet.lightbase.io:3100/db_test_test/movies?select=title,%20director,%20released,%20gross&limit=6

We’ve found it really useful here at Lightbase. And guess what? plug alert… You can get a free 500MB database combo including PostgreSQL + PostgREST and CouchDB from Lightbase at www.lightbase.io/freeforlife.

A code sample with the modified index.html and movies-table.js is available on our GitHub repo: lightbaseio/bootstrap4lit.edu.

That’s all folks. I hope you found this article useful in your never-ending quest to Git It Done. If you liked it, please clap the article and follow me so that I know to write more.

--

--

Lightbase
LightbaseIO

Database-as-a-Service provider of ready-to-go cloud hosted SQL and NoSQL software.