JavaScript Interop Recipes

The following recipes represent common tasks when mixing Volta and JavaScript code.

Object Sharing between JavaScript and .NET

Objective

We need to share objects between JavaScript and .NET. JSON is the prevalent interchange format for JavaScript applications.

Rationale

We need to bring into .NET objects that originate in JavaScript. Sometimes this is the result of service invocation. Other times this is the result of tier splitting, when a part of the application runs in the browser and another on the CLR.

Recipe

  • The typed access assumes that you created a WeatherService and WeatherObservation classes
  • The structure of the WeatherObservation class parallels that of the of the JSON string returned by the service. For example

JSON:

{"weatherObservation":{"stationName":"Redmond, Roberts Field Airport","datetime":"2007-08-27 13:56:00"}}

C#:

    public class WeatherObservation

    {

        [Import]

        public extern String datetime

        {

            get;

        }

        [Import]

        public extern String stationName

        {

            get;

        }

    }}

The WeatherService class is what provides WeatherObservations:

    public class WeatherService

    {

        [Import]

        public extern WeatherObservation weatherObservation

        {

            get;

        }

    }

For typed access you can only use properties, not fields.

For untyped access (possible only for subclasses of JavaScriptObject) you access the properties by key.

Invoking JavaScript Functions from Volta Code

Objective

  1. We have existing JavaScript libraries such as Virtual Earth and we want to write new applications that use them from managed code.
  2. We have existing managed code that calls unmanaged code and we want to use it in Volta applications.

Rationale

The Microsoft development stack comprising the .NET languages, .NET libraries, Visual Studio IDE and other tools is a powerful high-end development platform, by contrast to the relatively primitive JavaScript development experience. We want to help developers graduate upwards from total dependence on JavaScript as a complete platform by making it easy for them to leverage the power of .NET without giving up the ubiquitous reach of the browser.

The Recipe

Volta makes it trivial to call JavaScript from .NET languages. Simply annotate a class or an extern method with an Import custom attribute that names or defines the JavaScript implementation.

Here’s an example from the Virtual Earth library. The first line of code declares a .NET constructor whose implementation is supplied as an inline function. This function simply bridges .NET namespaces to JavaScript prefixes. The second line of code declares a .NET event handler that maps to a JavaScript event handler with a slightly different name. In both cases we use the extern modifier so we don’t have native implementations, by design.

namespace Microsoft.LiveLabs.Volta.VirtualEarth

{

    [Import(ScriptMemberNameCasing = Casing.Pascal, PassInstanceAsArgument = false)]

    public class Map

    {

        private static int s_counter;

 

        [Import("function(id) { return new VEMap(id); }")]

        public extern Map(string id);

        //

        [Import("onLoadMap",PassInstanceAsArgument = true)]

        public extern event HtmlEventHandler MapLoaded;

        //

    }

}

Consequences

If needed, we can customize the mapping via optional parameters. For instance, we can rename the function or reorder the parameters.

Once imported, a class or method is a first-class citizen of .NET and enjoys all the benefits of IntelliSense and compile-time type checking.

Detail Highlights

  • By default the compiler automatically generates the name of the JavaScript function or property method through camel-casing the name of the corresponding .NET element. If we need a different JavaScript name, override the default by specifying the desired name name as the first argument of the Import custom attribute.
  • By default, Volta passes an object instance as the first argument of the JavaScript function. We can override this default in the Import arguments.
  • The Volta.JavaScript namespace declares .NET functions and properties for standard JavaScript globals, like Global.Eval.

Invoking Volta code from JavaScript

Objective

Traditionally we’ve used JavaScript to build browser-based applications. However we reached a glass ceiling due to a combination of language features, available libraries, and/or IDE support. We’d like to leverage Volta when developing in JavaScript.

Rationale

When developing in JavaScript we don’t have access to the .NET libraries. In addition, since the language is dynamically-typed we give up features like IntelliSense or compile-time error checking and reporting.

Recipe

Volta makes it easy to integrate the .NET and JavaScript worlds. We carve out the code that is tedious to write in JavaScript, write it using a .NET language and libraries, and use Volta to invoke it from the JavaScript program.

Consider the following code fragment where we implement the ComputeVelocity method in C#. We use the Export custom attribute to mark this method as something that we want to expose to JavaScript programs. Once Volta compiles this code we could call this method from JavaScript code, and thus are able to tackle problems that are too complicated to be solved in JavaScript.

[Export]

public static double ComputeVelocity()

{

    // computation goes here

}

Detail Highlights

  • Only static methods can be exported to JavaScript.
  • By default the compiler automatically generates the name of the function or property method through camel casing the name of the corresponding C# element. If you need a different name you can override it by specifying the name as the argument.
  • The Volta compiler doesn't guarantee that the uniqueness of the generated JavaScript name.