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 ?

Romain
  • 3,586
  • 7
  • 31
  • 52
  • Do you see the same behavior in [all browsers](https://angular.io/guide/elements#browser-support-for-custom-elements)? Is it the same in Chrome and in Firefox? – ConnorsFan Sep 27 '18 at 17:01
  • Same on Opera, Firefox and IE don't support customElements. – Romain Sep 27 '18 at 17:06

7 Answers7

11

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

cozmik05
  • 477
  • 5
  • 13
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.

B Snow
  • 393
  • 3
  • 14
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

xrobert35
  • 2,488
  • 1
  • 16
  • 15
  • 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. – Romain Sep 27 '18 at 16:08
  • it's still not working with the new example ? You component-one is annoted with @Component right ? – xrobert35 Sep 27 '18 at 16:18
  • 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. – Romain Sep 27 '18 at 16:23
  • It doesn't make any sens. If you add {{toPass}} before – xrobert35 Sep 27 '18 at 16:25
  • You component-one is annoted with @Component right ? – xrobert35 Sep 27 '18 at 16:26
  • @Romain I think that's because of the way you defined the type of the varibale to be of type `String` which is a constructor for `string` types. – amal Sep 27 '18 at 16:26
  • the type could be any or anything else it would not change the runetime execution – xrobert35 Sep 27 '18 at 16:27
  • Nope, no error, just an undefined variable. Changing to string didn't change anything either – Romain Sep 27 '18 at 16:33
  • And yes of course, component-one is annoted with @Component, I'll edit my question. – Romain Sep 27 '18 at 16:35
  • Ahhh you added change strategy :). As a base solution remove it and it I'll normally work. Then we will see how to make it work with it – xrobert35 Sep 27 '18 at 21:35
  • Ok a fixed and tested your code (since you are not giving us a working code as exemple and everything work fine). I supposed that you just a made a mistake that we cannot see since you are not giving us the "real code" – xrobert35 Sep 27 '18 at 21:52
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);
    }
  }

}
1

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

@Input() variable:String = “”;
Asanka
  • 1,628
  • 1
  • 10
  • 20
  • Instanciating the variable in the the destination component just change the log to "Variable: " The string stays empty. – Romain Sep 27 '18 at 16:16
  • Yes it just remove the undefined error. I think xrobert is correct. :) – Asanka Sep 27 '18 at 16:21
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.

Sivaraman
  • 168
  • 1
  • 7
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.

amal
  • 3,140
  • 1
  • 13
  • 20
  • Nope, doesn't change anything. – Romain Sep 27 '18 at 16:38
  • Also tried the backquote templating, doesn't change anything. – Romain Sep 27 '18 at 17:02
  • Can you make a reproduction of the problem? – amal Sep 27 '18 at 17:04
  • Not easily, these are web components, and component-one is bundled and then imported as a script from the same domain. – Romain Sep 27 '18 at 17:09
  • @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. – amal Sep 27 '18 at 17:37