How to call an Angular 4 method from JavaScript function? [duplicate] - javascript

I'd like to be able to pass some data\propagate events from a plugin on the page to my Angular 4 app.
More specifically, in my case data\events are generated inside a Silverlight plugin app that is next to the Angular app on the page.
I have the following solution in my mind:
Create a global JS function which gets called from Silverlight (since this seems to be the simplest way to get data out from Silverlight) when
there is a need to talk to Angular side.
The function, in turn, calls some Angular class method passing data
collected from Silverlight.
As an illustration to that (excluding the Silverlight part), we could have the following.
A method as an entry point on the Angular side:
export class SomeAngularClass {
public method(data: any): void {
...
}
}
And somewhere outside the Angular realm, we add a global plain JavaScript function (to be called by Silverlight):
window.somePlainJsFunction = function (data) {
// How to consume SomeAngularClass.method() from here?
}
The question is: how can we call the Angular class methods from a plain JavaScript function?

As pointed by #Dumpen, you can use #HostListener to get the custom event dispatched from javascript outside of Angular. If you also want to send parameters, then you can send them by adding them as detail object.
In Javascript:
function dispatch(email, password) {
var event = new CustomEvent('onLogin', {
detail: {
email: email,
password: password
}
})
window.dispatchEvent(event);
}
Then you can call this method on button click.
Now to listen to this dispatched event in Angular, you can create function in Angular component and add #HostListener to it like below:
#HostListener('window:onLogin', ['$event.detail'])
onLogin(detail) {
console.log('login', detail);
}
This code you can add in any component. I have added it to my root component - AppComponent

You can use a events, so you have the JavaScript send an event that you are listening for in your service.
The CustomEvent is a new feature so you might need to polyfill it: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
JavaScript:
var event = new CustomEvent("CallAngularService");
window.dispatchEvent(event);
Angular:
#HostListener("window:CallAngularService")
onCallAngularService() {
// Run your service call here
}

I think this is what you are looking for service out side angular
You can create function/class outside Angular and provide as a value in the angular. in this way you can handle both angular and non angular stuffs together:
class SomePlainClass {
...
}
window.somePlainJsFunction = new SomePlainClass();
#NgModule({
providers: [{provide: SomePlainClass, useValue: window.somePlainJsFunction}],
...
})
class AppModule1 {}
#NgModule({
providers: [{provide: SomePlainClass, useValue: window.somePlainJsFunction}],
...
})
class AppModule2 {}
class MyComponent {
constructor(private zone:NgZone, private SomePlainClass:SharedService) {
SomePlainClass.someObservable.subscribe(data => this.zone.run(() => {
// event handler code here
}));
}
}

The way it should be done depends on particular case, especially on the precedence.
If Silverlight aplication is initialized before Angular application, and window.somePlainJsFunction is called before finishing Angular application initialization, this will result in race condition. Even if there was an acceptable way to get Angular provider instance externally, the instance wouldn't exist during somePlainJsFunction call.
If window.somePlainJsFunction callback is called after Angular bootstrap, window.somePlainJsFunction should be assigned inside Angular application, where SomeAngularClass provider instance is reachable:
window.somePlainJsFunction = function (data) {
SomeAngularClass.method();
}
If window.somePlainJsFunction callback is called before Angular bootstrap, it should provide global data for Angular application to pick up:
window.somePlainJsFunction = function (data) {
window.angularGlobalData = data;
}
Then window.angularGlobalData can be used inside Angular, either directly or as a provider.
Working Example

Related

NativeScript: Difference between a class and a service?

