Jump to content

Object oriented Typescript example for documenation


Wink
 Share

Recommended Posts

Had another look at my files, but still stuck. Apologies for the lack of information earlier but didn't have direct access to the files at that time. The problem occurs inside your game.ts file, in that it does not seem to recognise the new Table class.

Here is the entire updates game.ts file

class Game {
  private _canvas: HTMLCanvasElement;
  private _engine: BABYLON.Engine;
  private _scene: BABYLON.Scene;
  private _camera: BABYLON.FreeCamera;
  private _light: BABYLON.Light;
  private _table: Table;

  constructor(canvasElement : string) {
    // Create canvas and engine
    this._canvas = <HTMLCanvasElement>document.getElementById(canvasElement);
    this._engine = new BABYLON.Engine(this._canvas, true);
  }

  createScene() : void {
      // create a basic BJS Scene object
      this._scene = new BABYLON.Scene(this._engine);

      // create a FreeCamera, and set its position to (x:0, y:5, z:-10)
      this._camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5,-10), this._scene);

      // target the camera to scene origin
      this._camera.setTarget(BABYLON.Vector3.Zero());

      // attach the camera to the canvas
      this._camera.attachControl(this._canvas, false);

      // create a basic light, aiming 0,1,0 - meaning, to the sky
      this._light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), this._scene);

      // create a built-in "sphere" shape; with 16 segments and diameter of 2
      let sphere = BABYLON.MeshBuilder.CreateSphere('sphere1', {segments: 16, diameter: 2}, this._scene);

      // move the sphere upward 1/2 of its height
      sphere.position.y = 1;

      // create a built-in "ground" shape
      let ground = BABYLON.MeshBuilder.CreateGround('ground1', {width: 6, height: 6, subdivisions: 2}, this._scene);

      this.table = new Table("my table",this._scene,5);
  }

  animate() : void {
    // run the render loop
    this._engine.runRenderLoop(() => {
        this._scene.render();
    });

    // the canvas/window resize event handler
    window.addEventListener('resize', () => {
        this._engine.resize();
    });
  }
}

window.addEventListener('DOMContentLoaded', () => {
  // Create the game using the 'renderCanvas'
  let game = new Game('renderCanvas');

  // Create the scene
  game.createScene();

  // start animation
  game.animate();
});
 

 

The new table class

export class Table extends BABYLON.Mesh{
    private _height:number;
    private _width:number;
    private _length:number;
    

    constructor(name:string, scene:BABYLON.Scene, tableHeight:number,tableWidth:number= 5,tableLength:number= 5){
        //strip of constructor values and pass to the local class properties
        super(name,scene);
        
        //this._scene = scene;
        
        this._height = tableHeight;
        this._width = tableWidth;
        this._length = tableWidth;

        //create the table top
        var box = BABYLON.Mesh.CreateBox("box", this._height, scene);
    }

}

 

Vs code error.png

Link to comment
Share on other sites

Hi Wink, to be honest didn't try the make example as it was more complicated that my current level. I did research the make command and learn a heap of stuff though :)

However the table class actually compiles to a js file fine, it's just that the game.ts cannot seem to see it.

Like you say it probably needs to be imported, so I'll try adding the class declaration to the game file. If this works, i'll plug away at figuring out how to import that file.

Thanks again

Link to comment
Share on other sites

Yes sorry meant before the game class but inside the same file.

Prefer to separate classes into respective files, but currently getting an error in chrome (on inspect element).

 

require is not defined
    at game.js:7

 

//import statement in game.ts

import {Table2} from "./Table";

 

//export in Table2 Class

