Currently UI-Grid doesn’t automatically show any messages when data is loading.
Nor does it show anything when there’s no data to show.
This can be a bit confusing for your end users as all they’ll see is an empty white space in the grid.
Luckily this is easy to fix with the most misunderstood feature of Angular 1.2: transclusion.
Angular is a Donut
Think of transclusion as a delicious jelly donut. UI-Grid is the donut and the functionality you want to add is the jelly. Is your functionality raspberry flavored? Never mind; don’t answer that.
In addition to simply being inside the donut and available for consumption, your jelly filling
has access to all the features and properties of the donut. Like the frosting or
sprinkles, if there are any. Alright so maybe the metaphor breaks down there. The point is that your
transcluded will have a scope that is a descended of your grid’s scope, so you can access the
grid
property and all of its settings and data in your expressions.
What does transclusion look like? Not all that much:
1
2
3
|
<div
ui-grid="gridOptions" class="grid">
<div>I'm a special little transcluded div for grid {{ grid.id }}!</div>
</div>
|
The div inside your ui-grid
element is what’s being transcluded. Pretty simple,
right?
Alright enough theory, let’s get started. You and I are going to look at two use cases:
- A “Loading…” message with a spinner indicator
- A “No Data” message
Loading Message
This message will be an overlay that will sit on top of the grid while your data is loading. The markup
is short and sweet, just a couple divs and your message. We will use an ng-hide
on the
overlay that will only let it show if we are loading and have no data.
1
2
3
4
5
6
7
|
<div
ui-grid="gridOptions" class="grid">
<div class="no-data" ng-hide="gridOptions.data.length && !loading">
<div
class="msg">
<span>Loading Data...</span>
</div>
</div>
</div>
|
Our controller code is pretty short as well. We’ll make an init
function that is
called when the controller starts up. That will set a loading
flag on the scope, and then
unset it in our data fetch’s finally
handler.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
app.controller('MainCtrl',
function ($scope,
$http) {
$scope.gridOptions = {};
init();
function init()
{
$scope.loading = true;
$http.get('names.json')
.success(function
(data) {
$scope.gridOptions.data
= data;
})
.finally(function
() {
$scope.loading
= false;
})
}
});
|
Alright, if we fire this up what are we going to see?
Hmm, that’s not quite right. It looks like any content we transclude will just be appended to the grid element’s contents. I guess we’ll need some custom CSS to position it correctly and make it look nice. We want the overlay to:
- Overlay the entire grid
- Darken the grid a bit with some opacity
- Generally look nice
- Have an animated progress widget
Here’s the CSS for all that. Read the comments for what it all does.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
/*
This is the background of our overlay. We need it to
be
absolutely positioned within the grid, and fill from
top to bottom, and the full width. It will also have
a black background with 40% opacity.
*/
.grid-msg-overlay {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
background: rgba(0, 0, 0, 0.4);
}
/*
This guy will contain our message. We want it centered
so it's positioned absolutely with percentage-based
offsets and dimensions. It also has some basic border
stuff and most important is using "display: table" so
we can vertically center its contents.
*/
.grid-msg-overlay .msg {
opacity: 1;
position: absolute;
top: 20%;
left: 20%;
width: 60%;
height: 50%;
background-color: #eee;
border-radius: 4px;
border: 1px solid #555;
text-align: center;
font-size: 24px;
display: table;
}
/*
Lastly this is the actual message text. It uses
display: table-cell so the vertical alignment
works properly.
*/
.grid-msg-overlay .msg span {
display: table-cell;
vertical-align: middle;
}
|
Note on Vertical Centering: This is beyond the scope of this post, but there’s a bunch of different ways to vertically center elements. I’m using table CSS here, which might not be the best for you. Check out this Chris Coyier post for more.
Our result is much better! Let’s take a look at a demo of what we’ve just done:
OK, looking good. But what if nothing loads and our users are still staring at an empty grid? Since we’ve got Let’s add a “No Data” message.
No Data, No Problem
We already have the CSS set up so all we need is some markup:
1
2
3
4
5
6
7
8
9
10
11
12
|
<div
ui-grid="gridOptions" class="grid">
<div class="no-data" ng-hide="gridOptions.data.length && !loading">
<div
class="msg">
<span>Loading Data...</span>
</div>
</div>
<div class="no-data" ng-hide="!gridOptions.data.length && loadAttempted">
<div
class="msg">
<span>No Data</span>
</div>
</div>
</div>
|
Then we can add the loadAttempted
flag to the finally
handler:
1
2
3
4
|
.finally(function
() {
$scope.loading = false;
$scope.loadAttempted = true;
});
|
Here’s our resulting plunker:
Et voila! We are done. Transclusion is great for when you just want some simple extensions. Would you like more examples, such as:
- Selecting data for UI-Grid by selecting from a map of the US
- Loading message while using external paging
- Drag-and-drop file input for loading custom JSON data into grid
Enter your email below and I’ll send you a link to our bonus area where you can these uses cases for transclusion in UI-Grid, with example demos+code and explanatory documentation. Enjoy!