DEV Community

Cover image for Coding the design: Angular & user decision making flow
Jodamco
Jodamco

Posted on

Coding the design: Angular & user decision making flow

I really like design, both visual and usefull. I am a human being driven by beauty and I think that combining it with our daily lives and tasks is what differentiates greatness and uniqueness from pure raw purpose.

There's also beauty in seamless

The effort to make something so usefull and integrated to one's life that it goes unnoticed even when life can't be imagined without it. To achieve this outcome, countless designers spend their time studying both product and user to simplify decision making processes and create balance into complex-simple user flows.

That said, if you are not a designer that's mostly not for you to care, but often we face situations where we are the ones deciding the user flow on front end applications and taking care of the decision making process. That's not easy, but there's theory and that's what I want to tell you about.

Seven Stages of Action

Donald Norman showed us in the past decades that the ones decision making process has 7 steps he called 'Seven stages of action'. Those are:

  • Forming the goal (What do I want?)
  • Forming the intention (How can I get it?)
  • Specifying an action (If I do 'x' then I can get it)
  • Executing the action (Do 'x')
  • Perceiving the state of the world (What happened after my actions?)
  • Interpreting the state of the world (What's the meaning of what happened?)
  • Evaluating the outcome (Is it what I wanted?)

These steps also come with the cycle of action:

Cycle of action by David Norman

Making it symple, whenever the user wants to do something it'll follow the same cycle of

  1. evaluate the world
  2. decide what is wanted and
  3. act on it to achieve what is wanted

The best applications we use on our daily basis have mastered techniques to diminish the cognitive load on identifying the current state and deciding what to do to achieve an outcome. This is a job itself and requires experience and research, but I came out with a simple rule of thumb to achieve great results even in small systems

Let your user know what is going on

Quite dumb right? I'm gonna demonstrate what I mean, so let's write a few lines of code

Coding user interaction

Regardless of the techonology, we all run some sort of state management to controll user interaction, display of data and feature workflow. State is about what's going on now, the present of the application, the data it has and what's being displayed on the screen. If we want to let the user know what is going on then we need to be clear about the component state. Usually, a simple component with data will have something between 3 to 5 states:

  • idle or initial
  • empty
  • loading
  • error
  • data

Let's code each one of them. Consider a simple Angular component

@Component({
    selector: 'app-dummy-component',
    standalone: true,
    template: `<ul> 
                       <li *ngFor="let item of list">
                           {{item.name}}
                       </li>
                   </ul>

                   <button (click)="loadList()" >
                       Load Items
                   </button>`,
})
export class DummyComponent {
    public list: any[] = []
    constructor() {}
    public loadList(){ ... }
}

Enter fullscreen mode Exit fullscreen mode

This would be the idle/initial state and this also happens to be the data state since whenever data is available it will appear. To display a list of items can be as simple as this but at the same time it is a good practice to consider

  • what happens if the list is empty?
  • what happens if I am not able to load the list?
  • how can the user know if the load is still going on?

These questions try to fill the gaps in the decision making process of the user since whenever the cycle of action starts the component has to follow up to new states giving feedback on the actions taken by the user. Let's add some states to our component

@Component({
    selector: 'app-dummy-component',
    standalone: true,
    template: `<ul *ngIf="list.length > 0; else listPlaceholder> 
                       <li *ngFor="let item of list">
                           {{item.name}}
                       </li>
                   </ul>

                   <ngTemplate #listPlaceholder>
                       <h4> You have no items to display </h4>
                   </ngTemplate>

                   <button (click)="loadList()" >
                       <p *ngIf="isLoading; else loadPlaceholder>
                           Load Items
                       </p>
                       <ngTemplate #loadPlaceholder>
                           ... Loading items ...
                       </ngTemplate>
                   </button>`,
})
export class DummyComponent {
    public list: any[] = []
    public isLoading: boolean = false
    constructor() {}
    public loadList(){ 
        if(this.isLoading) return
        this.isLoading = true
        ... // load the list
        this.isLoading = false
    }
}

Enter fullscreen mode Exit fullscreen mode

Our updated component is now capable of giving feedback on two new occasions:

  • Empty state: now we are able to let the user know if the list is empty. This brings clearence to what is being displayed since now we're able to differentiate an empty list from a failure of the system
  • Loading state: displaying any kind of loader gives instant feedback for the users regarding their actions. Whenever the user acts on a screen, something must change and by having the loading feedback the user will be able perceive a change to trigger a new cycle of action.

Let's add some error handling to our simple component list:

@Component({
    selector: 'app-dummy-component',
    standalone: true,
    template: `<ul *ngIf="!error && list.length > 0; else listPlaceholder> 
                       <li *ngFor="let item of list">
                           {{item.name}}
                       </li>
                   </ul>

                   <ngTemplate #listPlaceholder>
                       <h4> You have no items to display </h4>
                   </ngTemplate>

                   <h4 *ngIf="error">{{error}}</h4>

                   <button (click)="loadList()" >
                       <p *ngIf="isLoading; else loadPlaceholder>
                           Load Items
                       </p>
                       <ngTemplate #loadPlaceholder>
                           ... Loading items ...
                       </ngTemplate>
                   </button>`,
})
export class DummyComponent {
    public list: any[] = []
    public isLoading: boolean = false
    public error: String|undefined = undefined
    constructor() {}
    public loadList(){
        try{
            if(this.isLoading) return
            this.isLoading = true
            ... // load the list
            this.isLoading = false
        }catch(error){
            this.error = 'Error while loading the list'
        } 
    }
}

Enter fullscreen mode Exit fullscreen mode

Now we are also able to handle the error state on our component.

By caring for a component state and cycle of action we end up increasing it's complexity since we will need new variables and conditions to handle all the things properly, but by doing it we also achieve a more stable and robust component that's able to handle different situations accordingly. Also, the component will be able to fully respond to the user's interactions providing feedback in a fluid cycle of action.

That's mostly it. Keep in mind that not all components will have all states though. Don't go for the number of states but for the clarity and always try to let your user know what is going on. The more you care with it in your smaller components the more you whole app will be able to cover the gaps on decisory process and become seamless into your user life.

Top comments (0)