7

I have several separated web components, made with angular elements, that I import in a main one, which has a router and is the base application. Routes are pretty simple:

import { Component } from '@angular/core';
import { Router } from "@angular/router";

@Component({
    selector: 'example',
    template: '<component-one [variable]="toPass" (callback)="onCallback($event)"></component-one>',
    styles: []
})
export class ExampleComponent {
    public toPass: string = "Hello World!";

    constructor(private router: Router) {}

    onCallback(event) {
        console.log(event);
        this.router.navigate(['example-two']);
    }
}

And the components do the things they are supposed to do.

import { Component, Input, OnInit } from '@angular/core';

@Component({
    selector: "component-one",
    templateUrl: "./component-one.html",
    styleUrls: ["./component-one.scss"],
    encapsulation: ViewEncapsulation.Emulated,
    changeDetection: ChangeDetectionStrategy.OnPush
})
export class ComponentOne implements OnInit {
    @Input() variable: string;
    @Output() callback = new EventEmitter<any>();

    constructor() {}

    ngOnInit() {
        console.log("Variable: ", this.variable);
    }

    someRandomButtonClicked() {
        callback.emit({ data: "Callback Called" });
    }
}

Now when I launch the main app, everything shows as expected, the callback works fine but the variable is undefined. Am I missing something in the @Input declaration in my webcomponents ?

2
  • Do you see the same behavior in all browsers? Is it the same in Chrome and in Firefox? Commented Sep 27, 2018 at 17:01
  • Same on Opera, Firefox and IE don't support customElements. Commented Sep 27, 2018 at 17:06

7 Answers 7

12

Since you are using angular elements, make sure your variable name is in smallcaps like so @Input() myvariable: string and not @Input() myVariable: string check here https://github.com/angular/angular/issues/24871

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

2 Comments

While this may theoretically answer the question, it would be preferable to include the essential parts of the answer here, and provide the link for reference.
This was it for me.
3

I think this is the explanation here: https://github.com/angular/angular/issues/29050. If you console.log() your variables in the ngOnChanges() hook you will get the correct values but in ngOnInit() they are still undefined. The order in which the hooks execute changes when using custom elements. That's why in your it was undefined. In my case I am still looking to see how I can force ngOnChanges() to run first and then ngOnInit() but no luck yet.

Comments

2

I think you just miss understand the new syntax :)

When you use [] around a input name you tell angular that you give him a variable. If you don't use it you ask angular to take it as a primitive.

So :

<component-one [variable]="myVar"   

A var called "myVar" must be define in the component

Without

<component-one variable="myVar" 

Angular will take "myVar" as a string value

Useless but working :

<component-one [variable]="'myVar'"

You give a new string as variable

11 Comments

My bad, I made a mistake while writing my question, I'm trying to pass the variable named toPass to component-one, I edited my question accordingly.
it's still not working with the new example ? You component-one is annoted with @Component right ?
No it's not working. As a matter of fact, it's working when I use attributes with hard-coded values like variable="Hello world" but neither [variable]="toPass" nor variable="{{ toPass }}" are working.
It doesn't make any sens. If you add {{toPass}} before <component-one in the template, does it work ?
You component-one is annoted with @Component right ?
|
2

I have had this issue about two days and finally I found the solution.

What you have to do is use the method ngOnChanges() in your component child (web component that you want to expose). Since this is to detect any change of your @Input() fields.

Here I leave a snippet code of my web component:

@Component({
  selector: 'app-shipment-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnChanges {

  @Input() domain;

  @Output() response = new EventEmitter<string>();

  constructor() {
  }

  ngOnInit(): void {
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.domain) {
      console.log("Domain URL: ", this.domain);  
      this.response.emit(this.domain);
    }
  }

}

Comments

1

You can just initiate the variable in ComponentOne. Then it should work

@Input() variable:String = “”;

2 Comments

Instanciating the variable in the the destination component just change the log to "Variable: " The string stays empty.
Yes it just remove the undefined error. I think xrobert is correct. :)
1
  • In my case, ngOnChanges wasn't called at all when I sent the input variable to custom element/web component. So I tried 'attribute binding' and it worked fine.

From angular.io:
Attribute binding in Angular helps you set values for attributes directly. Attribute binding syntax resembles property binding, but instead of an element property between brackets, you precede the name of the attribute with the prefix attr, followed by a dot

Change the below implementation

<custom-element name="{{userName}}"></custom-element>

                             (or)

<custom-element [name]="userName"></custom-element>

To this:

<custom-element [attr.name]="userName"></custom-element>

For passing constant value, use

<custom-element [attr.name]="'John'"></custom-element>

Note: You will get the value in ngOnChanges() method.

Comments

0

In your code, change String to string in all the places shown in the question. And see if it works.

String is a constructor for type string. More info here

See distinction between string and String.

UPDATE:

I have a feeling, it has to do with the inline template format you used.

Change this

@Component({
    selector: 'example',
    template: '<component-one [variable]="toPass" (callback)="onCallback($event)"></component-one>',
    styles: []
})

to this,

@Component({
    selector: 'example',
    template: `<component-one [variable]="toPass" (callback)="onCallback($event)"></component-one>`,
    styles: []
})

See, I changed the template: '' to this

template: ``

Otherwise, the parsing of the inline template string might be messed up.

See if that works.

5 Comments

Nope, doesn't change anything.
Also tried the backquote templating, doesn't change anything.
Can you make a reproduction of the problem?
Not easily, these are web components, and component-one is bundled and then imported as a script from the same domain.
@Romain If not, it will be hard to debug the problem. In any case, if you are using " or ' within your inline template: property's value, you should either revert to using the backticks (`) to wrap the entire string or join individual 'valid' string sections as an array.

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.