Generating a JSP file

There are a couple Node packages you'll need installed in order to start generating JSP files on your machine. You can get them by cloning this repo and running $ npm install in the repo directory. Make sure any work you do moving forward is within that directory.

To get familiar with the build process, let's generate some simple HTML. Our end goal will be to produce a simple page like this:

<html>
  <head>
    <title>My First jsxQuery Component</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
  </body>
</html>

Writing a Component class

Let's start by creating a Component class with jsxQuery.createClass(). You can find a Component class that's already been started for you in HelloWorld.jsx.

components/HelloWorld.jsx

var jsxQuery = require('jsxquery');

var HelloWorld = jsxQuery.createClass({
  // we will fill this in
})

module.exports = HelloWorld;

We have to add our Component class to exports, since we want to use it outside this file later.

Filling in the render method

Before our HelloWorld component will do anything, we need to give it a render method. createClass will expect your render method to return some JSX.

That HTML above can also serve as perfectly valid JSX, so let's copy and paste that right into our render method. (Remember: we can return a piece of JSX just like any ordinary JavaScript value, since it's really just a function call.)

components/HelloWorld.jsx

...
var HelloWorld = jsxQuery.createClass({
  render: function() {
    return (
      <html>
        <head>
          <title>My First jsxQuery Component</title>
        </head>
        <body>
          <h1>Hello, World!</h1>
        </body>
      </html>
    );
  },
})
...

Our HelloWorld component is now ready to be rendered.

Building some JSP files

Try running this command and you'll see a change in the repo directory:

$ npm run jsxquery-build

Now, there's a bunch of new files in the jsp folder. If you take a look inside, you'll see markup equivalent to what we wrote earlier.

jsp/myFirstJsp.jsp

<html>
  <head>
    <title>My First jsxQuery Component</title>
  </head>
  <body>
    <h1>Hello, World!</h1>
  </body>
</html>

If you're wondering why that ended up in myFirstJsp.jsp, take a look in filetree.jsx.

filetree.jsx

var jsxQuery = require('jsxquery');
var HelloWorld = require('./components/HelloWorld.jsx');

module.exports = {
  myFirstJsp: <HelloWorld />,
  mySecondJsp: <HelloWorld />,
  aDirectory: {
    aSubDirectoryWithOneFile: {
      anotherJsp: <HelloWorld />,
    },
    yetAnotherJsp: <HelloWorld />
  }
};

Our jsxquery-build task uses this object to fill out a file tree. You'll notice that the contents of the jsp directory mirror exactly the structure of that object.

Making an instance of a Component class

You must have noticed how we used our HelloWorld component to create a JSX element. This is how you instantiate a Component class.

<HelloWorld />
// transforms into:
// jsxQuery.createElement(HelloWorld, null, null)

When the JSX interpreter sees that capitalized tag name, it knows to look for a Component with that name. Now, the jsxquery-build task will use that Component's render method to produce our markup.

As you can see, you can instantiate a Component as many times as you want. Of course, it's hard to see the point in that if all our instances of HelloWorld render the exact same thing. Let's make HelloWorld a lot more useful by turning the page title and <h1> text into dynamic values.

Introducing dynamic values as props

The first step to making our HelloWorld component work with dynamic values is to add some special attributes to an existing instanc.

filetree.jsx

module.exports = {
  myFirstJsp:
    <HelloWorld pageTitle="My First jsxQuery Component"
                headerText="Hello, World!" />
  // ...
}

We have just defined two props. We can access these within our Component's render() function as this.props. Then, we can inject them into our JSX with curly braces.

components/HelloWorld.jsx

render: function() {
  var pageTitle = this.props.pageTitle;
  var headerText = this.props.headerText;

  return (
    <html>
      <head>
        <title>{pageTitle}</title>
      </head>
      <body>
        <h1>{headerText}</h1>
      </body>
    </html>
  );
},

If you run $ npm run jsxquery-build again, you should get the same result in myFirstJsp.jsp. No surprises there. But now, we can create something completely different just by changing the props passed to our HelloWorld component.

filetree.jsx

module.exports = {
  myFirstJsp:
    <HelloWorld pageTitle="My First jsxQuery Component"
                headerText="Hello, World!" />,
  mySecondJsp:
    <HelloWorld pageTitle="Reusing our HelloWorld Component"
                headerText="Woohoo!" />
  // ...
};

Run $ npm run jsxquery-build again, and you'll find mySecondJsp.jsp looking like this:

jsp/mySecondJsp.js

<html>
  <head>
    <title>Reusing our HelloWorld Component</title>
  </head>
  <body>
    <h1>Woohoo!</h1>
  </body>
</html>

As you can see, any value you extract into a prop can be replaced on the fly. The values in this.props are not set until the moment you instantiate your Component.

Keeping prop data separate

The props we used here were just a couple simple strings. However, the real purpose of the props object is to keep all your Component's dynamic values in one place. Having them separate from the view logic in your component's render() method is what makes jsxQuery components testable.

Since any given Component could potentially have dozens of dynamic values, that list of props could get really long. For that reason, you probably don't want them all typed in right in your filetree.jsx.

Thanks to JSX spread attributes, you don't have to. Take a look at our first HelloWorld instance written with spread attributes.

var myFirstJspAttributes = {
  pageTitle: 'My First jsxQuery Component',
  headerText: 'Hello, World!'
};

module.exports = {
  myFirstJsp: <HelloWorld {...myFirstJspAttributes} />,
  // ...
};

This way, you can write your props in the nice, readable format of a JavaScript object. Since we're in Node, we can put that object in a separate file and keep your filetree.jsx nice and short.

Our complete Component

Here's our complete HelloWorld code.

components/HelloWorld.jsx

var jsxQuery = require('jsxquery');

var HelloWorld = jsxQuery.createClass({
  render: function() {
    var pageTitle = this.props.pageTitle;
    var headerText = this.props.headerText;

    return (
      <html>
        <head>
          <title>{pageTitle}</title>
        </head>
        <body>
          <h1>{headerText}</h1>
        </body>
      </html>
    );
  },
});

module.exports = HelloWorld;

Aside: Taking advantage of ES6

Feel free to skip this part.

We can make things a bit more concise using ES6 destructuring. Since you'll be referencing properties on that that props object all the time, this will really come in handy. Another nice piece of syntactic sugar we get is shortened function definitions in object literals. Additionally, we can prevent anyone from accidentally touching values they shouldn't, via the new const keyword.

Here's what our HelloWorld component looks like with those changes. At this scale, it's not a very huge difference, but you can imagine the benefits adding up as you write more and more JavaScript.

const jsxQuery = require('jsxquery'); /*** CONST KEYWORD ***/

const HelloWorld = jsxQuery.createClass({
  render() {  /*** METHOD DEFINITION SHORTHAND ***/
    const { pageTitle, headerText } = this.props; /*** DESTRUCTURING ***/

    return (
      <html>
        <head>
          <title>{pageTitle}</title>
        </head>
        <body>
          <h1>{headerText}</h1>
        </body>
      </html>
    );
  },
});

module.exports = HelloWorld;

Conclusion

I hope you agree that this way of generating JSP files isn't so far removed from the way you're used to writing JSP files. It's not an entirely new way of doing things--it's more like a new way of combining skills you already have as a front-end developer. You've seen this tag syntax in HTML/JSTL before, and you've dealt with data stored in JavaScript objects via JSON, as well. Even if the bits of Node that glue everything together are new to you, don't forget--it's just JavaScript!

results matching ""

    No results matching ""