# Two tracked variables not updating

**URL:** https://meta.discourse.org/t/two-tracked-variables-not-updating/345332
**Category:** Development
**Created:** [January 2, 2025, 11:18pm UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332 "2025-01-02T23:18:03Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [January 2, 2025, 11:18pm UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/1 "2025-01-02T23:18:03Z")

</div>

I have the following code:

```js
import Component from "@glimmer/component";
import { apiInitializer } from "discourse/lib/api";
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from "@ember/service";
import { defaultHomepage } from "discourse/lib/utilities";

/*
const xhr = new XMLHttpRequest();
xhr.open("GET", "/cakeday/anniversaries/today.json");
xhr.send();
xhr.responseType = "json";
xhr.onload = () => {
    if (xhr.readyState == 4 && xhr.status == 200) {
        let resp = xhr.response;
        let numberOfAnns = resp['total_rows_anniversaires'];
        let allAnns = resp['anniversaries']; // Is a list of dicts
        console.log(allAnns);
        let allAnnsUsernames = [];
        for (var annUserdata in allAnns) {
            console.log(allAnns[annUserdata]['username']);
            allAnnsUsernames.push(allAnns[annUserdata]['username']);
        }
        var annsOfData = {'num_anns': numberOfAnns, 'anns_users': allAnnsUsernames};
        
    }
};
return annsOfData;
*/

export default apiInitializer("1.14.0", (api) => {
    //const banner_location = settings.banner_location
    api.renderInOutlet(
        settings.banner_location,
        class BdaysAnnsBanner extends Component {
            @tracked annsDataFinal = null;
            @tracked bdaysDataFinal = null;
            @tracked areBothBannersVisible = true;
            @tracked isAnnsVisible = null;
            @tracked isBdaysVisible = null;

            @service router;

            constructor() {
                super(...arguments);
                this.fetchAnnsData(); // Automatically fetch on initialization
                this.fetchBdaysData();
            }
        
            // Asynchronously fetch the data and update tracked property
            @action
            async fetchAnnsData() {
                const response = await fetch("/cakeday/anniversaries/today.json");
                const json = await response.json();
        
                let numberOfAnns = parseInt(json['total_rows_anniversaries']);
                let allAnns = json['anniversaries']; // Is a list of dicts
                let allAnnsUsernames = [];
        
                for (let annUserdata of allAnns) {
                    allAnnsUsernames.push(annUserdata['username']);
                }
        
                this.annsDataFinal = {'num_anns': numberOfAnns, 'anns_users': allAnnsUsernames, 'visible': true, 'isFilled': true};
                this.updateBothBannersVisibility(this.annsDataFinal);
            }

            // Asynchronously fetch the data and update tracked property
            @action
            async fetchBdaysData() {
                 // Declare bdaysDataFinal here
                let bdaysDataFinal;
            
                // Fetch birthdays data
                const response = await fetch("/cakeday/birthdays/today.json");
                const json = await response.json();
            
                // Run the logic to process the data
                let numberOfBdays = parseInt(json['total_rows_birthdays']);
                let allBdays = json['birthdays']; // Is a list of dicts
                let allBdaysUsernames = [];
            
                for (let bdayUserdata of allBdays) {
                    allBdaysUsernames.push(bdayUserdata['username']);
                }
            
                this.bdaysDataFinal = {'num_bdays': numberOfBdays, 'bdays_users': allBdaysUsernames, 'visible': true, 'isFilled': true};
                this.updateBothBannersVisibility(this.bdaysDataFinal);
                //console.log(annsDataFinal); // Just to verify the result
            }

            @action
            updateBothBannersVisibility(bannerData) {
                console.log(bannerData.num_anns);
                // Check if it's anns or bdays
                if (bannerData.num_anns) { // It's anns
                    console.log('Anns:')
                    console.log(bannerData.num_anns);
                    if (bannerData.num_anns == 0 && settings.hide_unused_data) {
                        console.log(`Anns+setting: ${bannerData.num_anns == 0 && settings.hide_unused_data}`);
                        this.isAnnsVisible = false;
                        console.log(`isAnnsV: ${this.isAnnsVisible}`);
                        //console.log(this.isBdaysVisible);
                    }
                } else { // It's bdays  
                    console.log(bannerData.num_bdays);
                    if (bannerData.num_bdays == 0 && settings.hide_unused_data) {
                        console.log(`Bdays+setting: ${bannerData.num_bdays == 0 && settings.hide_unused_data}`);
                        this.isBdaysVisible = false;
                        console.log(`isBdaysV: ${this.isBdaysVisible}`);
                        //console.log(this.isAnnsVisible);
                    }
                }
                console.log(`isAnnsV: ${this.isAnnsVisible}`);
                console.log(`isBdaysV: ${this.isBdaysVisible}`);
                // Uses an inequality. If not the same (true), banner is shown. If it is the same, inequality is not satisfied, and the banner will be hidden.
                this.areBothBannersVisible = !(this.isAnnsVisible === false && this.isBdaysVisible === false); // Or: this.areBothBannersVisible = this.isAnnsVisible || this.isBdaysVisible;
                console.log(this.areBothBannersVisible);
            }

            
            // Getter for the data
            get annsData() {
                //return this.annsDataFinal;
                if (this.annsDataFinal !== null) {
                    if (this.annsDataFinal.num_anns == 0) {
                        if (settings.hide_unused_data) {
                            this.annsDataFinal.isFilled = false;
                            this.annsDataFinal.visible = false;
                        } else {
                            this.annsDataFinal.isFilled = false;
                        }
                    } else {
                        this.annsDataFinal.isFilled = true;
                        this.annsDataFinal.visible = true;
                    }
                    
                    //this.updateBothBannersVisibility(this.annsDataFinal);
                    // If the data is not loaded yet, return null or any default value
                    return this.annsDataFinal;
                }
            }
        
            // Getter for the data
            get bdaysData() {
                //return this.bdaysDataFinal;
                if (this.bdaysDataFinal !== null) {
                    if (this.bdaysDataFinal.num_bdays == 0) {
                        if (settings.hide_unused_data) {
                            this.bdaysDataFinal.isFilled = false;
                            this.bdaysDataFinal.visible = false;
                        } else {
                            this.bdaysDataFinal.isFilled = false;
                            this.bdaysDataFinal.visible = true;
                        }
    
                    } else {
                        this.bdaysDataFinal.isFilled = true;
                        this.bdaysDataFinal.visible = true;
                    }

                    //this.updateBothBannersVisibility(this.bdaysDataFinal);
                    // If the data is not loaded yet, return null or any default value
                    return this.bdaysDataFinal;
                }
            }

            get isHomepage() {
                const { currentRouteName } = this.router;
                return currentRouteName === `discovery.${defaultHomepage()}`;
            }
            //console.log(this.areBothBannersVisible);

            <template>
                {{#if this.areBothBannersVisible}}
                    {{#if this.isHomepage}}
                        <div class='bdaysannsbanner' id='bdaysannsbanner'>
                            {{#if this.annsData.visible}}
                                <div class='anns'>
                                    {{#if this.annsData.isFilled}}
                                        <p>{{this.annsData.num_anns}} users are celebrating their anniversary!</p>
                                        <!-- Display the anniversaries data -->
                                        {{#each this.annsData.anns_users as |username|}}
                                            <span><a class='mention'>{{username}}</a></span>
                                        {{/each}}
                                    {{else}}
                                        <p>No one has their anniversary today!</p>
                                    {{/if}}
                                </div>
                            {{/if}}
                            <br />
                            {{#if this.bdaysData.visible}}
                                <div class='bdays'>
                                    {{#if this.bdaysData.isFilled}}
                                        <p>{{this.bdaysData.num_bdays}} users are celebrating their birthday!</p>
                                        <!-- Display the birthday data -->
                                        {{#each this.bdaysData.bdays_users as |username|}}
                                            <span><a class='mention'>{{username}}</a></span>
                                        {{/each}}
                                    {{else}}
                                        <p>No one is celebrating their birthday today!</p>
                                    {{/if}}
                                </div>
                            {{/if}}
                        </div>
                    {{/if}}
                {{/if}}
            </template>
        }
    );
});

```

The part that isn’t working are the `this.isAnnsVisible` and `this.isBdaysVisible` variables. They don’t seem to be updating, causing the banner to be shown.

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [January 8, 2025, 7:27am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/2 "2025-01-08T07:27:35Z")

</div>

It’s odd that these variables aren’t updating. Could it be that they are not being used correctly?

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [January 31, 2025, 10:13am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/3 "2025-01-31T10:13:07Z")

</div>

I’m sorry to bump this, but I still have no idea why this is happening.  
My code now:

```gjs
import Component from "@glimmer/component";
import { apiInitializer } from "discourse/lib/api";
import { ajax } from "discourse/lib/ajax";
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from "@ember/service";
import { defaultHomepage } from "discourse/lib/utilities";
import { getOwner } from '@ember/application';

export default apiInitializer("1.14.0", (api) => {
    //const banner_location = settings.banner_location
    
    api.renderInOutlet(
        settings.banner_location,
        class BdaysAnnsBanner extends Component {
            @tracked annsDataFinal = null;
            @tracked bdaysDataFinal = null;
            @tracked areBothBannersVisible = true;
            @tracked isAnnsVisible = null;
            @tracked isBdaysVisible = null;

            @service router;

            constructor() {
                super(...arguments);
                this.fetchAnnsData(); // Automatically fetch on initialization
                this.fetchBdaysData();
            }
        
            // Asynchronously fetch the data and update tracked property
            @action
            async fetchAnnsData() {
                const response = await fetch("/cakeday/anniversaries/today.json");
                const json = await response.json();
                console.log(json);
                let numberOfAnns = parseInt(json['total_rows_anniversaries']);
                let allAnns = json['anniversaries']; // Is a list of dicts
                let allAnnsUsernames = [];
        
                for (let annUserdata of allAnns) {
                    allAnnsUsernames.push(annUserdata['username']);
                }
        
                this.annsDataFinal = {'num_anns': numberOfAnns, 'anns_users': allAnnsUsernames, 'visible': true, 'isFilled': true};
                this.updateBothBannersVisibility(this.annsDataFinal);
            }

            // Asynchronously fetch the data and update tracked property
            @action
            async fetchBdaysData() {
                 // Declare bdaysDataFinal here
                let bdaysDataFinal;
            
                // Fetch birthdays data
                const response = await fetch("/cakeday/birthdays/today.json");
                const json = await response.json();
                
                // Run the logic to process the data
                let numberOfBdays = parseInt(json['total_rows_birthdays']);
                let allBdays = json['birthdays']; // Is a list of dicts
                let allBdaysUsernames = [];
            
                for (let bdayUserdata of allBdays) {
                    allBdaysUsernames.push(bdayUserdata['username']);
                }
            
                this.bdaysDataFinal = {'num_bdays': numberOfBdays, 'bdays_users': allBdaysUsernames, 'visible': true, 'isFilled': true};
                this.updateBothBannersVisibility(this.bdaysDataFinal);
                //console.log(annsDataFinal); // Just to verify the result
            }

            @action
            updateBothBannersVisibility(bannerData) {
                console.log(bannerData.num_anns);
                // Check if it's anns or bdays
                if (bannerData.num_anns) { // It's anns
                    console.log('Anns:')
                    console.log(bannerData.num_anns);
                    if (bannerData.num_anns == 0 && settings.hide_unused_data) {
                        console.log(`Anns+setting: ${bannerData.num_anns == 0 && settings.hide_unused_data}`);
                        this.isAnnsVisible = false;
                        console.log(`isAnnsV: ${this.isAnnsVisible}`);
                        //console.log(this.isBdaysVisible);
                    }
                } else { // It's bdays  
                    console.log(bannerData.num_bdays);
                    if (bannerData.num_bdays == 0 && settings.hide_unused_data) {
                        console.log(`Bdays+setting: ${bannerData.num_bdays == 0 && settings.hide_unused_data}`);
                        this.isBdaysVisible = false;
                        console.log(`isBdaysV: ${this.isBdaysVisible}`);
                        //console.log(this.isAnnsVisible);
                    }
                }
                console.log(`isAnnsV: ${this.isAnnsVisible}`);
                console.log(`isBdaysV: ${this.isBdaysVisible}`);
                // Uses an inequality. If not the same (true), banner is shown. If it is the same, inequality is not satisfied, and the banner will be hidden.
                this.areBothBannersVisible = !(this.isAnnsVisible === false && this.isBdaysVisible === false); // Or: this.areBothBannersVisible = this.isAnnsVisible || this.isBdaysVisible;
                console.log(this.areBothBannersVisible);
            }

            
            // Getter for the data
            get annsData() {
                //return this.annsDataFinal;
                if (this.annsDataFinal !== null) {
                    if (this.annsDataFinal.num_anns == 0) {
                        if (settings.hide_unused_data) {
                            this.annsDataFinal.isFilled = false;
                            this.annsDataFinal.visible = false;
                            this.isAnnsVisible = false;
                        } else {
                            this.annsDataFinal.isFilled = false;
                        }
                    } else {
                        this.annsDataFinal.isFilled = true;
                        this.annsDataFinal.visible = true;
                    }
                    
                    //this.updateBothBannersVisibility(this.annsDataFinal);
                    // If the data is not loaded yet, return null or any default value
                    return this.annsDataFinal;
                }
            }
            
            // Getter for the data
            get bdaysData() {
                //return this.bdaysDataFinal;
                if (this.bdaysDataFinal !== null) {
                    if (this.bdaysDataFinal.num_bdays == 0) {
                        if (settings.hide_unused_data) {
                            this.bdaysDataFinal.isFilled = false;
                            this.bdaysDataFinal.visible = false;
                            this.isBdaysVisible = false;
                            console.log(`this.isAnnsVisible: ${this.isAnnsVisible}`);
                            console.log(`this.isBdaysVisible: ${this.isBdaysVisible}`);
                        } else {
                            this.bdaysDataFinal.isFilled = false;
                            this.bdaysDataFinal.visible = true;
                        }
    
                    } else {
                        this.bdaysDataFinal.isFilled = true;
                        this.bdaysDataFinal.visible = true;
                    }

                    //this.updateBothBannersVisibility(this.bdaysDataFinal);
                    // If the data is not loaded yet, return null or any default value
                    return this.bdaysDataFinal;
                }
            }

            get isHomepage() {
                const { currentRouteName } = this.router;
                return currentRouteName === `discovery.${defaultHomepage()}`;
            }
            //console.log(this.areBothBannersVisible);
            
            <template>
                {{#if this.areBothBannersVisible}}
                    {{#if this.isHomepage}}
                        <div class='bdaysannsbanner' id='bdaysannsbanner'>
                            {{#if this.annsData.visible}}
                                <div class='anns'>
                                    {{#if this.annsData.isFilled}}
                                        <p>{{this.annsData.num_anns}} users are celebrating their anniversary!</p>
                                        <!-- Display the anniversaries data -->
                                        {{#each this.annsData.anns_users as |username|}}
                                            <span><a class='mention'>{{username}}</a></span>
                                        {{/each}}
                                    {{else}}
                                        <p>No one has their anniversary today!</p>
                                    {{/if}}
                                </div>
                            {{/if}}
                            <br />
                            {{#if this.bdaysData.visible}}
                                <div class='bdays'>
                                    {{#if this.bdaysData.isFilled}}
                                        <p>{{this.bdaysData.num_bdays}} users are celebrating their birthday!</p>
                                        <!-- Display the birthday data -->
                                        {{#each this.bdaysData.bdays_users as |username|}}
                                            <span><a class='mention'>{{username}}</a></span>
                                        {{/each}}
                                    {{else}}
                                        <p>No one is celebrating their birthday today!</p>
                                    {{/if}}
                                </div>
                            {{/if}}
                        </div>
                    {{/if}}
                {{/if}}
            </template>
        }
    );
});

```

My repo:  
[https://github.com/NateDhaliwal/Discourse-Birthdays-Anniversaries-Today](https://github.com/NateDhaliwal/Discourse-Birthdays-Anniversaries-Today)

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [January 31, 2025, 10:54am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/4 "2025-01-31T10:54:48Z")

</div>

I’m not exactly sure just yet but …

> [@NateDhaliwal](#):
>
> `const json = await response.json();`

this `await` is unnecessary.

the response should already be an object so not sure why you need the `.json()` etc. ? You can probably simplify this a lot

> [@NateDhaliwal](#):
>
> `this.updateBothBannersVisibility(this.annsDataFinal);`

you don’t need to pass this as the tracked variables are in scope throughout the Component, so just call it and refer to it in the action.

Some style tips:

- don’t smother your shared code with console.logs, that makes it harder to see the wood for the trees
- use two space indentation not 4 which is a discourse standard. See your template
- consider moving this into a dedicated .gjs file
- you can click on the numbers in github and get a shareable link which previews the code if you need.

advanced:

- set up Prettier and ESlint … the former for style and the latter for both style and it might help you catch some bugs.

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [February 3, 2025, 7:04am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/5 "2025-02-03T07:04:48Z")

</div>

> [@merefield](#):
>
> set up Prettier and ESlint … the former for style and the latter for both style and it might help you catch some bugs.

It’s not this, is it?

> [@Automatically lint and format code before commits](https://meta.discourse.org/t/automatically-lint-and-format-code-before-commits/132947):
>
> Discourse uses [lefthook](https://github.com/evilmartians/lefthook) for git hooks, and bin/lint as the main CLI entry point for running the same checks manually. If you are working in a local clone, install the hooks once: pnpm install pnpm lefthook install After that, staged files will be checked automatically on git commit. The main command: bin/lint Use bin/lint when you want to run the repo’s configured linters yourself instead of waiting for the pre-commit hook. Common examples: bin/lint bin/lint path/to/file.rb path/to/file.gj…

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [February 3, 2025, 8:34am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/6 "2025-02-03T08:34:00Z")

</div>

You don’t need to go that far but that’s an option.

It’s enough to do it manually at first.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [February 3, 2025, 9:11am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/7 "2025-02-03T09:11:21Z")

</div>

> [@NateDhaliwal](#):
>
> `{{#if this.annsData.visible}}`

This kind of thing probably doesn’t work.

I don’t think EmberJS will watch nested properties.

You should create, track and test properties at the same level.

So create a tracked property called say annsDataVisible, update its value and use that in your if statement.

---

<div class="post-metadata">

### Author: ![merefield](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/merefield/32/176214_2.png) [@merefield](https://meta.discourse.org/u/merefield)
#### Post date: [February 3, 2025, 9:48am UTC](https://meta.discourse.org/t/two-tracked-variables-not-updating/345332/8 "2025-02-03T09:48:36Z")

</div>

You can also reassign the entire object each time instead of just a value of a child attribute which might trigger a repaint.
