Items in array not updating in UI

I’ve setup a StackPanel with Items coming from a javascript array. There is a TextInput on top where new items can be added. This works fine: when adding an item, the view also updates.

The issue is where I want to update a title from a single item in the list. For the method addTag() I would expect the item title to update to ‘New Title’ if the button within that item is clicked. The array on the javascript part seems to be updating but not the view.

My javascript is as following:

DealNotifier.js

class DealNotifier {
    constructor() {
        this.title = '';
        this.items = [];
    }

    addItem(e) {
        this.items.push({
            title: this.title,
            tags: []
        });
        this.title = '';
    }

    addTag(props) {
        this.items = this.items.map((item, index) => {
            if (index == props.index) {
                console.log('setting item'); // this actually logs to the console
                item.title = 'New Title';
            }
            return item
        });
    }
}

And this is the UX:

<App Model="DealNotifier">
    <ClientPanel Background="#EEF0F2">
        <DockPanel>
            <DockPanel Dock="Top">
                <TextInput Value={title} PlaceholderText="Give a title.." Background="#ccc" />
                <Button Text="Add watcher" Dock="Right" Tapped={addItem} />
            </DockPanel>
            <ScrollView>
                <StackPanel>
                    <Each Items={items}>
                        <StackPanel>
                            <Rectangle CornerRadius="10" Color="#EEC643" Margin="2,5,2,0" Padding="10, 20">
                                <StackPanel>
                                    <Text Value={title} FontSize="15" TextColor="#fff" />
                                    <!-- tags -->
                                    <StackPanel Orientation="Horizontal">
                                        <Each Items={tags}>
                                            <Text Value={} />
                                        </Each>
                                    </StackPanel>
                                    <DockPanel>
                                        <TextInput PlaceholderText="Add some tags.." />
                                        <Button Text="Add" Clicked={addTag} Dock="Right" Color="#000" />
                                    </DockPanel>
                                    <!-- /tags -->
                                </StackPanel>
                            </Rectangle>
                        </StackPanel>
                    </Each>
                </StackPanel>
            </ScrollView>
        </DockPanel>
    </ClientPanel>
</App>

Hi,

The issue is that we currently only listen for when you call a method of an object, not when you change the value of a property. So this.title = 'New title'; will not trigger change detection.

This is unfortunate, and will be fixed in an upcoming release. But for now, I’d recommend making a class to represent each item with methods to update fields on that object. Something like this:

class Item {
    constructor(title) {
        this.title = title;
        this.tags = [];
    }

    setTitle(newTitle) {
        this.title = newTitle;
    }

    addTag() {
        this.tags.push("My new tag");
    }
}

Then you can rewrite DealNotifier to something like this:

export default class DealNotifier {
    constructor() {
        this.title = '';
        this.items = [];
    }

    addItem(e) {
        this.items.push(new Item(this.title));
        this.title = '';
    }
}

Thanks Sebastian. How can I update an attribute then for a particular object in an array?

For example I have an array of user objects with some properties like name, email etc. For only one user in the array I want to update the name. Can you give an example on how to do that? I was using .map in my previous example but that is indeed changing the property directly.

.map is completely fine and is not the source of the issue. The problem is that you have to change the value of the property from inside a method on the object.

I’m guessing you are getting this list of users from a server using something like fetch?

Sebastian Reinhard wrote:

I’m guessing you are getting this list of users from a server using something like fetch?

No this was actually just an example.

Okay, great :slight_smile: In any case, you would follow the same pattern.
Create a class for your object with methods (member functions) that update the actual properties:

class User {
    constructor(name, email) {
        this.name = name;
        this.email = email;
    }

    setName(name) { this.name = name; }
    setEmail(email) { this.email = email; }
}

Then you call setName instead of setting the name property directly.
It does not matter if the user object is inside an array, or how you get a hold of it, only that you update the data it holds using a method on that class.

class MyApp {
    constructor() {
        this.users = [
            new User("John", "john@doe.com"),
            new User("Jane", "jane@doe.com")
        ];
    }

    someExamples() {
        // These are just examples:

        // Get the first user, and set their name to "Jack"
        this.users[0].setName("Jack");

        // Find the user who has the email "jane@doe.com", and set their name to "Jane"
        this.users
            .find(user => user.email == "jane@doe.com")
            .setName("Jane");
    }
}

Makes total sense! Thanks for the extensive answers!