New App Design Considerations Part 6 - variable number of Grid Elements

Part of the app I’m working on will require a series of buttons (they will actually be implemented as an image and a label). They will be presented in a CSSGrid, with each button in a single CSSGridItem. The number of these buttons is dependent on data from a server, so the number is unknown until runtime.

One solution is to have the maximum number of buttons/grid items already defined at design time and hide all the ones not needed. This is very messy at design time. It probably loads the faster than the next option.

Second solution would be to create the griditems and the button and label children at runtime and add them to the CSSGrid using something like https://blog.appstudio.dev/2012/10/creating-runtime-buttons-bonus-a-single-event-for-all-your-buttons/. The CreateDiv sample has similar code.

In the second solution I would create one griditem with children formatted as desired, and then copy the properties from this original to the ones being created. It seems to me that maintenance of the layout of buttons would be much easier with only one to keep up with, rather than dozens that fill up the design screen.

I don’t see anything wrong with this approach.

jQuery has a .clone() function which might help:
https://api.jquery.com/clone/

A third alternative presented itself. I think it is simpler. As I stated, I am creating one CSSGridItem under the CSSGrid in the format I want all of the GridItems to be presented in. The only change would be image src, title text, tooltip text, etc.

During project start up, get a copy of the innerHTML of the GSSGrid - we’ll call it the original innerHTML. It has all of the HTML for the CSSGridItem, Container, Image, Text (and whatever else I add/change later).

Prior to presenting the form with the Grid, use the original innerHTML and do a search and replace to change the IDs, SRC, textContent, etc as needed in a temp string for each GridItem HTML segment, Concatenate all of the GridItem HTML segments, and load the concatenated HTML into the innerHTML of the GridItem, replacing the original innerHTML of the grid, but saving it above for usage later when repainting a new menu.

Unfortunately, it didn’t really work that way. It appeared to be work because I had a typo in the replace statements for the IDs. The IDs were not changed. When I fixed the IDs, the 2nd and following griditems lost their formatting. The CSS is only setup in the IDE for the 1st element I defined. Copying the HTML is only half of the problem. (the jQuery .clone can copy CSS settings if I had taken that direction).

Leaving all of the IDs the same provides the CSS for painting the grid correctly, but without IDs, there is no way to associate the onclick to the item in each grid. We could use the onclick of the FORM (div) to capture the click, we can use the ID returned to know it’s one of the grid items, but then we’d have to figure out which grid item was under the cursor. Not sure I want to go there.

It seems to me that adding the style on the fly as each grid item is added would work. I copied the CSS styling from the index.html file and insert for each grid item the following the HTML of the new griditem (eg, concatenated the style elements with the grid and control elements:

<style type="text/css">
#HomeGridItem1 {position:relative; display:inline-grid; height:100px; width:100px;}
#HomeGridContainer1 { display:flex; height:100px; width:100px;}
#HomeGridImage1 {position:absolute; height:100px; width:100px;}
#HomeGridLabel1 {position:absolute; height:100px; width:100px;}
</style>

This solved the problem. I not only have unique IDs, but I have the correct styling copied easily too. Note that since the CSS styling is in the same group of code as the items themselves, replacing the innerHTML of the Grid replaces the styling as well. This seems to be a very workable solution.

An improvement would be reading the style that was setup in the IDE for the first element by it’s identifier (#HomeGridIntem1, etc.) and using the returned value to setup the style tokens.

But, thinking back I realized there are CSS classes that should solve this problem as well. Or using the style property of the controls within the IDE could also do the trick.

You’re always better off staying with css classes vs inline styles for a variety of reasons. However, assigning an id to your inner container will also alleviate suffering pain as you’ll be able to work with the contents of each grid item directly and you can use one onclick handler for all the grid items. Just set your id="##" on that inner div.

Also, when you look at the class statements you posted, those are assigned to id’s.

#HomeGridContainer1 {...}

is assigned to the element with that id.

if you change the class definition to this

.HomeGridContainer {...}

Notice the dot instead of the # and then use it as

<div class="HomeGridContainer" id="1">Image</div>

you can apply that same class to ALL the containers within that grid. You can then set an onclick handler with jquery using

$(document).on('click', '.HomeGridContainer', function (that) {
    // your function here
    event.stopPropagation();
    event.stopImmediatePropagation();
    var id = that.id;
    //(... rest of your JS code)
});

While the above code has several typos, it’s there to get a point across.

Since all the grid cells have the same class you can use one handler. Because you’re using one handler you have to stopPropagation or it may (will) get fired on all instances of the class.

The other alternative is to place an "onclick=“doSometing(this)” in each of your containers and when a user clicks one of the images or labels (anywhere in the grid cell), the onclick handler will call doSomething passing the ‘this’ object and allow you to extract the id.

Lots of options.

I’ll have to figure out how to assign a class to controls in AppStudio. I see where to create them. What I was trying to avoid was manually creating the CSS. That is the point of an IDE. To make the definition visually, rather than as code. :cowboy_hat_face: And then at runtime copy whatever was created visually at design time and apply it to all the controls. But I assume I could reference the “designed CSS” and make it the class used by the other controls. I’ll be looking into this.

Controls have a class attribute in AppStudio’s Property Grid. You can put the class names there.

Under the Project menu, there’s a selection called Project CSS. That’s the place to put all your CSS for classes (and other CSS as well).

The improvement for reading the CSS Style token of the base controls is easy:

const CSSIDs = document.getElementsByTagName('style');
console.log(CSSIDs[0].innerHTML);

Note, this assumes there is only one style tag. This is generally the case, but it is possible to have multiple tags, in which case you’ll want to concatenate the contents of all of them to find your base Styles.

And while you are copying the innerHTML of the original grid to each item with the modified IDs, you’ll know the IDs of the base controls. So looking them up in the style content is easy.

In other words, you can automate the process, name the base controls whatever, have as many or few base controls as desired, and it will always work.

The potential array of CSSIDs can easily happen when you specify a CSS section in your project. Those styles will be created in a 2nd style tag. This really doesn’t change the above posting, as the ID based styles (with # as the first letter) will be in the 1st (zeroth) array element, as they are in the 1st style tag.