Is there a substitute for AngularJS $watch in Aurelia?

I'm in the process of transitioning my existing Angular.js project to Aurelia.js. Here is an example of what I am trying to accomplish:

report.js

export class Report {
       list = [];

       //TODO
       listChanged(newList, oldList){
             enter code here
       }
}

report.html

<template>
    <require from="component"></require>
    <component list.bind="list"></component>
</template>

My question is: How can I detect when the list changes?

In Angular.js, I would typically do:

$scope.$watchCollection('list', (newVal, oldVal)=>{ my code });

Is there something similar in Aurelia?

Answer №1

If you are using @bindable fields, the listChanged(newValue, oldValue) function will be triggered every time the value of the list is updated. You can find more information in the Aurelia documentation.

@customAttribute('if')
@templateController
export class If {
  constructor(viewFactory, viewSlot){
    //
  }

  valueChanged(newValue, oldValue){
    //
  }
}

Another option is to utilize ObserveLocator as explained in a blog post by an Aurelia author here:

import {ObserverLocator} from 'aurelia-binding';  // or 'aurelia-framework'

@inject(ObserverLocator)
class Foo {  
  constructor(observerLocator) {
    this.bar = 'baz';

    var subscription = this.observerLocator
      .getObserver(this, 'bar')
      .subscribe(this.onChange);
  }

  onChange(newValue, oldValue) {
    alert(`bar changed from ${oldValue} to ${newValue}`);
  }
}

UPDATE

In a response on this question provided by Jeremy Danyow:

The ObserverLocator is Aurelia's internal low-level API. There is now a public API for the binding engine that could be used:

import {BindingEngine} from 'aurelia-binding'; // or from 'aurelia-framework'

@inject(BindingEngine)
export class ViewModel {
  constructor(bindingEngine) {
    this.obj = { foo: 'bar' };

    let subscription = bindingEngine.propertyObserver(this.obj, 'foo')
      .subscribe((newValue, oldValue) => console.log(newValue));

    subscription.dispose();
  }
}

Warm regards, Alexander

Answer №2

Just a small adjustment is needed to make your original code work:

report.js

import {bindable} from 'aurelia-framework'; // or 'aurelia-binding'

export class Report {
       @bindable list;  // add the bindable decorator to the list property

       // This method will be automatically called by Aurelia
       listChanged(newList, oldList){
             enter code here
       }
}

report.html

<template>
    <require from="component"></require>
    <component list.bind="list"></component>
</template>

Aurelia follows a convention where it looks for a [propertyName]Changed method in your viewmodel and triggers it automatically. This convention applies to all properties decorated with @bindable. For more information, visit this link

Answer №3

The ideal solution for the current scenario appears to be utilizing CustomEvent

Therefore, a complete solution would resemble this:

report.html

<template>
    <require from="component"></require>
    <component list.bind="list" change.trigger="listChanged($event)"></component>
</template>

component.js

@inject(Element)
export class ComponentCustomElement {
    @bindable list = [];

    //TODO invoke when you change the list
    listArrayChanged() {
        let e = new CustomEvent('change', {
            detail: this.lis
        });

        this.element.dispatchEvent(e);
    }
}

It is necessary to modify the component element by adding a trigger function that sends out a change event. It is assumed that the component will be aware of any changes in the list.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Mastering the art of customizing classes and styles in material-ui (React)

I am facing a challenge with version 1.2.1 of material-ui. My goal is to make the AppBar component transparent by overriding its styles as per the documentation here. This is the code snippet I have been working on: import React, { Component } from ' ...

How come the item I just inserted into a JavaScript array is showing up as undefined when I try to retrieve it immediately after adding it?

