Aurelia: encountering a troublesome data binding problem with the main attribute of a custom element

Recently, I have been delving into Aurelia and encountering a data binding issue related to a custom attribute.

I've crafted a custom attribute named square (inspired by examples from the "Templating:Custom Attributes" guide on the Aurelia website):

square.ts:

import { bindable, autoinject } from "aurelia-framework";

@autoinject
export class SquareCustomAttribute {
  @bindable sideLength = "100px";
  @bindable color = "red";

  constructor(private element: Element) {
    this.sideLengthChanged(this.sideLength);
    this.colorChanged(this.color);
  }

  sideLengthChanged(newValue: string) {
    if (this.element instanceof HTMLElement) {
      this.element.style.width = this.element.style.height = newValue;
    }
  }

  colorChanged(newValue: string) {
    if (this.element instanceof HTMLElement) {
      this.element.style.backgroundColor = newValue;
    }
  }
}

I want to make this custom attribute usable without explicitly binding it, allowing it to default to predetermined values. For example, in this consuming view:

app.html:

<template>
  <require from="./square"></require>
  <div square></div>
</template>

The above code functions as intended, rendering a square with 100px sides and a red background.

However, complications arise when designating the color property of SquareCustomAttribute as the primary property using @bindable's parameters like so:

Updated square.ts:

import { bindable, autoinject } from "aurelia-framework";

@autoinject
export class SquareCustomAttribute {
  @bindable sideLength = "100px";
  @bindable({ primaryProperty: true }) color = "red";

  constructor(private element: Element) {
    this.sideLengthChanged(this.sideLength);
    this.colorChanged(this.color);
  }

  sideLengthChanged(newValue: string) {
    if (this.element instanceof HTMLElement) {
      this.element.style.width = this.element.style.height = newValue;
    }
  }

  colorChanged(newValue: string) {
    if (this.element instanceof HTMLElement) {
      this.element.style.backgroundColor = newValue;
    }
  }
}

Unexpectedly, setting color as the primary property triggers the colorChanged callback twice: first within the constructor with the default value, then again during some phase of initialization with an empty value.

How can this redundant invocation of the colorChanged callback be avoided to ensure the default value remains intact without explicit binding or supplying a value for the square attribute in the consuming view's HTML markup?

Answer №1

Approach this challenge from a different angle:

import { bindable, autoinject } from "aurelia-framework";

@autoinject
export class SquareCustomAttribute {
  @bindable sideLength;
  @bindable({ primaryProperty: true }) color;

  constructor(private element: Element) {
  }

  sideLengthChanged(newValue: string) {
    if (this.element instanceof HTMLElement) {
      this.element.style.width = this.element.style.height = newValue;
    }
  }
  
  bind(){
    this.sideLengthChanged(this.sideLength ? this.sideLength : "100px");
    this.colorChanged(this.color ? this.color : "red");
  }

  colorChanged(newValue: string) {
    if (this.element instanceof HTMLElement) {
      this.element.style.backgroundColor = newValue;
    }
  }
}

By specifying { primaryProperty: true }, you are instructing the framework to establish a binding on the value of your custom attribute. Whether it is provided or not, the framework will link it to your color property. Therefore, when you use <div square></div>, the color property in the bind() lifecycle will be an empty string by default. Since bind() is called only once, it is the ideal place to set default values for cases where they are initially empty.

See an example here:

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

typescript exploring the versatility of dynamic types and generics

Understanding TypeScript dynamic and generic types can be challenging for me. The goal is to create a function that generates an object with a specific type, where some properties of the object must match the parameters provided to the function. Essentia ...

Erase the destination pin on Google Maps

I am currently working on a project that involves displaying hit markers on google maps along with a route from start to finish. Although I have successfully displayed the route, I encountered an issue where both the origin and destination have identical ...

Is it possible to use the HostListener in Angular 7 to detect any scroll event occurring on a specific element within a webpage?

I am developing a breadcrumb bar component in Angular 7 that should dynamically hide and show based on user scrolling behavior. To achieve this, I created a directive to track the scroll position of the container element. While my code for capturing the s ...

The expect.objectContaining() function in Jest does not work properly when used in expect.toHaveBeenCalled()

Currently, I am working on writing a test to validate code that interacts with AWS DynamoDB using aws-sdk. Despite following a similar scenario outlined in the official documentation (https://jestjs.io/docs/en/expect#expectobjectcontainingobject), my asser ...

When using ReactJS, the modal body and footer may appear twice when loading data from an API

When trying to load data from a Laravel route using an Axios request in a modal, I am encountering the issue of the modal body and buttons showing twice inside the modal. Can anyone explain why this is happening? Below is the code snippet for reference. c ...

Can TypeScript be used to dynamically render elements with props?

After extensive research on SO and the wider web, I'm struggling to find a solution. I have devised two components, Link and Button. In short, these act as wrappers for <a> and <button> elements with additional features like chevrons on t ...

What is the best approach to testing the React Hook "useEffect" that is used to make an API call with Typescript?

Currently, I am working on writing Jest-enzyme tests for a basic React application using Typescript along with the new React hooks. The main issue I am facing is with properly simulating the api call made within the useEffect hook. Within the useEffect, ...

"My NodeJS code is returning a string instead of JSON data when using the MongoDB populate

My current project involves managing a product with multiple providers, each having its own price. The challenge I am facing is that when using populate() to retrieve provider information by ID, it returns the data as a string instead of JSON format. Below ...

A guide on specifying a generic type while exporting a component with generic type

Currently, my implementation of react components uses typescript. An issue I am encountering involves defining a generic type. Below is an excerpt showcasing the problem: interface Props<T> { classes: { [className in keyof typeof styles]: string } ...

Enhancing the API response with Typescript interfaces for complex nested objects

Just stepping into the world of Typescript and attempting to introduce types to a basic application. Struggling with an error related to a deeply nested object. export default function AnimalAdoptionCosts() { const [currencyQuote, setCurrencyQuote] = use ...

Attempting to build a table within a table structure

My goal is to create a nested table structure similar to this image: https://i.sstatic.net/v6lZo.png The number of months, topics, and arguments for each topic can vary as they are retrieved from a database. I have attempted to implement this functionali ...

The localStorage retrieval function is returning a null value for getItem

I successfully incorporated a theme switcher into my app, enabling users to toggle between different theme/variable.scss files using an on/off switch in the interface. However, I encountered an issue with storing and retrieving user preferences. While I m ...

When implementing 'useGlobalGuards' in NestJS, remember to exclude endpoints for enhanced security

After implementing the useGlobalGuards method in my main.ts file, all endpoints now utilize the AuthGuard. This guard triggers a 401 error if a valid auth token is not present in the request header. Previously, I used @UseGuards(AuthGuard) on individual cl ...

What is the best way to loop through a template literal union type in JavaScript?

Is there a way to iterate over the different values in a string union type created with template literals? type Foo = "Foo" | "Foo2" | "Foo3"; type Bar = "Bar" | "Bar2" | `${Foo}Type`; One common approach is to use a <const> assertion, like this: c ...

Enhance the functionality of angular-material buttons by incorporating dynamic loading animations into

I am currently working on a solution in Angular 12 to disable a button (and show a spinner) when it is clicked, until the API responds. To achieve this, I plan to follow a similar approach to the angular-material button implementation. Essentially, I want ...

Unable to locate any NativeScript modules for tns-core-module/ui

I'm working on a {N}-Application and facing an issue with importing Images from the tns-core-modules/ui/image module. Unfortunately, it seems that the target cannot be found within the tns-core-module. This is my code snippet: import * as ImageModul ...

ESLint is reminding you that the `parserOptions.project` setting must be configured to reference the tsconfig.json files specific to your

Within my NX Workspace, I am developing a NestJS-Angular project. Upon running nx lint, an error is triggered with the following message: Error: A lint rule requiring the TypeScript type-checker to be fully available has been attempted, but `parserOptions. ...

There is no valid injection token found for the parameter 'functions' in the class 'TodosComponent'

While working in my code, I decided to use 'firebase' instead of '@angular/fire'. However, I encountered an issue that displayed the following error message: No suitable injection token for parameter 'functions' of class &apos ...

Modify animation trigger when mouse hovers over

I am looking to create a feature where a slide overlay appears from the bottom of a thumbnail when the user hovers over it, and retracts when the user is not hovering. animations: [ trigger('overlaySlide', [ state(&ap ...

Tips for incorporating ACE Editor syntax highlighting rules into an Angular application

I am attempting to create custom highlighter rules by referencing examples from here and here. import { Component, OnInit, ViewChild, ElementRef } from '@angular/core'; import * as ace from 'ace-builds'; import 'ace-builds/src- ...