2

I have an input signal which I take and set as attribute but in browser I see an error

this.width is not a function

export class SkeletonComponent implements OnInit {
  readonly width = input<string>('');

  ngOnInit(): void {
    if (isPlatformServer(this.platformId)) {
      return;
    }

    const host = this.host.nativeElement;
    host.style.setProperty('--app-skeleton-width', this.width() || '100%');

1 Answer 1

1

The reason you might be getting this error, is because the ngOnInit is execute in a context (scope) somewhere other than your component scope, The code shared does not seem to have any issues, but you can achieve this feature using lesser code using @HostBinding or host demonstrated below.


Define the width CSS variable using host property of @Component params:

@Component({
  ...
  host: {
    'style.--app-skeleton-width': 'width()',
  },
})

Here we can define the CSS property using 'style.--app-skeleton-width' and execute the signal inside a string.

We can then use the transform property to set a default value.

readonly width = input<string, string>('', {
  transform: (value: string) => value || '100%',
});

Or we can simply define the width as the default value.

readonly width = input<string>('100%');

Define the @HostBinding decorator and execute the signal inside:

We can define the @Hostbinding and return the signal value.

@HostBinding('style.--app-skeleton-width')
get skeletonWidth() {
  // if (isPlatformServer(this.platformId)) {
  //   return;
  // }
  return this.width() || '100%';
}

We can achieve this using the two methods, below is a working example for your reference:


Full Code:

import { Component, HostBinding, input } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

@Component({
  selector: 'app-child2',
  template: `
    this is the child
    <div style="width: var(--app-skeleton-width)">
      I got my width from --app-skeleton-width
    </div>
  `,
  styles: [
    `div {
      background-color: lightblue;
    }`,
  ],
  host: {
    'style.--app-skeleton-width': 'width()',
  },
})
export class Child2 {
    readonly width = input<string, string>('', {
      transform: (value: string) => value || '100%',
    });
}

@Component({
  selector: 'app-child',
  template: `
    this is the child
    <div style="width: var(--app-skeleton-width)">
      I got my width from --app-skeleton-width
    </div>
  `,
  styles: [
    `div {
      background-color: lightblue;
    }`,
  ],
})
export class Child {
  readonly width = input<string>('');

  @HostBinding('style.--app-skeleton-width')
  get skeletonWidth() {
    // if (isPlatformServer(this.platformId)) {
    //   return;
    // }
    return this.width() || '100%';
  }
}
@Component({
  selector: 'app-root',
  imports: [Child, Child2],
  template: `
    <app-child [width]="'500px'"/>
    <br/>
    <br/>
    <app-child2 [width]="'300px'"/>
  `,
})
export class App {
  name = 'Angular';
}

bootstrapApplication(App);

Stackblitz Demo

Sign up to request clarification or add additional context in comments.

2 Comments

quite good approach but why this is happening ? as usual signals in angular you can take value as width() or any value. For instance in below code I would also need chekc value of this this.class() for some logic like adding class to host and etc.
@Sunstrike527 do a console.log(this) above the host.style.setProperty('--app-skeleton-width', this.width() || '100%'); then observe the scope where it is executed, the code you shared should not have any issues as far as I can see

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.