Iterative Development && Abstraction - Part 1
Tags: javascript, beginners, programming, angular
Published: 2024-05-31 11:45:23

Development is generally an iterative process going from brute dump of code on to the template to abstraction and then repeat with more abstraction until you find a balance of maintainability, efficiency, and functionality as well creating a better user experience. 

Sometimes, daresay most times, it's not all that obvious how a page might function or look. I wanted to build a "learn more" page of an Angular project I am reworking but didn't really know how to structure it. After a bit of experimenting, notecards seemed to be the right fit since this is a recipe site.

The next thing to do was to actually create some content to go on the cards. With some basic ideas of what the site should convey to potential members and a few design iterations, the following code seemed a reasonable start:

(Note: Using W3CSS for overall styling with some custom styling)
```
<div class="center-element w3-margin w3-animate-opacity">
    <div class="recipe-card" style="max-width: 800px; height:  1000px;">
        <div class="recipe-card-title recipe-card-title-position-1">
            create and edit recipes
        </div>
        <div class="recipe-card-body card" style="text-align: left;height: 250px;font-size: 2em">
            At mealHash, you can create and edit recipes. This includes areas for ingredients, 
            instructions, and other key points you'd expect as part of recipe.    
        </div>
    </div>
</div>
```
Since there were 5 points of information, the above code was repeated 5 times swapping out the appropriate text for each card. This gets the information conveyed but the experience is really just a "meh". And while users generally don't care about what is happening behind the scenes, developers do.

When developing, thinking (or not thinking) about the long term consequences of the code can have big impact on its maintainability. In this particular example, the question becomes: What happens if a card is to added, edited, or removed? 

Leaving the code as is, the answer would be open the project, CRUD the cards as necessary, test, recompile (using Angular in this instance), and redeploy. That's fine, kind of, if only an update or two is needed. But this isn't going to be a static site as over time we will be adding more features. So let's abstract a bit and while we're at it provide the potential member with a little better experience in learning more.

Making the "learn more" cards function as a carousel would be a nicer user experience. Adding a `slide` variable keeps track of which slide the user is on. `nextSlide` and `prevSlide` are two simple functions to move forward and backward through the slides. 

```
nextSlide() {
    this.slide = this.slide + 1;
    if (this.slide > this.slideCount) {
        this.slide = 1;
    }
}

prevSlide() {
    this.slide = this.slide - 1;
    if (this.slide < 1) {
        this.slide = this.slideCount;
    }
}
```
Tying the functions to buttons on the card allows the user to carousel through the "learn more" content. It all comes together by wrapping a each card in a if statement that tests which slide to display.

```
@if( slide == 1 ) {
<div class="center-element w3-margin w3-animate-opacity">
    <div class="recipe-card" style="max-width: 800px; height:  1000px;">
        <div class="recipe-card-title recipe-card-title-position-1">
            create and edit recipes
        </div>
        <div class="recipe-card-body card" style="text-align: left;height: 250px;font-size: 2em">
            At mealHash, you can create and edit recipes. This includes areas for ingredients, 
            instructions, and other key points you'd expect as part of recipe.    
        </div>
        <div style="display: flex;justify-content: center;">
            <button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"><span class="bx bx-left-arrow" style="font-size: 2em;" ></span></button>
            <button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"><span class="bx bx-right-arrow" style="font-size: 2em;" ></span></button>
        </div>
    </div>
</div>
}

@if( slide == 2) {
    ...
}

...

@if ( slide == 5 ) {
<div class="center-element w3-margin w3-animate-opacity w3-animation-left">
    <div class="recipe-card" style="max-width: 800px;">
        <div class="recipe-card-title recipe-card-title-position-5">
            cost
        </div>
        <div class="recipe-card-body" style="text-align: left;height: 250px;font-size: 1.5em">
            mealhash is free and we intend to keep it that way. But will gladly accept donations to keep the site running. In future, 
            we may build out premium features as we have more mealhasher feedback. But our goal is to make mealhash a must have site
            for your recipes, grocery lists, and meal plans.
        </div>
        <div style="display: flex;justify-content: center;">
            <button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"><span class="bx bx-left-arrow" style="font-size: 2em;" ></span></button>
            <button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top" style="height: 60px;align-self: center;"><span class="bx bx-right-arrow" style="font-size: 2em;" ></span></button>
        </div>
    </div>
</div>
}
```
Now this gets us a carousel and a better user experience. But the developer experience has now become a nightmare. Imagine if a change was needed on the cards such as styling or additional text. Each card would need to be updated which means 5 opportunities to introducing a bug into the page. 

The key here is that duplicate code is an opportunity for abstraction and ask: What parts of the code can be condensed and / or reused and how could the non-duplicated parts (generally data) be implemented. 

The solution in this instance was really simple: moved the card titles and text into JSON.

```
    public cards: Array<any> = [
        {   title:  'create and edit recipes',
            text:   `   At mealHash, you can create and edit recipes. This includes areas for ingredients, instructions, and other key points 
                        you would expect as part of recipe. 
                    `,
            index: 1
        },
        {   title:  'stores',
            text:   `   You can also add stores into your mealHash, create grocery lists and easily add and remove items from the list. 
                        Ingredients can be added to directly to your grocery  lists from your recipes allowing you manage your shopping experience.
                    `,
            index: 2
        },
        {   title:  'search',
            text:   `   Use the search page to find recipes by name or that have a particular ingredient. When you find a recipe you want to try, to can copy
                        it right to your recipe binder and then edit it to make it your own.  
                    `,
            index: 3
        },
        {   title:  'recipe feed',
            text:   `   A feed is available of your and others recipes as they are added to mealhash. Just like the search, you can add a recipe to your 
                        mealhash and modify it to make it your own.
                    `,
            index: 4
        },
        {   title: 'cost',
            text:  `    mealhash is free and we intend to keep it that way. But will gladly accept donations to keep the site running. In future, 
                        we may build out premium features as we have more mealhasher feedback. But our goal is to make mealhash a must have site
                        for your recipes, grocery lists, and meal plans.
                    `,
            index: 5
        }
        
    ]
```
Title and text are obvious as they refer the `recipe-card-title` area of the card, and the text is in the `recipe-card-body`. Index is used as a way to track the cards. The template can be condensed to one card. A `@for` is used to iterate over the JSON array based on one of the card blocks:

```
@for( card of cards; track card.index) {
    @if( slide == card.index) {
        <div class="center-element w3-margin w3-animate-opacity">
            <div class="card" style="max-width: 800px; height:  400px;">
                <div class="w3-padding card-title">
                    {{card.title}}
                </div>
                <div class="w3-padding" style="text-align: left;height: 250px;font-size: 1.8em">{{card.text}}</div>
            </div>
        </div>
        <div style="display: flex;justify-content: center;gap: 20px">
            <button (click)="prevSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"><span class="bx bx-left-arrow" style="font-size: 2em;" ></span></button>
            <button (click)="nextSlide()" class="w3-circle w3-hover-black w3-padding w3-margin-top w3-card" style="height: 60px;align-self: center;"><span class="bx bx-right-arrow" style="font-size: 2em;" ></span></button>
        </div>
    }
}
```
If a card needs to be changed, it only has to be done one time. All the data elements relevant to the card are pulled from the JSON array which makes updating the "learn more" section much easier. Updating the "learn more" page just means updating the array. This still means going through opening the project, making the mods, testing and deploying...but with a bit more abstraction, that process can be made much smoother. Stay tuned for part 2.

If you got this far, thanks for reading. I don't post that often so please be nice - but I do welcome construction feedback. 

Cheers!