I'm trying to get into Nativescript + Angular2, and I read the following in the tutorial:
We’ll build this functionality as an Angular service, which is Angular’s mechanism for reusable classes that operate on data.
What they then do is to create a simple class, like this:
import { Injectable } from "#angular/core";
import { User } from "./user";
#Injectable()
export class UserService {
register(user: User) {
alert("About to register: " + user.email);
}
}
Now, I can't really see the difference between a normal class and a service - this is a very normal class definition.
So, why is it called an "Angular service"?
This creates a basic Angular service with a single method that takes an instance of the User object you created in the previous section.
Also, when using this "service" in the tutorial, it isn't clear to me when this class is instantiated - when is the construction executed? Is the object saved in memory for later use? The only call to the "userservice" in the tutorial is like this:
import { Page } from "ui/page";
import { Component, ElementRef, OnInit, ViewChild } from "#angular/core";
import { User } from "../../shared/user/user";
import { UserService } from "../../shared/user/user.service";
import { Router } from "#angular/router";
import { Color } from "color";
import { View } from "ui/core/view";
#Component({
selector: "my-app",
providers: [UserService],
templateUrl: "./pages/login/login.html",
styleUrls: ["./pages/login/login-common.css", "./pages/login/login.css"]
})
export class LoginComponent implements OnInit {
user: User;
isLoggingIn = true;
#ViewChild("container") container: ElementRef;
constructor(private router: Router, private userService: UserService, private page: Page) {
this.user = new User();
this.user.email = "bla#bla.com";
this.user.password = "1234";
}
//.... methods and stuff...
}
A class, in that context, is a regular class as in any other OO language: a "prototype" of objects which you can create instances simply using:
let myInstance = new MyClass(<arguments...>);
So, actually, an Angular service is also a class.
But consider services a special kind of class. The main difference between regular classes and service classes is their lifecycle, specially who creates their instance (who calls new).
Instances of a service are created - and managed (disposed) - by the Angular "container" (angular injector, actually).
You can also "inject" instances of service classes in constructors of other service classes (or other managed components).
A good resource in the capabilites of services is Angular's Dependency Injection Guide.
When is the construction executed?
The injector executes the construction. When? See below.
Is the object saved in memory for later use?
It could be. Depends on where you registered the service.
Before anything, know that Angular DI is a hierarchical injection system.
If you register the service with an Angular Module, the service will be created by the application's root injector. So everyone below it (aka everyone in that module) will receive the same instance of the service. In other words, Angular (will call the injector only once and) will create only one instance of the service class and pass that same instance to whoever asks for that service. And that instance will live as long as that module lives.
Now, if you register the service with a component, then the service will be registered with that component's injector. So when such component requests an instance of the service, angular will call the injector and create an instance. If any child of that component asks for an instance of such service, angular will provide the same instance. No one else, only children of the component, will receive that same instance. When that component dies, the service instance dies as well.
How does a "regular class" differ? It lacks the Injector?
The difference is not only the lack of an injector.
Angular aside, just JavaScript: you create an instance of a "regular class" by calling let instance = new MyRegularClass() somewhere in your code, right?
This instance has no "magical effects", it does nothing more than any class would (just regular JavaScript methods and properties). Example: if you need instances of other classes as arguments in the constructor, no one will "magically" create you those instances and pass them. You will have to create them manually, when calling new (e.g. new MyClass(new SomeOtherClassIDependOn(), ...)). If you want to instantiate SomeOtherClassIDependOn only once and reuse the same instance everywhere it is needed, you will have to save that instance and pass it wherever it is neeed yourself.
As services, though, angular can take some of that burden off your shoulders.
Now, before anything: since every service, deep down, is a class, someone has to call new MyServiceClass(). The difference is that someone is not you anymore. There is no new UserService() in your code. So, who is it? This someone is the Injector.
When Angular notices someone asks for a service, it calls for the injector to instantiate that service. The injector then calls let serviceInstance = new MyServiceClass(<dependencies>) and adds some "magic" to it (e.g. it can pass - inject - instances of other services to the constructor of a service), and make it available (save it) for anyone that requests that service in the scope you registered it.
Note: You can call new UserService(...) yourself, as it UserService is a class. But this instance is a regular object, not managed by angular, there is no magic (no constructor arguments will be injected, no instance is saved and reused).

How to call an Angular 4 method from a standalone plain JavaScript function?

