4

I have these two examples:

Example 1:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})

export class AppComponent {
  options:any = {isOpen: false };

  logOptions() {
    console.log(this.options);
  }

}

Example 2:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})

export class AppComponent {
  options:any = {isOpen: false };

  @Input() logOptions() {
    console.log(this.options);
  }

}

in html:

<app-root></app-root>

<script>
  document.querySelector('app-root').logOptions();
</script>

In Example 1 returns error: document.querySelector(...).logOptions is not a function

In Example 2 returns: undefined

Does anyone have any ideas?

ferhado
  • 2,363
  • 2
  • 12
  • 35
  • What are you hoping to accomplish here? Telling us that would help us point you in the right direction. But regarding `Example 2`, you get `undefined` because you have declared a variable named `options` in the class, but you haven't assigned it to anything, so by default it's going to be `undefined` when you call a function that logs the value to the console. – Muirik Jul 15 '20 at 02:26
  • I'm trying to create a custom element that I can later use it in a plain js app, for example a modal dialog with open and close methods – ferhado Jul 15 '20 at 02:28
  • In example 2 I get undefined also when options is defined, I updated the example above – ferhado Jul 15 '20 at 02:38
  • Not sure how/where you're trying to get `this.options` but here's an example where I call your `logOptions()` function in the `ngOnInit()` lifecycle hook and have no problem getting the value: https://stackblitz.com/edit/angular-playground-ulovvd?embed=1&file=app/app.component.ts – Muirik Jul 15 '20 at 02:59
  • Did you look at the stackblitz? I think you need to provide more of your code, as it's unclear how you're actually invoking your function within the component if you're not using something like `ngOnInit()`. – Muirik Jul 15 '20 at 03:17
  • I made an example on stackblitz, but it not seams tobe work on stackblitz, please try the same on localhost https://stackblitz.com/edit/angular-web-component-custom-element – ferhado Jul 15 '20 at 03:20
  • check the files index.html, app.module, app.compoenet – ferhado Jul 15 '20 at 03:21
  • Your script tag under "app-root" runs first, by console logging you'd see that it runs before your component constructor, and even if you manage to wait for angular component resolution you would still not be able to access getOptions through the DOM. have you tried an event based solution? you could register custom events in your angular component and trigger them manually anywhere, even in your script tag with document.dispatchEvent – Alejandro Camba Jul 15 '20 at 03:27

1 Answers1

4

It's a bit late, but in case you still need this, there are couple of ways you could get it to work.

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html'
})

export class AppComponent {

    // @Input() << see comments below
    options: any = {isOpen: false };
    
    // This will NOT be available outside the component, in plain JS
    logOptions() {
        console.log(this.options);
    }

    // This will work ONLY if options is @Input()
    @Input()
    logOptions2() {
        console.log(this.options);
    }
    
    // This property will always provide correct data
    // Ideally a getter should not be @Input() though
    @Input()
    get logOptions3() {
        return this.options;
    }
}

You can access them in plain JavaScript code as

const logComponentOptions = () => {
    const el = document.querySelector('app-root');
    // el.logOptions(); << Error
    el.logOptions2(); // works ONLY if options is @Input()
    console.log(el.logOptions3); // WORKS
    console.log(el.options); // if options is @Input()
}
  1. So the logOptions3 property can always be used to get the options from plain javaScript code. But semantically not correct (a getter as @Input())
  2. The first logOptions() method is not accessible from outside as it is NOT marked @Input()
  3. The logOptions2() method is accessible but can only print correct value IF options is also marked @Input()
  4. But if you mark the options property as @Input(), you can directly access that itself, rather than wrapping that in another method
  5. Lastly, if you simply make @Input() options = false, you can also access it as a simple attribute from the HTML as well
<app-root options="false"></app-root>

Update

If you want to pass data into the component, you can simply expose a setter property with @Input. Then you can set the value from outside the component, with JavaScript

@Input()
public set myData(value: MyDataType) {
    // validation & logic if any
    this._myData = value;
}

// Then in JavaScript
const el = document.querySelector('app-root');
el.myData = {a: 10, b: true, c: 'my data object' }
Arghya C
  • 9,805
  • 2
  • 47
  • 66
  • Thanks for your help, but logOptions3() with set or get anyway returns error `.logOptions3 is not a function`, I need to call method and pass some data to the component from javascript, this data can be type object like: `setOptions({isOpen: true})` – ferhado Jul 18 '20 at 00:16
  • Notice that `logOptions3` is now a property (read-only, with `get`), so it IS NOT a functions. Just call it like a property `el.logOptions3` with no parentheses For passing data into the component, i have updated my answer – Arghya C Jul 18 '20 at 06:15