Apologies for the messy code, but I'm facing an issue with my JavaScript. I can't figure out why the specified child is not considered as a task to derive from: var childrenToOperateOn = []; for (var i = 0; i < $scope.der ...

Occasions focused on the <input type="file"> feature

Looking for a way to write a file input in React that accepts CSV files, validates them, and prevents upload if there are errors? Check out the code snippet below: <CustomInput type="file" id="fileBrowser" name="file" label={filename || 'Choos ...

The value of request.url in NodeJS is consistently set to "/"

As a beginner in learning NodeJS, I am curious about how to determine the URL of a request in order to execute different functions. However, every time I use request.url, it seems to only return "/". My approach involves using NodeJS without additional m ...

Determine with jQuery whether the img src attribute is null

My HTML structure is as follows: <div class="previewWrapper" id="thumbPreview3"> <div class="previewContainer"> <img src="" class="photoPreview" data-width="" data-height=""><span>3</span> </div> </div> ...

Generate a dynamic vertical line that glides smoothly over the webpage, gradually shifting from one end to the other within a set duration using javascript

I have designed a calendar timeline using html. My goal is to implement a vertical line that moves from left to right as time progresses, overlaying all other html elements. This functionality is similar to Google Calendar's way of indicating the curr ...

Error: Attempting to access a property of an undefined value (referencing '8') was unsuccessful

Hello everyone, I am new to posting and identify as a junior Frontend developer. I'm encountering a confusing issue with the InputLabel from Material UI (ver. 5) on a specific section of the website I'm working on. On the homepage, I successfully ...

Using a Javascript plugin in Laravel with Vue by importing it into the project

Currently, I am in the process of creating a Vue component by utilizing the functionalities provided by the JavaScript plugin known as Cropper JS. The application is developed using Laravel 5.6. Initially, I installed Cropper JS via NPM: npm install cropp ...

Why am I seeing back-end console errors that are related to my front-end?

Recently, I encountered an error message that prevents me from using 'import' in my front end code when trying to execute 'node index'. This issue never occurred before, and it only arose when I returned to this project. In my backend ...

Upon the initial loading of the React component, I am retrieving undefined values that are being passed from the reducer

Upon the initial loading of the React component, I am encountering an issue where the values originating from the reducer are showing up as undefined. Below is a snippet of my code: const [componentState, dispatchComponentState] = useReducer( versionReduc ...

The iframe came to a halt as soon as it was no

I am currently developing a live video website that utilizes third-party tools to play the videos. To simplify things, I have embedded all the components required for live video into a single HTML page. Here is how it looks: <iframe data-v-140cfad2= ...

Unable to successfully link filter outcomes with input in AngularJS

Here is the code snippet I am working with: <body ng-app=""> <div ng-init="friends = [{name:'John', phone:'555-1276'}, {name:'Mary', phone:'800-BIG-MARY'}, {nam ...

Mastering the art of effectively capturing and incorporating error data

Is there a way to retain and add information to an Error object in typescript/javascript without losing the existing details? Currently, I handle it like this: try { // code that may throw an error } catch (e) { throw new Error(`Error while process ...

Tips for resolving dependency conflicts in a JavaScript project starter template

Currently, I'm utilizing the Airframe React template and the procedure seems quite simple: Extract the files and execute npm install in the project directory. However, an issue arises when running npm install as follows: npm WARN config global `--glob ...

Enhancing JavaScript form validation techniques

I am currently working on a user profile form that includes 15 text fields, dropdown menus, and a textarea. Users can input information into these fields, and upon saving the form, not all fields are required to be filled out. However, I need to validate a ...

Get the application/pdf document that was just sent to you from the software backend

I am facing an issue with downloading a PDF file sent from the backend. Upon receiving a blob response, I notice that when I download and view the file, the sheets are empty, matching the expected number of sheets. Could this be a coding problem? Current ...

How can I update an image source using JavaScript in a Django project?

Is it possible to dynamically change the image src using onclick without relying on hard paths or Django template tags? I have concerns that this may not be best practice. How can I implement a method to inject/change the ""{% static 'indv_proj&b ...

ReactJS - Element not specified

I'm experiencing a specific issue with the component below related to the changeColor() function, which is triggering an error message: TypeError: Cannot set property 'color' of undefined This error seems to be isolated within this compo ...

How come the state update result is triggering two different responses?

I recently developed a basic TicTacToe game using react-hooks. At times, I notice that the result is displayed before the state update on the screen. Other times, the result shows up after the update is visible on the screen. I'm puzzled by this ...

Using Three.js to create smooth rotations for objects

I have a logo in .obj format that I am loading onto a canvas using three.js. The logo is an integral part of the website's loading section that I am currently developing. Within this section, there is a 'Click to Enter' button. My goal is fo ...