export class Table2 extends BABYLON.Mesh{

...

 

 

Link to comment
Share on other sites

Thanks @wink, I think the problem could be the compiled js.

This typescript line..

import {mod} from "./Table2";

compiles to this in JS

var Table2_1 = require("./Table2")

I googled and came across this..

http://stackoverflow.com/questions/31931614/require-is-not-defined-node-js

So it looks like a browser cannot execute this line, but how else can I run the html?

 

Link to comment
Share on other sites

@PixelPush I've "figured" out a way to make a module work, please see this, hopefully someone might offer up something "better".  But I can't believe how complex the whole thing is, the best/simplest example I found was Understanding JavaScript Modules: Bundling & Transpiling by Mark Brown and with that info I was able to get it "working". Hopefully The Future that Mark mentioned is coming in the next few months.

Link to comment
Share on other sites

@Pryme8, how do you use modules you Javascript code? I had a hell of a time finding a solution but as noted above I did find something that was relatively straight forward. But I'm guessing you might have something even simpler, I'd be very interested in what you do.

Link to comment
Share on other sites

Hi, I'm sort of barging in here, please ignore if I've mixed some context, but, transpiling and bundling are two separate concerns.

The browser does not (yet) understand modules in any form so you have to use some sort of bundler to give it the functions it needs to be able to understand a module system, usually this ends up with one single file (a bundle) of all of your code, however, it is not simply a concatenation (like we used to do) because it adds some extra stuff that you can not get simply by munging files together. For example, browserify (a popular bundler) turns each module into a function which other modules (also functions) can then invoke and it keeps track of them all by generated ids, webpack works in a similar fashion.

So you need to transpile first, then create a bundle, and you only need to bundle so that the browser can understand your modules (node, for example, has a module system so you'd only need the transpile step).

Usually the way it works is that the bundler is the one in charge, you then include a transpiler inside it which runs transforms before converting into whatever format the bundler expects. If you look at the TS approved ways of working with build systems you can see how TS is included into bundlers, for example, checkout the examples for browserify and webpack (the 2 most popular bundlers currently), both include using a separate module (tsify and ts-loader respectively) which simply handles plugging TS into their bundle system. They both work by creating an AST of your code, transpiling that AST and then bundling it, so, they hit and entry point, transpile that file, then run through the dependency tree when they find a module import (usually require is expected, which is what import becomes after a transpile) they transpile that file and include it into their bundle system so that other files can find and execute that dependency.

For example, if main.ts is your entry point, and it includes (via import) table.ts, then running something like: 

browserify main.ts -p [ tsify --noImplicitAny ] > bundle.js

Will create a `bundle.js` file that includes both `main.ts` and `table.ts` (and any other dependencies they import) that the browser can execute. In browserify's case, this creates a small wrapper function that specifies how require should work and then adds a function for each file, when the browser starts executing the JS it starts at the top of `main.ts` (as you would expect) and invokes the function that `table.ts` has become, thus running that code, it then continues executing as normal et voila, you have modules in the browser.

(Note that there are many config options for ts, I've no idea what they are, so the command above may not work verbatim for you).

I'll agree its complex, but, if you want to use anything other than regular JS then this is the world we live in, at least most of the tools available now go to great lengths to hide that complexity behind a simple api, such as running just one command (as above) to handle all this complexity for you.

Link to comment
Share on other sites

Thanks @Wink i'll take a look at the project !

@mattstyles Thanks sharing some knowledge on this !

This link shows some clear ways to get your modules export and imports working. Although it doesn't fix the issue with the client browser not being able to handle require.

http://stackoverflow.com/questions/32805559/typescript-es6-import-module-file-is-not-a-module-error

Link to comment
Share on other sites

@PixelPush Do you want to write server or client code? If you're using Babylon I'm assuming client, so you'll need to learn some sort of bundler. My suggestion would be browserify, its relatively simple to dive in to but, via plugins and transforms, its ridiculously powerful (webpack is also very popular but despite being a fuller featured project—this is not always a good thing, see unix philosophy for discussion—it requires a metric ton of confusing config, horses for courses really, both will do the job).

The following, for example, will create a bundle for the browser, having passed it through typescript (I'm far from a ts expert, not a fan, but you wouldn't have to be for this terse trivial example!):

* Install dependencies

npm init
npm i -D browserify tsify typescript

* Create files

touch main.ts user.ts
// main.ts

import user from './user'

console.log('Hello', user)
// user.ts

export default 'PixelPush'

* Run your build script

browserify -p tsify main.ts > bundle.js

Hopefully you can get a good idea what would happen when you whack bundle.js into a browser. The process is fairly straight forward (build systems should always strive for simplicity, as any good system should), install the dependencies you need, in this case you need a bundler (browserify) which is responsible for allowing you to use a module system and a transpiler, in this case that is typescript, the extra dep we've included is a module that glues typescript in to browserify (basically it just runs typescript over each file you import, sometimes they do a bit more, for instance, if you needed polyfills they could be used to include those only once into a bundle, rather than for the each file), most of the time plugins come bundled with a transpiler (if they need it), but, in the case of the typescript one it also needs the actual ts module, hence 3 deps.

Next you create an entry file, this is where your code will start executing, everything flows from this point. You can have multiple of these if you want for creating multiple builds, but now you're adding complexity and mostly you don't need this.

The import instruction literally turns into an `var user = 'PixelPush'`, as you can probably guess, in reality there is a little more going on but the only point is that the import grabs the export from the named file, which is referenced by file path. Browserify handles the pathing, as the bundler this is part of its job.

Under the hood when browserify runs its process it knows to start parsing the dependency, user.ts, and it will run typescript over that file before stuffing it in to the bundle.

Once browserify has run, and it'll throw errors at you from either itself or from its plugins (as I found out when I realised ts modules are ever so slightly different from spec), you should have a shiny new bundle.js file which is ready for the browser to understand.

Go ahead, have a look at bundle.js, built and transpiled code isn't really human-friendly but our example is so terse you should see what is happening. Browserify adds a small chunk at the top, which wraps everything in a closure (to keep things away from global by default) and adds a function so that we can use those `require` functions you see in the file. After a bit of jiggery-pokery it stuffs each file in the dependency tree into an object as a function, this allows it to invoke each function to run that dependency. If you look close enough you'll see it passes in a dependency map so that code like `require('./user')` can get mapped to the big object where it keeps dependencies. All module bundlers I've seen work in roughly this fashion.

Hope it helps you get your chops with modules! It's worth this initial pain!! Also note that this is coming natively in the browser so it's worth going through the learning of how it works (browser api should match up but how it works is significantly more complex, hence why it doesn't yet exist).

Link to comment
Share on other sites

Ah, no idea, tentatively it was scheduled for Q2 this year (I think), which never happened. Usually Chrome supports stuff first, sometimes Firefox, although, to be fair, Edge has been really snappy on the uptake.

It's been moved from es2016 to es2017 spec, although I'm not sure what stage it is at, its still on the TC39 agendas though so stuff is happening. Of course timescales from TC39 to browser implementation can be sketchy, some things are far easier to implement than others and modules clearly falls in the 'amazingly difficult' category.

Link to comment
Share on other sites

On 12/23/2016 at 8:25 PM, Wink said:

@Pryme8, how do you use modules you Javascript code? I had a hell of a time finding a solution but as noted above I did find something that was relatively straight forward. But I'm guessing you might have something even simpler, I'd be very interested in what you do.

Think of a module as a named function that returns a object with its own variable scope.

so

someCoolProcess = function(args){
//How to control defaults:
args = args || {};
this.setting1 = args.setting1 || "default";
.. other code ...
return this
};

coolProcess = new someCoolProcess();
//then to see that your object formatted correctly
console.log(coolProcess); or console.log(JSON.stringify(coolProcess));


With this being a named function now it gets declared on the scope of the document and returns a unique thread basically when ever the function is called.  All variables inside that function are only accessible by that function.
Now this is where the fun begins though, is you pass your scope between objects.  So now you have come into the situation where you want to add secondary function that holds specific information and functions that are dependent on the object that called it (like some transforms or something) you would make a secondary function and in its construction stick a parent var so:

someOtherFunc = function(args, parent){
this.parent = parent;
...other code...
this.settingA = this.parent.setting1; //Now setting A was able to be set by the parent object that called it.

};

so in our someCoolProccess after we set the args we would so something like:
this.otherCoolStuff = new someOtherFunc({}, this);

Once you understand scope passing and when to reference vs when to prototype you can make what ever happen and fast.

Sick of programing if and then elses? let your for and while loops handle it with some nifty Object.keys() looping.
 

Link to comment
Share on other sites

On 27/12/2016 at 8:18 PM, Wink said:

So I want to use ES6 module syntax, I'm leaning towards using jspm opinions on that choice for my package manager?

It's very good, far less supported than Browserify or Webpack though, and less mature.

If you want the fastest way in, choose Browserify. It does not understand ES6 modules by default, it requires a transpile phase, usually via babel but this is trivial to set up and gives you access to all the other goodies that both browserify and babel give you. Webpack is identical in nature, although requires much more config to do the same things. Both are extremely popular and well used with a wealth of information out there, along with a huge number modules, plugins and presets.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...