I'd like to be able to pass some data\propagate events from a plugin on the page to my Angular 4 app.
More specifically, in my case data\events are generated inside a Silverlight plugin app that is next to the Angular app on the page.
I have the following solution in my mind:
Create a global JS function which gets called from Silverlight (since this seems to be the simplest way to get data out from Silverlight) when
there is a need to talk to Angular side.
The function, in turn, calls some Angular class method passing data
collected from Silverlight.
As an illustration to that (excluding the Silverlight part), we could have the following.
A method as an entry point on the Angular side:
export class SomeAngularClass {
public method(data: any): void {
...
}
}
And somewhere outside the Angular realm, we add a global plain JavaScript function (to be called by Silverlight):
window.somePlainJsFunction = function (data) {
// How to consume SomeAngularClass.method() from here?
}
The question is: how can we call the Angular class methods from a plain JavaScript function?
As pointed by #Dumpen, you can use #HostListener to get the custom event dispatched from javascript outside of Angular. If you also want to send parameters, then you can send them by adding them as detail object.
In Javascript:
function dispatch(email, password) {
var event = new CustomEvent('onLogin', {
detail: {
email: email,
password: password
}
})
window.dispatchEvent(event);
}
Then you can call this method on button click.
Now to listen to this dispatched event in Angular, you can create function in Angular component and add #HostListener to it like below:
#HostListener('window:onLogin', ['$event.detail'])
onLogin(detail) {
console.log('login', detail);
}
This code you can add in any component. I have added it to my root component - AppComponent
You can use a events, so you have the JavaScript send an event that you are listening for in your service.
The CustomEvent is a new feature so you might need to polyfill it: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
JavaScript:
var event = new CustomEvent("CallAngularService");
window.dispatchEvent(event);
Angular:
#HostListener("window:CallAngularService")
onCallAngularService() {
// Run your service call here
}
I think this is what you are looking for service out side angular
You can create function/class outside Angular and provide as a value in the angular. in this way you can handle both angular and non angular stuffs together:
class SomePlainClass {
...
}
window.somePlainJsFunction = new SomePlainClass();
#NgModule({
providers: [{provide: SomePlainClass, useValue: window.somePlainJsFunction}],
...
})
class AppModule1 {}
#NgModule({
providers: [{provide: SomePlainClass, useValue: window.somePlainJsFunction}],
...
})
class AppModule2 {}
class MyComponent {
constructor(private zone:NgZone, private SomePlainClass:SharedService) {
SomePlainClass.someObservable.subscribe(data => this.zone.run(() => {
// event handler code here
}));
}
}
The way it should be done depends on particular case, especially on the precedence.
If Silverlight aplication is initialized before Angular application, and window.somePlainJsFunction is called before finishing Angular application initialization, this will result in race condition. Even if there was an acceptable way to get Angular provider instance externally, the instance wouldn't exist during somePlainJsFunction call.
If window.somePlainJsFunction callback is called after Angular bootstrap, window.somePlainJsFunction should be assigned inside Angular application, where SomeAngularClass provider instance is reachable:
window.somePlainJsFunction = function (data) {
SomeAngularClass.method();
}
If window.somePlainJsFunction callback is called before Angular bootstrap, it should provide global data for Angular application to pick up:
window.somePlainJsFunction = function (data) {
window.angularGlobalData = data;
}
Then window.angularGlobalData can be used inside Angular, either directly or as a provider.
Working Example

Accessing an Angular Service from within a function, inside a class, inside a module

module Helper {
export class ListController {
static batchDelete(data) {
// Do something with Angular's $http ...
$http.post(data)
}
}
}
// On click function
Helper.ListController.batchDelete(toBeDeleted);
Deleting data from the array works fine, however, I would like to do an $http request from within that function. How do I access Angular's $http service inside batchDelete()?
Your code sample:
// Do something with Angular's $http ...
$http.post(data)
You need to get $http injected. The ideal way to do this would be to create a class as a service and then let angular inject the service into your controller.
More
I have a video on the subject : https://www.youtube.com/watch?v=Yis8m3BdnEM

Pub/Sub design pattern angularjs service

