[Salesforce & Angular 2+] Remote Actions

Jason Kim
Jason Kim
Sep 4, 2018 · 3 min read

There are a few parts of salesforce that make working in Angular a bit difficult. This post will cover how to create a service that calls remote actions using the native Visualforce.remoting.Manager.invokeAction method, and how to utilize this service in your components. Here are the versions I am using at the time this article is written:

Angular CLI: 6.1.4
Node: 8.11.3
@angular-devkit/architect 0.7.4
@angular-devkit/core 0.7.4
@angular-devkit/schematics 0.7.4
@schematics/angular 0.7.4
@schematics/update 0.7.4
rxjs 6.2.2
typescript 2.9.2

Note that this article does assume that you have a salesforce organization that you are able to successfully deploy your frontend code to. I can cover the setup for this in a separate article in the future if needed!


Creating the Service

First we want to create a service that can call Salesforce remote actions defined in one of our controllers (defined in the visualforce page you are working in). For this example, let’s use the following controller names:

<apex:page controller=”MyController” extensions=”AnotherController, MoarControllers”> … </apex:page>

Then, make the “window” context available to our service by defining it as a provider to the app. In app.module.ts (located in the “src” directory of your angular project), add the following to @NgModule({})

providers: [
{ provide: ‘Window’, useValue: window }
]

Next, create the service file. I put this inside the directory “src/app/Services” but feel free to place it anywhere in your project that is accessible to your components. In this example, I will be calling my service file “remotingManager.service.ts

Setup this service file with the boilerplate code for an injectable service:

import { Injectable, Inject } from “@angular/core”;@Injectable({
providedIn: ‘root’
})
export class VisualForceRemotingManager { constructor(@Inject(‘Window’) private window: Window) { }}

Now that we have access to the window context in our service, create a variable inside the service to act as a reference for the visualforce remoting manager. This can be done by simply declaring the variable in the class as

private _remoteManager: any;

…and in the constructor of the class as

this._remoteManager = (window as any).Visualforce.remoting.Manager;

So far, we have our service file created with access to the Visualforce.remoting.Manager object. Next is to create a wrapper method to call remote actions. Usually, in a visualforce page or component we would reference the remote action using the following format:

'{!$RemoteAction.MyController.myRemoteAction}'

Since we don’t have access to the native salesforce remote action references in our javascript code, we can simply reference these remote actions by concatting the controller and remote action name, such as “MyController.myRemoteAction”. This means our action in our service will look something like:

var myRemoteAction: string = `${controller}.${method}`;

Since remote actions are asynchronous, a promise format must be used when calling and retrieving results from the remote action in the frontend. Using native Promises, we can now create a method to call our remote action

new Promise((resolve, reject) => {
let callback = (result, event): any => {
event.status ? resolve(result) ? reject(event)
}
let remotingParams: object = { escape: false };
this._remotingManager.invokeAction(
myRemoteAction,
cb,
remotingParams
);
});

However, there is one drawback to the method above: we can’t provide any arguments to our remote action. In order to handle this case, let’s use the ES6 spread operator by changing the last line of the code above to:

this._remotingManager.invokeAction(
myRemoteAction,
...args,
cb,
remotingParams
);

This way the service can be generalized to any remote action with any number of arguments (even if there are no arguments)


Final Code

Putting everything together, we have the following service with an optional exception handling that you can fill with whatever you want:

import { Injectable, Inject } from "@angular/core";@Injectable({
providedIn: 'root'
})
export class VisualForceRemotingManager {
private _remoteManager: any;
constructor(@Inject('Window') private window: Window) {
this._remoteManager = (window as any).Visualforce.remoting.Manager;
}
_exceptionHandler(event: any): String {

}
invoke(controller: string, method: string, ...args: Array<any>): Promise<any | Error> {
let action: string = `${controller}.${method}`;
return new Promise((resolve, reject) => {
let cb = (result, event): any => {
event.status
? resolve(result)
: reject(this._exceptionHandler(event));
}
let remotingParams: object = { escape: false };
this._remoteManager.invokeAction(
action,
...args,
cb,
remotingParams
);
});
}
}

Using the Service

In any of your angular components, import the service you just created

import { VisualForceRemotingManager } from “path/to/remotingManager.service”;...constructor(private vfrm: VisualForceRemotingManager) { }

Then, you can define a method that can call this remote action and act upon the results

callMyRemoteAction(): Promise<any | Error> {
this.vfrm.invoke('MyController', 'myRemoteAction', 'argument 1', 'argument 2')
.then((result) => {
// do something here
})
.catch((error) => {
// do something here
})
}

If there are any issues or errors in this article, please let me know and I will update it with credit!

Jason Kim

Written by

Jason Kim

Front-end Engineer @ LeanData, HackReactor Remote Alumni, Ionic Developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade