RX 24 - The New Obsidian Grid - Nick Airdo
Transcribed Content
Alright. Welcome everybody. The next session is gonna be the Obsidian Grid, and Nick Erdo is gonna be presenting. Let's give him a round of applause to get started. Alright.
You guys can hear me? Everybody hear me in the back? Good. Alright. We have 40 slides in forty minutes, so I can't delay on any one slide.
So it's going to be a little bit rushed at times. Just know that there's a link at the end with lots and lots of details you guys can read to go deeper into the few things that I'm going to be able to show you today. So I think I talked about this in my observability session. We often, you'll see this in older documentation where we've got, this is basically flipped, where the C sharp is at the bottom and we tell people if you know C sharp you can do so much. But we recognize that there's way more people that don't know C.
So we're like the tip of the pyramid in this diagram. Okay, so what is the grid? If you simply state it, it's a UI control. It's probably one of the most powerful controls in the entire Rock Obsidian UI toolkit. It's used by obviously what we call our list blocks to show you lists of things.
And it's also used by some blocks to show smaller lists within blocks or the output of your SQL query table rows in the dynamic data block. So have you ever come across the UI toolkit for Obsidian? How many of you know about this? Raise your hands. Oh, not many.
Okay. So I don't think it's on a public page yet, but if you go to where the old UI toolkit is, I think it's under CMS, it's called like control gallery. If you look there's a new version of the Control Gallery. It's the Obsidian Control Gallery. I think we're going to end up throwing that onto that page so you'll be able to see.
But basically there's over 180 Obsidian controls that you have available to you. And unfortunately the grid is too powerful to be seen in this gallery. So you won't see it there. It's in another gallery block called the Obsidian list gallery block. So again, if you go to a page, you want to throw that block, the Obsidian gallery list block onto a page, can then play with the block that you're going to see throughout this session.
I like looking at that block because it's, you can look at the code, you can go see how we did it, it's a little more interesting. However nowadays there's also the financial batch list block, which is also Obsidian. And I'm going to be using a little bit of both during this presentation. So just really quickly at a super high level, the features of the Obsidian grid. It has paging, a little pager at the bottom.
Filtering, which is going to be is awesome and we'll talk at length about some of those things. It's got sorting. It has grid actions or custom grid actions. I'll show you where those go if you're doing your own development with that. It has exporting and much, much more.
Now before I go into the breakdown of what how we see the grid, how many of you in here are actually potentially C developers that will be maybe using this in your own code? Can I get a show of hands? Okay. So for the rest of you, it's not too late to leave my session. This isn't really gonna be for the developers that want to.
Alright, well you've been warned. We like to think of the grid in four parts. There's the grid state, and that's kind of the internally managed state of your grid at any one point in time. It's really an internal piece and you don't need to worry too much about that. But it caches data.
It tracks the column definitions and the row data, tracking it, meaning it knows what state they're all in. It knows about row selection. Again, it's all internal. You don't need to worry about that. There's the grid shell, which is really the definition and the properties that make up your grid.
Okay, well that's all I'm going to say about that. Then there's the column definitions, which is really the meat of your grid. It's defining what each of your columns is going to look like, what data it's holding, what kind of data, what kind of filtering you want. We're going to get into that at length. And then last but not least, it's the row data, which is exactly what we'd think it is.
It's just the data that makes up the rows. So let's move on to the grid definition. Now I learned a new trick today. Okay, how's that? Better?
All right. So this is an example of a grid definition. So let's just highlight a few things. Or not. Let's see, is this going to work?
No. Is that really not going to work? Okay. The key field, that's just basically which row represents the key. In most cases, you'll probably want to use the GUID.
In some cases, it might be an ID. But yeah, there's going to be a lot of details about each of properties that you really don't need to worry about. So I'm going to gloss over at a really high level. Think of it as like management speak. The person key field is the next one down.
Yeah, this is not going to work, zoomed in. Okay. The person key field, if you have a person in your table, in your grid, you'll want to define that. That will unlock additional features that the grid understands people because they're kind of unique entities in Rock. The item term, that's the little thing that shows up in the, that label there, it tells you how many rows.
Show me, raise your hand in the back if you can see that. Okay, oh, Okay, good. So yeah, that's the little label there. It says 57 of 59 people. That's the term.
You'll want to make sure that when you're creating your lists that you're defining that term correctly. Because otherwise it'll just, it'll do its best job and try and probably grab the entity type as the name that it shows you there. There's a tooltip field. So here in this case, we're saying tool tip field is the email property or column. So how does that work?
Well when you hover over a row, that's what will pop up when you hover over that row, whatever column you define that to. There's several other things here. The sticky header, whether the grid stops, the header stops while you're scrolling down. There is the entity type GUID. That's probably important.
You'll probably want to set that. I'm not going to get into the details of why. And then the expected row count. I think I just blew right past that. The expected row count is important because there is something called a skeleton that will show up.
While your data is loading, you'll get a skeleton. And if you know basically how many rows you're going to have, you want to tell the grid that. So it doesn't like create a giant skeleton and then snap it once all the data shows up. So that's kind of how you can help control that. And then there are custom actions.
Those are the things that go in here. So that one is pointing to a grayed out item that's not active. But let's take a little closer look at the definition of custom actions. Because if you can see that, it just says custom action equals custom actions. So again this is from the financial batch list Obsidian block.
And it has a good example of how custom actions are created. So I encourage you if you're needing to do that, go look at that. It's a reference block that we use internally too, to remember some of these things in addition to the documentation. Now there is a little side note on here. That little blue badge that shows up next to the item term, there you see it's a one, that keeps track of how many rows you've selected.
So if I select another row here, it goes from a one to a two. Just a little detail I thought I'd pass along. Okay, let's jump back to the grid definitions. Last few items. Again, there's some other properties on the grid that you can set, but I'm just picking a handful to talk about.
There's the on add item and selected item of what we call event properties. And that just controls the behavior for when that plus, the little add button, when you click that, or what to do when an item is selected because you might have a custom action you want to control. Okay. Last but not least, filtered rows changed. In the event that you want to control or recalculate something based on which rows are selected, you can do that too.
In fact, the financial batch list block uses that feature. So as you're choosing rows, the summary at the bottom of that grid will recalculate based on which rows you have picked. So let's now talk about the columns. There are many, many column types that we've got already in the grid. It's got a Boolean column, a Currency column, Date column, Date time column, Label column, Number column, Person column, it goes on.
Text column, delete column, button column, select column, reorder column, attribute column. So don't and then column, column. If you run across a column that you think you need, just double check the list. And we might already have it. You don't have to create your own delete column or your own select column.
That's the check boxes in the front. There is also something called an attribute column. So the attribute columns, it's kind of a special placeholder column that informs the grid where it should place the dynamic columns that are tied to your entities' attributes. You know in Rock where you can say show in grid? That's for those kinds of properties.
So let's take a look at a column definition. I'll just zoom in to make it a little less painful. So here's a date column. We have a name, a title, the field that you're actually binding to. And then this is really important, the filter.
So that filter, and you see here in this case we have it set to date value filter. I'm going to talk about those in a second. But those are important when you're building columns to pick the right filter type. There are certain situations where you might want to use not a date column. You might not want to use a date value filter.
You might want to use one of the other filters. And it'll make sense in a second when you see some of these other filter types. And I'll show you what that even means. Like how does that what does that look like? What are you saying, Nick?
I'll get there. And then there is the let's zoom out. There is the visible priority. The visible priority is I think I have a better definition. It specifies the minimum window size for the column to be displayed.
So I think by default it's XS, which is like, hey we're going to show everything all the time. Like that's the smallest, narrowest grid you can make, like mobile phone size. But if you're in that mode and you've got 10 columns, and you're like, well, if somebody is on a mobile phone, we don't want to see all the columns. Then you decorate with a different value in the visible priority, probably small or LG for large or XL. Like this only needs to be seen when somebody's on a big giant screen.
There are some other properties that aren't pictured here. Hide on screen, that's kind of important to keep in mind. That will say, you won't see it on the screen, but when they export the data it will be in the grid. Now there's also an opposite property called exclude from export, which will say, yeah, we'll show it on the screen, but we don't want to include that in the export. So these are just properties.
You just throw them on there. You decorate that column, and that's how it works. And then last but not least is the width. The width is just going to control how wide that column should be. And as of today, if the string ends in a percent, then it's going to be the proportional percentage of the grid size.
If you don't end it in a percent, it assumes you mean pixels. And it'll just show you that many pixels. Now we're to amend that so that maybe Daniel's hearing this for the first time, that it will also look for any strings that end in px. So it'll be px percent or just a number, and that will be interpreted as pixels. So let's get back to that filter and talk about the filter.
So that filter controls what shows up when the person hovers over that column. And it's cool because a filter, a date value filter knows what a date is. And so you'll get some really cool filtering capabilities that understand the context of a date. So you can get between, you can get this week, last week. Things that we just there's no way we could do that before.
Not as easy as this. Like with no code, you the developer do not have to add anything. You just literally decorate that and boom, you get all these cool features. So in a nutshell, that filter understands what the date is. And there's tons of and filters to choose from.
So let's just talk about a couple of these here. The Boolean column, and this is showing the Boolean column with the Boolean value filter. So pretty simple, yes, no. The next one is a example of a label column using the pick existing value filter. Now this is the one that I think you need to really think about when you want to use this.
Because even if you've got, let's say, a list of campuses or a list of maybe some finite set, or maybe not so finite set, a set of data where maybe that one that value is going to be repeated on multiple rows. You might want to just use that pick existing value filter because it makes it super simple for the user to click that value and then boom, immediately their whole grid shows you only rows with that value. I think we missed this on a number of the blocks that we converted to Obsidian already. Hopefully we're going to get to those and re fix them before we drop them in v17. I'll be working with Nathan to make sure we do that.
There's also a number column and a number value filter. And again it understands what a number is. So how about that, top n. You can filter based on the top n or above average or below average for the data in that column, which is pretty sick if you ask me. All right.
Let's talk about rows. And I'm going to go maybe a little quicker here. The row data is just an array of objects. They're completely unknown to the grid. The grid doesn't really care about the structure at all.
The column definitions handle mapping the row data into something that can be displayed. Now it's obviously up to you to fetch the right data for the rows that you're going to fetch on the C back end. Now the documentation I'm going share at the end gives you like the whole recipe for all this. But in a nutshell, this is really it. Like this one method that you define in your list block, get list queryable.
You get a context, you use that context, Go fetch the data you want. Include things that you know you're going to need on the grid for performance reasons. In this case, we're only taking so many based on the count parameter. But it's pretty simple to get data. So the back end of a very simple list or Obsidian grid block or list block is pretty simple.
What is happening? Oh, have to zoom out. Don't like that feature. Alright, so little animated GIF here. One thing to realize with Obsidian is you're getting all the rows.
Like all the rows are coming back to the client side. So that's powerful because that means now the sorting, the filtering is like nearly instantaneous. When you are typing, you're using the entire horsepower of that person's PC to do all that. But if you have a grid, a row, or a query that's going to potentially return, let's say, millions of rows, that's probably a bad idea. You probably want to add a separate mechanism so you don't just go fetch all the rows and bring them back to the grid.
So just be mindful of that. That's all I really wanted to point out here. There is a feature within that grid. We call it the light grid, almost as if it's its own grid. Now there might have been an actual control called the light grid, but nowadays I don't think that's what you want to use.
You really just want to decorate the grid with the property. So let me just give you a quick view of what that looks like. So I'm going to click here. I'm going to switch this grid to light mode. Did you see it?
It's a little subtle. It basically got rid of that header. You still get the column headers, but it it moved the action bar at the bottom. And there might be a plus, an add button. Yeah, there should be an add button down at the bottom too.
So I know that was a little bit fast. Let me do it one more time for you. Let's slide the old one back in place here. And then so pay attention to the footer and the header up there. Personal list will go away, and those other features will go away.
Okay, there it is. Now again, you'll want to use that in spots where you have a grid that just needs to be tight. This is probably a bad example of a light grid. Generally you'd not have this many columns in a light grid. It's really meant for really light simple grids.
But it's as easy as just that. Like you just literally put the word light on the grid definition. And now it's a light grid. Okay. So this is kind of an FYI.
It's almost an aside from this presentation. And it's some slides that I got from Daniel that he wanted to make sure we talked about in the session. I apologize, Daniel, if I botched this. So I said it's a side note about blocks, client server. There's basically when we talk about Obsidian blocks, there are two pieces.
There's the server side, C. That compiles into a DLL. And then there is the, we'll say JavaScript part, it's actually TypeScript when you're writing it. And that gets transpiled into the .obs.js part. And so those two pieces make up really one block in Rock, one Obsidian block in Rock.
And this is sort of the, I guess we call it the page lifecycle. Page load will get or it will call get obsidian block initialization, which is really the C side. And then if I'm understanding this correctly, Daniel will nod his head up and down. The part in the gray box is the obsidian side UI, more or less. He's shaking his head.
He's like, yeah, all right. I'll let you get away with The setup, waiting for input, on form submit, calculate length. We really thought at this RX we might have time to do like a more deep dive into just like creating what is how does Obsidian work? Like break it all apart for us. But we decided we didn't have enough time.
So next year we'll go really deep in those things. And then there's the block actions. So on the C side is where the let's say you're saving data. On your grid or your Obsidian block, you're going to have a button that's going to call back to the server side. And we make that really simple.
You don't have to understand how did that get wired up. It's a little bit magical to have a, what I'll call an event or a method or a function on the Obsidian side that just kind of magically calls back to the C back end through the REST endpoint that we magically stitch together. And all you really have to know about is on the C side, you just decorate with a C attribute called block action. And the methods that are block actions are the ones that meant to be used with the events on the JavaScript side. But again, don't worry, it's like magic.
You really don't need to know how all that works. Okay. So now let's resume our regularly scheduled program. I'm gonna go a little now in-depth into the C sharp side of the Obsidian list gallery, just to give you an idea of how simple it really is. Okay, so this is really the bare bones Obsidian list gallery block.
And I think I stripped out the comments at the top. It starts on line 37. There's this method, get obsidian block initialization. I mentioned that earlier. Let's see, can I scroll?
Oh I can, good. It calls a get grid builder, which is then used in the grid definition there on line 59. And the bottom line here is this get obsidian block initialization, it's just returning a list block box. So you're going to see that term box and bag a lot in the Obsidian nomenclature. Just think of it as like, well it's the data that's getting shipped over to the client.
And we have tried to hide a lot of the complexity so you can focus more on building solutions and not worrying about the technical side of how do you make all these things happen. By now there's probably enough examples in even version 16.7. If you go look at the code base for everybody that has the branches, you'll see a lot of blocks that aren't even yet on pages. Those are coming and they will be cut over in later versions. But again, there's a lot of examples to look from.
Okay. So let's just take a look at that method, that get grid builder method. Because I think that's really where the interesting part of this is. So this is essentially building the definition that kind of goes alongside with the columns, potentially with the column definitions. And if you just read this, add text field, the nickname.
Add text field, last name. Add text field, etcetera, etcetera. Add a date time field. Add the attribute fields. So if you're dealing with attributes, not all list blocks have to, but there's at the very last line there, line 90.
Add the attribute fields and you can call a method, get grid attributes. I believe that one's also in the base if I'm not mistaken. Okay, yeah, yep, that's what I remember. And then a few things to point out here. We use camel case names for the field names.
That's the convention here. And let's take a look at Okay, if you'll notice on line 88 that get connection status. What's going on there? Nothing, nothing really magical. Let's kind of zoom in and show you.
That's just a special function that you can call. It's your own method. If you need to create your own custom value or what we call the list item bag to hold your value in your text, this is what it might look like. So this is again just a simple example where we're given a person from that previous query. At this point you assume you've got like from a particular row and you're fetching that value, that particular value for that particular person, their connection status.
And here we're just getting a color, grabbing the color, setting it to the list item bag's value. You can't see my mouse. I should not be pointing. So they're on line twenty three and twenty four. All right, so now that's really it.
That's the C side. It's pretty simple. Again, just a very simple, simple block. Just get some data. So let's look at the Obsidian side or the TypeScript side.
So this is the top of the Obsidian gallery list block. So we've got our template and our grid. I'm gonna kind of go fast because you already heard what the grid is. Or I should say the template followed by the grid control. There we go.
After that we have the columns that we're putting into that grid control. So we've got the select column, I'll zoom in for this one. This is an example of custom column. So you saw earlier we had the column just called column, column type called column. This is building a custom person row.
But I would say, well, do not do this anymore. Just take this as an example of how to build a very complicated custom thing. It's got an example of a custom skeleton for that while it's loading. Why would I say it's not recommended? Well because we now have there's the custom skeleton defined after the template for the actual markup that you want to see.
The reason why you don't want to do that anymore is we now have a person column. So the person column is a standardized column in Rock Obsidian which also has some capabilities to turn some things on and off. You can customize what shows up underneath the name slightly, although we'd like for people to stick with the standards as much as possible. But if you need to change the value that's underneath the name, that's how you would do it. There's a field for that.
Now I'm really going fast because I see the time is running out and I have 10 slides and 10. Alright, what was I trying to say here? Let's zoom in. Okay, so template, grid, inside the grid all the columns. Now we're ending the grid, we're ending the template.
That's sort of the end of the UI part of an Obsidian block, a list block that is. And now we start the script side or the code side. And so this is highlighting the code side. Notice here that we're using TypeScript. So it's a script setup language TS for TypeScript.
Then we've got really mostly boilerplate stuff like import in C. Those were our usings. So you're importing a bunch of functions into this class or this block so you can use those functions. We've got some configuration and setup that's use configuration values and use invoke block action. Again, I'm not going to get into details.
It's all in the documentation. You're going to see these though in lots of blocks. It's kind of somewhat boilerplate in my opinion. And then this is maybe a little bit more of the nuts and bolts. Here's the load grid data.
This is what will be called when it needs to go load the grid data. There are some event handlers in here. So what do we do? If you remember correctly, in the grid definition we said, what do we do when we unselect? Well this is where you define the TypeScript side or the Obsidian client side code for what to do in the event of an unselect.
So in this particular case, we're just saying when the row is clicked, when that item is selected, just change the windows location href to slash person slash in the key from that particular row. And then on edit, similarly, we're just taking them over to the person profile. But imagine, you could do whatever you wanted there. And here's another example of a on delete click. So if you had a delete column and they click that delete button at the end of your column, here's what will happen on the client side.
And oh, there is no is there a back end event for deleting? There is none. But not in this case. And then on add, somebody clicks the add button, just take them over to the new family. Whoops.
Alright. That's not what I wanted to do. And I call this like, well now we bind the grid. We're binding to the grid data source, similar to C. We end our script and that's really it for a very, very simple Obsidian list block.
Kind of end with this. Let me slow down a little bit. Goals and guidelines. We really like to see all of us on the same page and making our blocks look similar so that even with plug ins, they kind of feel and look the same. We've always had that philosophy.
But there's a couple of things I wanted to point out. And I hope I have a screenshot closer. Yeah. Here we go. Okay.
So we haven't really talked about that little button right there. Right there, that's a settings. So in the event that your grid needs some kind of filtering that we just can't put in the columns, this is the mechanism that you'll want to use and follow for what used to be the old fashioned grid filter in the old web form stuff where you'd click that filter button, it would open up, and then you got to control that UI. With the Obsidian grid, if you need that, we don't want we don't think all grids will need to do this. We're finding that it becomes a little bit more rare now that you can do all the filtering you need in the columns.
But the financial batch list block is an example where we used this setting because we don't want to go fetch eight years worth of data, 8,000,000, tens of millions of records. So if you want to control like, well how many rows should I go fetch? That's an example of how we achieved that. And here it is. So we built a modal in this case, and we're giving some additional settings.
The save button, when hit save, it will refresh your grid using well then you take those parameters, those additional settings, and do what you want with them. And then it might be hard to see on screen here. When that setting is set, it's got something configured in there, it would be what we call active. And so it'll be a little bit darker. And if it wasn't set, it would just be kind of like ghosted white and you wouldn't really quite see it so prominently.
Yeah. So there's an example. And again, I don't know if you can see the difference when it's not set. It's just kind of a white color. Let me see if I can go back.
Yeah, maybe you can see the difference a little bit there. Okay. Well that's really it. The last thing is, if you would call it a takeaway, is the takeaway. So if you want to write this down, I don't know if it's published anywhere else yet.
It's sort of been a secret for a long time. Only like two people outside of the core team have had access to this. This is the entire Obsidian developer's guide. Now I'll just say it's very in-depth. It's not meant for a quick start tutorial necessarily.
Would you agree with that Daniel? Okay. Yeah, so this is where we said, well we better start documenting all the things. So we started, really Daniel did the majority of it, started putting all the documentation in here. So there is a section on the grid.
There are other sections in this guide that probably aren't applicable to you. The stuff about the code generating tool, I don't really think that that's intended for Daniel's shaking his head. It's not intended for you guys to use and worry about that. But all the other reference material in that guide you can start using to kind of make sense of what you're seeing in the code as you're looking at code. Now if you want to you can DM me, I can't make promises, but you can DM me if you think, hey we're missing information about this important thing.
We probably know it, but it wouldn't hurt for you to just pass it along to me and let me know. And I'll keep a list, and we'll we'll we'll focus on that next because that probably means you're interested in that part too.