I've been trying to use the answer here posted by Mark Rajcok
angular JS - communicate between non-dependend services
I am having trouble understanding his answer. Specially this part:
angular.forEach(event1ServiceHandlers, function(handler) {
handler(some_data);
});
Is the event1ServiceHandlers array populated with functions (here called handler) that is triggered in this forEach loop?
I think it would be much easier to understand with a good example how a publish/subscribe is set up.
I have two services who need to communicate but I want to avoid $rootScope.$broadcast so from what I have read a pub/sub service is the best approach. One of my services need to execute a function on my other service, but that service already has my first service as a dependency so I cannot do the same both ways because of circular dependency.
My question: So assume you have two angularjs services (factory), how does service 1 execute a function on service 2 if service 2 already has service 1 as a dependency. Not using $broadcast and $on
Is the event1ServiceHandlers array populated with functions (here called handler) that is triggered in this forEach loop?
Yes
how does service 1 execute a function on service 2 if service 2 already has service 1 as a dependency
Create service 3, NotificationService as before:
.factory('NotificationService', [function() {
var event1ServiceHandlers = [];
return {
// publish
event1Happened: function(some_data) {
angular.forEach(event1ServiceHandlers, function(handler) {
handler(some_data);
});
},
// subscribe
onEvent1: function(handler) {
event1ServiceHandlers.push(handler);
}
};
}])
Have service 2 register a callback function with the NotificationService:
.factory('Service2', ['NotificationService',
function(NotificationService) {
// event1 handler
var doSomething = function(someData) {
console.log('S2', someData);
// do something here
}
// subscribe to event1
NotificationService.onEvent1(doSomething);
return {
// define public API for Service2 here
}
}])
Whenever service 1 wants function doSomething() on service 2 to execute, it can publish the event1Happened event:
.factory('Service1', ['NotificationService',
function(NotificationService) {
var someData = ...;
return {
// define public API for Service1 here
callService2Method: function() {
// publish event
NotificationService.event1Happened(someData);
}
}
}])
In his example, NotificationService is a new service that any of the existing services would depend on. He provided an implementation of Service1 but Service2 would essentially be the same...both depend on NotificationService and neither know about each other.
Service1 and Service2 each subscribe to events by calling NotificationService.onEvent1(event1Happened); and trigger events by calling NotificationService.event1Happened(my_data);.

Angular 2 + SignalR - Accessing Angular internals from external script

I'm using SignalR with an Angular2 app, where we want the SignalR client methods to call into the Angular app with data received from the server, and then have Angular redo the data-bindings. For example, within the Angular app I expose a global variable for our store, which has a collection on it.
E.g.
(TypeScript)
....
export class Store{
Customers : Customer[];
constructor(){
window["GlobalStore"] = this;
}
setCustomers (customers : Customer[]){
this.Customers = customers;
}
}
....
and in my client SignalR javascript I have a function:
$.connection.MyHub.client.receive = function(data){
//Call into the Angular app and set data, which is then rendered in views
//via data-binding
//data contains a json array of customers
window.GlobalStore.setCustomers(data);
}
This seems to work and set the data on the store, however, when the data is reset Angular does not seem to detect changes, and hence the UI is not refreshed.
It's not an issue with data-typing, as even passing a simple string/integer etc through to the store correctly sets the store property when I debug, however, the Angular framework doesn't seem to then trigger change detection and refresh the views.
Any ideas on how to either:
A) Manually trigger the angular databinding so it refreshes the view?
B) Call methods within the Angular 2 app from external using a different means?
Thanks
To manually run change detection:
Use ApplicationRef::tick() method.
Use NgZone::run() method to wrap you code which should be executed inside angular zone.
You can get them by using dependency injection or by bootstrapping your application using platform().application(bindings).bootstrap(Component):
import { platform } from 'angular2/angular2';
const app = platform().application([] /* - bindings */); // you can use `app.tick()`
const zone = app.zone; // you can use `zone.run`
app.bootstrap(Component);

Categories

Resources