The Practical Client

Integrating ASP.NET MVC, React and JSX with TypeScript

Creating a form with TypeScript, React and JSX lets you generate the HTML for your form dynamically, at runtime, and on the user's computer. Here's how to integrate a data- driven form into ASP.NET MVC.

In last month's column, I showed how to configure Visual Studio and TypeScript to use React+JSX to generate a basic "Hello, World" Web page. This month, I'll show how to do something slightly more useful with TypeScript and React+JSX: deliver a display-only HTML form from an ASP.NET MVC View.

A quick introduction to the technologies: React+JSX allow you to define a set of XML-compliant tags that, when processed on the client, generate all or part of an HTML page. More generally, as I pointed out last month, React+JSX let you specify tags that, when fed to a processor (like React), generate anything you want.

In addition to allowing you to dynamically generate the HTML you need at runtime, React+JSX also transfers page creation from the server (a limited resource shared among all users) to the user's computer (a potentially unlimited resource that's used by a single user). This is a good thing for the scalability of your application: Every user brings a new CPU to the application and generates their own HTML.

Integrating with ASP.NET MVC
The first thing to recognize when integrating with ASP.NET MVC is that MVC doesn't care what tool (or tools) you use to generate your page. As long as the page in the browser is valid HTML, that page can send user data back to your server-side Action methods. Regardless of whether you use Razor or JSX+React to build your page, model binding will take care of matching the data that comes up from the browser into your action method's parameters.

That doesn't mean you need to abandon using Views all together, though. It just means that much of the work in generating the HTML can disappear from the View and reappear in your TypeScript code. All you have to do is decide how much (and which part) of your HTML is going to be handled through JSX. For this column, I'm going to turn all of the page construction over to React and JSX because … well, what the heck: Go for the gusto.

As a result, in my View I include only my page's stock HTML (for example, html, head, body, and form elements) plus the tags that invoke my scripts and stylesheets. Inside my form element, all I have is a div element where I'll shove the form's HTML.

To generate that HTML and insert it into my div element, I call a TypeScript function that uses React+JSX to generate my form's HTML. To give that function access to my View's Model object, I pass a JSON-encoded version of the object in my View's Model property to the function. Just to make sure that everything's ready to work before I start generating my HTML, I wrap that call in a jQuery-ready function. That means a View to display Customer information now looks like Listing 1:

Listing 1: An ASP.NET MVC Page, Integrating JSX
@model SampleApp.CustomerDTO

@{
  Layout = null;
}

<!DOCTYPE html>

<html>
<head>
  <meta name="viewport" content="width=device-width" />
  <title>Display Customer</title>
