UX first with SvyProperties module (Tech Webinar #73)
UX first with SvyProperties module (Tech Webinar #73)
Today’s webinar is how to create personalized experiences in application. And it’s also about a new module that we just released when we released the latest version of the SFI security extension. So we kind of maybe skimmed over this in one of the last webinars when we were announcing that complete package release, but there was a lot of stuff that was a really long webinar. And this, I think, warrants its own small webinar. So these webinars should be, I think, pretty simple and pretty short. And to the point, but we always like to start with a few demos to first show you what kind of end experience we’re going for and what’s possible. And then we’ll dive into the code a bit and talk a little bit about use cases or best practices. So I’m going to jump first into an example solution that I have running. So this is a nice looking application. It has some navigation, it has a grid. And what I want to show is how a user can kind of, you know, work in the application, log out and log in. It should be kind of left the way they, you know, they had last left it. And although this is kind of simple on the surface to do it actually takes a bit of work and what I hope to show is that this module makes that a bit easier. So for example, suppose that I do some filtering here and I decide I’m going to filter by customer. And I select a few customers. And I apply the filter and maybe I also want to filter by. Ship country. And I think we do. Maybe a few of these. So now you see I have orders shipping to France and Mexico and also filtered by these customers. If I mouse over I get it in the tooltip. So I might be working there. Maybe I have a list of orders that I need to send out notifications for something. And well, I log out. And what I want to have happen is when I log in that I get the same setup. So if I come back to the orders. You can see that the filter is there with the customer and the ship country. Another example of this could be suppose I have some some styling. And I want to I want to apply kind of my own. My own theme for that. Perhaps I do this and I like right. Don’t know if this is going to look good. No, that’s not too bad. And then suppose that I log out and log in and again we want to see the same sort of thing. That’s the application remembers who I am and kind of what I’ve done. Oh, I’m putting in. So now you can see in this example it did remember the sort of style template that that I created for myself. So this is a nice sample application. It looks good. It has a lot of stuff in it. This is actually running at sample.servoy.com. We’re going to talk more about this in another webinar. But what I wanted to show was a nice looking application. But make things a bit more simple. I thought I’d strip this down and take you guys through how to do this in a more simple application. So in this case I have a simple version of this application. And really it’s just one form and all the code is in that form. So it should be pretty easy to follow along. Suppose that I want to set some grouping on the grid by customer. And now that I’m grouped by customer maybe I want to also group by. Now all the customers are going to be in the same country. So that is where the groups will be the same. Let’s do sales rep. And then. I think this was. I should relaunch this. I’ll come in here to the IDE. I’m going to launch this in the debug client. And take it from the beginning. So I’m logging in as a admin user in the admin tenant. And I have already some default here that was done done the grouping. But suppose that that I want to change the grouping there. And maybe I don’t know. Maybe I want to see the sales rep over here on the left. Now I expect that when I log out and I log back in. I get that the same way. Yeah, it doesn’t do it. I seem to break in it right right before the demo. Let me try a different a different user. So this user I’m going to revert the grid. And. Let’s drag the sales rep over here and try this again. Oh, wait, I didn’t set them to save settings. Hang on. Rooker, you can see that I practiced this a lot before the webinar. Yeah, I do. Okay. So I’m going to take this save settings box. That’s another property that I set that it was just do it so that you can visualize how you could maybe turn it off or on or something. And then I’m going to drag this over here now. Hopefully if I log out and log in, we get it to look right. Okay. So I have to go in and I’ll just get into the code and show it to you. And while we do that, we’ll probably be able to debug it pretty easily. So I have a grid here and it has some events. That’s what it is. The on column state changed event somehow became unhooked. I must have hit undo or something somewhere along the line. So, but I’ll follow through with that and then hopefully we’ll show it working. So this is triggered anytime I’m reorder the columns or change the grouping. And I have that little flag, this little check box down in the corner here. If that is checked, then I’m going to store the grid state. And this is a little method that I wrote and this is where I want you to follow along because this is where that module is used. So there are really two ways that you identify the property that you want to save and then the value that you save is always a string. So it’s really simple. We’re going to do scope.sfy properties set user property and then we pass in a key and a property type and a value. So think of the property type as almost like a really general name for the property. If it were a database, this would be a table name. So it’s not the actual identifier of this specific property. In this case, we’re calling a grid state. But this could be grid state of any user on any form, say in any application. Then the key is really identifying the unique instance of that property. And I like to take like a namespace approach. So here I’m calling it calm.servoy.example. and then controller.get name is the name of the form. So that it could be I have 100 forms. Maybe I put this logic in a base form and it just works everywhere. All the grids get saved. But each grid is a different value or a different instance. So I have this namespace. It could be the name of my application or solution. And then the name of the form, I could apply this as a module that works across even solutions. So think of your key as something really kind of like a namespace. And then there’s the value which can be anything. It can be a string value of any length. In this case, I’m just asking the grid for the to get column state. And that actually returns a string that’s already kind of formatted in JSON type format. And then finally, I just pass it, I pass it in here calling the set user property of the SUI properties module. So that’s how how it gets saved. Now that I made that change and I saved it, let’s go back and try to rescue the demo. So this is toggled. I’m going to drag the sales rep over. And I’m going to say a prayer that when I log back in, it will work. This was this user, I believe. Okay, there we go. So that was it. It was just it had become unhooked from the event. I must have actually disconnected it right before the demo. But now you can see that the sales rep comes here. And so you can see that I could again, I could group by sales rep. And I think by country is what I had planned to do. You can see that there’s some grouping here now in this nice grid. Suppose I want to leave it in this way. And this should work again. Logging out, change the user log in. And now you can see that it remember that it’s grouped by sales rep and then by country. Now I want to show kind of the inverse of that, which is well, how would I clear this? And I’m going to click reverts. And that does a couple things. It reverts the grid state to the original design time state. But then I also cleared the property so that when I I log out and log in, it doesn’t remember that property. And it should be remembering kind of the original state or defaulting to the original state. So now you can see it’s in the original state. So quickly what I want to do is look at the where I had cleared the. The storage setting. And if we look on the design of the form, I have referred here. It’s connected to this event handler called clear grid state. And first I tell the grid to to restore itself to the design time state. That’s just an API of that actual component. And then the next thing I do is I again, I get the property type, which is the general property. The name is the key in all of these examples is going to be the same. Because there are different properties, I just keep the key is really just this example dots and then orders, which is the name of the form. And then here I explicitly grab the tenant name and the username from the currently logged in user intended. And then I get that property and then I delete it. So it’s a couple of steps to delete the property. You could also set the property to null, but that actually leaves it there. So delete is a bit more specific. So I wanted to show that. So that’s how I cleared it. And now this tenant doesn’t have any or this user doesn’t have any. Save settings. I’m also going to uncheck this. This gets remembered too. So I have a setting to save the settings, which is also persisted so that as that user logs in that remains unchecked. And that’s important because I’m going to show in a minute how to sort of set tenant level properties that all your users can get. And so what I’d like to do is log out and I’m going to log back in as that other user named admin. Supposing that this user has. And you can see their grid is is the same as before where they had customer and and and or sales rep in country. And also, they have some columns turned off. What I’d like to do is copy the setup of this grid to the tenant and show how that can be applied tenant wide. So I’m going to click a copy to tenant and. I’m going to log back in as that user who. Who didn’t have a default who no longer had a default grid setting. It was just cleared. Now again this. Must be have remembered it. Because if they don’t have they should get the tenant. Try this once more. There we go. I don’t know what happened. Now you can see that when this user logs in they get the original or the setup that the other. User made that we saved as like copy to tenant and then you can override it if you have it. But here they didn’t have it the setting safe. So it’s not been overwritten. If I toggle save settings and suppose I. I removed the country group. I’m going to go out and log in. I should get. Now overwriting again and then all. Yeah. So now it’s just by sales rep. So now I’d like to jump into the code and show you that because it’s a sort of a different scope. Of saving a property. If we look at this form and the copy to tenant. And I look at the. I have to move my go to meeting controls. There we go. And I look at this method again. Different property type. It’s called grid state. Well the same as the user property and the key is the same. However the. The call is slightly different here. I’m calling set tenant property where in the other case I was calling set user property. So it applies this property for the current tenant. And when I’m on. On show and I didn’t show this before but if we look at. This form. And we look at the on show event. This is when I’m processing all that so if it’s the first show. What I’m doing is applying. The grid state. And you can see that in the apply grid state it looks up the value so again it takes the property type it takes that key. And this time it’s calling get user property. And that returns or get user property value that returns the value as a string now it could be that the user has no. Value and in this case if they don’t I check the tenant value. And it so if they have a tenant value I grabbed that instead so in that this is how I’m doing. the override the override itself is not implicit in how you store it. Just that you can set it for the end. And or the user in this case if the user has it we choose to use or otherwise. The tenant. And so if there was a setting for either of them then I call back to that component and I restore that. So really we’re storing a string we’re getting back the string we’re applying it. Just that there’s an underlying framework and database for how this stuff is stored. And it does some nice filtering for the end and the user and makes it convenient. So you could really put anything in here. Something else that. That we did on show that I’d like to show you is. I think go back to the on show handler. Is the record selection and I didn’t point that out. But what I’d like to do is for this user I’m going to. I’m going to clear the grouping. And I’m just going to select one of these records here suppose that I was. Get in an account as a. An employee using this application and when I come back I want it to be on the same. Say the same data so I’m even capturing the record selection event so when I log back in it should be this. This particular customer here. So logging back in. So that’s what I’m going to do. And so we can see that the first record should get that same super market. So now you can see this record was selected. The default would have been to have selected the first record in the found set. So one thing is how did we capture that. If we look at the on record selection events of the form. And we can see that that ties to. Well, one thing is, is I sort of don’t want to capture the first record selection because record selection happens before. Form show so. I have to ignore the first one. And then and then. Grab the next one so. I log if the form has already shown as already runs. So that’s all this is just a bullying that I set up above here. But then on every subject when record selection. I’m calling this store record selection. And this should start to look pretty familiar because I have a different property type here record selection. The key is actually the same. I’m just reusing that key over and over again. And the value is now we stored as a string. So I call this to fix which converts the number in this case. Because it’s an integer database column into a string. And then I set it for the user property. So that’s how I’m just remembering what the last selected record was. You could do anything with this. You could you could have like recent. I think that the UX would be better than it just magically selecting it. And I seen this in. In other applications of our customers where they have maybe the five most recent. Customers they worked with or orders they were looking at, etc. And and again we could store anything in that in that value. It could be an array because remember you could just convert everything to base. And so you could have an array of the five most recent records and array of your favorite navigation spots. So you could do anything in here. And it’s just a really a framework for how you store and recall that stuff. And so finally to finish the example. If we look at the on show. I have the apply record selection method. And it’s just the inversion of this. I get the property type in the key. And I get the user property value. If it exists, I call found sets like record on that. And and try to select that record. So there’s just a few examples of what you can do. I wanted to keep this pretty short. Do a quick overview of what we saw. And then we’ll take some questions. So again, what I’m showing you was part of the sfy properties. Module of the sfy security extension. The reason we included this is that properties actually have a database. Small database table for them. It’s very simple, but we we wanted to include it with an existing database that’s shipped with another module. And also it has that dependency on user antenna. Now, you don’t have to use the user antenna part. You can also apply global properties, which will apply to everyone. You can also. If you want to set properties on a user that’s not logged in, you can do that. So there’s a lot of more stuff there in the API. But what I showed you is really the core way that you might use it. And this really just provides the property persistence and the user antenna. And global scope. So remember to use those. And you can really put anything in those. Just a couple of observations of or best practices. Number one, there’s that key, which I think you should do like a namespace. So it could be the name of your solution dot the name of your form dot the name of your component. If it’s like a component setting. So really you can think of any component grids. So if navigation menus, that sort of thing. Another example could be recent searches. So if you’re in a typing in the search box, you want to show searches. The user’s done recently. You could do that. There’s really no limit to how you could use this. And if you do store complex values, remember everything just gets stored as a string. So it’s intended to store long strings, including serialized jobs, or objects. So you can arrange that however you want. Just remember that you have to deserialize it when you when you get it back from the API. I’m going to bring up the documentation and the project home for this while we take some questions. So this is under the SFI security if you go to the wiki from that for that project. And if that’s new to you, we just did a webinar on this I think last two weeks ago. So you can rewatch that webinar. But if you go down to the SFI properties link here on the wiki pages, it’ll give you a getting started guide in a little bit of an overview. And then of course there is complete wiki documentation there for how to use all of the properties. And this can be added or included into your solution by using the package manager in the IDE. So if you’re new to that you take your active solution. You right click you download with the package manager and you come all the way over to solutions over here. And it would be SFI security has has that now included as a module or you could you could add it here. Sorry it’s the other way around SFI properties depends on on SFI security. Just click the plus there and it will insert it into your application. Okay I’ll leave up the useful links. And Ritker do we have any questions? Let me check. Yes I see a couple of questions. This one you just answered where is the documentation. Okay. And another one is asking how we to do the login with the switch standards. Okay yeah so to make the example work I had to I had to log out and log in as a different tenant or as a different user rather. I didn’t show the example of a different tenant, but you can imagine that. If I wanted to there was the one example where I saved the tenant level property with a certain group configuration to show how users who haven’t over written that could get it. And so I had to log out and log in the switch users but it could be that I even come in as a different tenant and then you see that it really has the. A different setting there so I could have showed that. But to answer the question. The log out and log in was facilitated by the S.P.Y. Security module so I didn’t actually even. Build anything if you look at my application. It’s just I have two forms here I have the outer navigation form in the orders form that I was showing you. The log in form for that solution and all of the multi tenancy and that sort of thing is handled for me by that framework so you can see the log in form is a pre build. You X template, we did a webinar about that recently when we did the launch campaign so. I would go back a few webinars and watch those because that’s all done for me by the by the extension you can see that I have. I’m standing on a lot of extensions here with the security you X which provides the UX template and the navigation you X which provides the navigation you X template so I’m reusing a lot of those which is how I’m able to do with really just one form. Other questions. Yeah, I got a remark. Wow, this really helps. Another question is can you query. Yeah, that’s that’s a good one so at the end of the day this stuff does just go into a database and the structure for that is very simple we wanted to do it that way on purpose so we really thought hard about the schema for that. It’s intended to be used through the API but you can query it so. Click the wrong one here if you look at the it does require the SUI security database and there’s one table in there called SUI properties. And you can see that it’s a pretty simple schema so you can query that directly. The other thing is if you look at the the API documentation there’s a lot here that that I that I didn’t show. For example, you can when you’re just search for it there’s something called get properties. And so this is returning an array of property values rather than I the what I was always going with get property value which assumes that you got the property that you wanted for that particular user tenant. But you can get properties and then you can pass in some optional arguments like the property key itself can have wild cards in it. So that’s when that namespace that I recommended really comes in handy because you could say yeah I want to get all of the grid settings for this particular user this particular tenant. You can query that way through the API so I recommend reading the documentation going through the API if you need to get kind of fancy with it if you wanted to for example show a list of. One use case that I see is I see customers that that do reporting and they have these screens to launch the reports and the users may put in a bunch of parameters for the reports and they want to save almost like instances of those reports so they’re going they’re making database tables and that they’re very strongly typed to the type of thing they’re doing. And this is a really good use case where it gets a bit more complex because you might say three port parameters as a as a Jason string and then you there’s a display name value as well you might want to have a nice list of. You know sort of quote reports or report instances. And then the display names are what show and the user and you could say that as a tenant property where all of them all of your users get these five reports and they’re really just you know configurations of the same report or something like that. That’s a use case where I see that and in order to do that right you’d have to kind of query the API by getting you know a list of properties rather than a single value and then each one of those has a display name so. And it’s been more complex of an example, but we could always do another webinar where we get deep into this if there’s interest in that. All right. Another question that comes in. Is there a limit to the size of data value that can be stored. Well, there is a limit we set it to be really high. So it’s not just a 50 or 100 length string column is really a large fair care in the database. So there there is a limit to what you can put in there. But it is intended to store you know moderately complex Jason. You know serialized JavaScript. For for complex examples like the one I just described, but there would be a limit, but we said it really high if you look and and again this just goes to a database. So if you actually want to change the length of that you can. Also another topic that I didn’t get into is that you can imagine that in a complex application say an ERP application with 500 screens and multi tenant where each tenant has a lot of users. That you start storing really a lot of properties. And that’s another reason why we made the table structure very simple so that when you get into tuning this you can apply indexes that still make it perform really fast. So we’re confident that this performs well even for complex applications with a lot of users because that’s what’s intended for it’s intended to be an engine to really. Drive personalized experiences for complex business applications in a multi tenant environment. Okay, cool. Yeah, I see one more question. That’s about where where was the code for saving the record filtering that was shown initially. That was in a I didn’t have that. In my workspace I was showing that sample so I think what the user is asking for is. Here, and I had applied a filter here. I can actually show that but it might take a minute to load to load that. But it almost looks the same that that is actually the pop up filter. And we did a. We did a webinar on that during the launch campaign back in December and I think even in that webinar I sort of briefly pointed out that it’s saving it. But if you look at the API for that. There is. In this case it’s it’s just calling set application or application set user property which puts it in a cookie, which isn’t the way that you’d want to do it. But if you look at this, this is what what you would see in in in the code there you’d see the filter state, which is a lot like when I asked the grid about the column state. The tool bar filter was the filter state it returns back it’s string which is really serialized JavaScript. Then you make up the key just like the key that I made up before. And then instead of calling set user property which is the old way to do it where you might put something in a cookie, which you wouldn’t want to put all this stuff in a million cookies. You really want to store server side so it’s also a device independent. There’s where you would say set, you know, SUI properties set user property you pass in the key, you pass in the property type it could be you know filter state something generic like that. And then the value which would be this filter state object so it would look almost identical to saving the grid state just that you get back this filter state object from the toolbar filter component. And I don’t see any other questions right now. Okay. I would just leave with this that we plan to to enhance this some more so we’re starting here with an API. To show you what’s possible you have seen in the last few webinars that we’re now starting to provide you ex templates to take these APIs to kind of the next level by combining them in sort of extendable templates that can sort of instead of you having to write the code can sort of implement the code so. What we’re looking to do in the future with this particular project is to provide some built in experiences like save searches like favorites in the navigation like you know remembering every grid on every form because you’re using a base form. This is a building block but I think we can we can make this pretty powerful and start to with some of the those UX modules that we provided in the last lease in the future releases we’re going to keep enhancing those to provide more in the box so you really get to writing less and less code but it’s nice to the developers to see under the hood how it worked. Cool. I’m sure did this at value. one one last question is is coming in. For a new sample test on the fresh Servoy developer. What is the question. Remark. Can you repeat it for a character. We’ve already assembled tests on a fresh Servoy developer. A sample test on the fresh Servoy developer. Oh wait. Give to better sample. Yeah, it’s. Okay. Alrighty, another thing that I would just point out is that this example solution that I was showing we’re working on that and we’re we’re going to announce some more about that because we want to try to combine all of these best practices and ideas into something where people can break it down and consume it and learn from it so. Yeah. Alright, cool. Thank you, Sean. My pleasure. Thank you, Rasker. This is definitely adds value. All the entities. Also, thank you. Hopefully you. You have enjoyed the session and you’ll see your chat are in two weeks. Okay. Bye bye. Bye bye.