</head>
<body>
    
  @using (Html.BeginForm())
  {
    <div id="divCustomerDisplay"/>        
  }  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.7/react-dom.js"></script>
  <script src="~/Scripts/jquery-2.1.4.js"></script> 
  <script src="~/Scripts/CustomerDisplay.js"></script>
  <script>
    $(function()
    {
      (DisplayCustomer(@Html.Raw((Json.Encode(Model))));
    });
  </script>
</body>
</html>

The CustomerDisplay.js file in the last script tag contains the TypeScript code (compiled to JavaScript, of course) that will generate my HTML and insert it into my divCustomerDisplay element.

Assuming the View holding that code is called CustomerDisplay, an Action method that would use this View would look like this:

public ActionResult CustomerDisplay(string CustId)
{
  CustomerDTO cust = new CustomerDTO(CustId);       
  return View("CustomerDisplay", cust);
}

Passing .NET Framework Objects to TypeScript Code
In my TypeScript function, I want to accept the Customer information that's being passed to my CustomerDisplay function in that JSON-encoded string. Therefore, I need to translate that JSON string into a client-side, JavaScript-compatible class. I also want that class to be written in TypeScript but to match my server-side, .NET Framework CustomerDTO class written in C#.

I could attempt to write that class in TypeScript by hand … but there's an easier way. Lately (and when coding in C#), I've been using TypeScriptSyntaxPaste to convert my C# classes into TypeScript. TypeScriptSyntaxPaste gives me this TypeScript class from my original C# class:

class CustomerDTO {
  public FirstName: string;
  public LastName: string;
  ...more properties...

That still leaves one potential problem: In my CustomerDisplay method, I'm accepting a string of JSON-encoded data, but inside that function, I want to use my CustomerDTO class. Fortunately, thanks to TypeScript duck typing, converting from JSON to a class is easy: I just need to change the way I declare my method's parameter from string to the type I want (CustomerDTO, in this case). With that change, my DisplayCustomer method's signature looks like this:

function DisplayCustomer(cDTO: CustomerDTO) 
{
}

With that in place, it's time to generate the page's HTML.

Generating the HTML
To generate the page's HTML, I call the render method on the ReactDOM object, passing two parameters. The first parameter is a custom element that will control what HTML is generated (I've called this element CustomerForm). My CustomerForm element has two attributes: FirstName and LastName, which I set to appropriate data from my TypeScript CustomerDTO object.

As the second parameter to the render method, I pass a reference to the element I want to shove my HTML into. To find that element, I use a jQuery selector and, to retrieve the DOM elements referenced by that selector, I use jQuery's get function (passing 0 to get the first matching element). Because all the work of generating the HTML is handled by my CustomerForm element, my complete DisplayCustomer function now looks like this:

function DisplayCustomer(cDTO: CustomerDTO) {
  ReactDOM.render(
    <CustomerForm FirstName={cDTO.FirstName} LastName={cDTO.LastName}/>,
    document.getElementById('divDisplayCustomer'));
}

One note: Your custom element names must be declared with an uppercase letter as the first character in its name. Both TypeScript and React use this convention to distinguish your custom elements (called "value-based elements" in React-speech) from tags in the host environment (called "intrinsic elements"). In this case, the host environment is HTML, so the intrinsic elements are tags like input, div and so on. The corollary of this rule is that you must use lower case names when referring to HTML elements.

Defining Your Custom Element
The first of two steps in creating my CustomerForm element is to specify the properties that will define the attributes on an element. I do that through a TypeScript interface: The property names I specify in this interface will, eventually, be tied to corresponding attributes on my custom element. Because I want my CustomerForm to have attributes called FirstName and LastName, I create an interface with properties with those names:

interface propsCustomer {
  FirstName: string;
  LastName: string;
  ...more properties...

I don't have to use multiple properties. I could, for example, have had a single property that accepted a CustomerDTO class. With that design, I could pass my whole CustomerDTO object through the corresponding attribute in my custom element (see next month's column for an example of that design).

The second and final step in defining my custom element is to create a class that specifies the name of my element. This class must extend React's Component class and be tied to the interface that defines the properties – in this case, my propsCustomer interface – and, for now, an empty class (again, see next month's column for what that empty class is used for). For my CustomerForm element, my class looks like this:

class CustomerForm extends React.Component<propsCustomer, {}> 
{

Within that class you must define a method called render. When it's time to generate your custom element's HTML, this render method will be called by React through the call to the render method on the ReactDOM object. In your render method, you define the HTML to be produced from your custom element. In amongst that HTML, you use React's {} delimiters to specify where values set in your custom element's attributes are to be used.

In this example, I just want to display the FirstName and LastName property from my CustomerDTO with the appropriate captions:

  render() {
    return <p>
      First Name: {this.props.FirstName}
      <br/>
      Last Name:{this.props.LastName}
      <br/>
      ...more properties...
      </p>
  }
}

You can only pass a single element to the return statement in the render method, so I've enclosed my form in <p> tags.

With my class, my interface and my call to ReactDOM's render method, my page magically builds itself on the user's computer when the user navigates to my Action method. Next month, I'll enhance this code so that I can generate an updateable page and integrate some AJAX code to handle updates. At that point, I really will have something useful.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

  • Compare New GitHub Copilot Free Plan for Visual Studio/VS Code to Paid Plans

    The free plan restricts the number of completions, chat requests and access to AI models, being suitable for occasional users and small projects.

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

Subscribe on YouTube