0

I am a complete newbie in web development, and I am completely lost.

I have downloaded the Angular Quickstart used in the Angular tutorial (https://github.com/angular/quickstart) and now I want to insert a simple x3d element. To do so, I have modified the files app.module.ts, app.component.ts and index.html.

The modified file app.module.ts is:

import { NgModule, NO_ERRORS_SCHEMA }      from '@angular/core';`
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent ],
  schemas:      [ NO_ERRORS_SCHEMA ],
  bootstrap:    [ AppComponent ]
})

export class AppModule { }

The new app.component.ts that I have created is:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `<x3d width='600px' height='400px'> 
                <scene> 
                    <shape>     
                        <appearance> 
                            <material diffuseColor='1 0 0'></material>
                        </appearance>       
                        <box></box> 
                    </shape> 
                </scene> 
             </x3d>`,
})
export class AppComponent  { name = 'Angular'; }

And finally, the new index.html looks like that:

<!DOCTYPE html>
<html>
  <head>
    <title>Angular QuickStart</title>
    <base href="/">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="styles.css">

    <!-- Polyfill(s) for older browsers -->
    <script src="node_modules/core-js/client/shim.min.js"></script>

    <script src="node_modules/zone.js/dist/zone.js"></script>
    <script src="node_modules/systemjs/dist/system.src.js"></script>
    <script type='text/javascript' src='http://www.x3dom.org/download/x3dom.js'> </script> 

    <script src="systemjs.config.js"></script>
    <script>
      System.import('main.js').catch(function(err){ console.error(err); });
    </script>
  </head>

  <body>
    <my-app>Loading AppComponent content here ...</my-app>
  </body>
</html>

When I run $ npm start, nothing is shown, and the browser does not raise any errors, and I cannot figure out why. I am using Angular 4. Any help to solve this issue would be appreciated, thanks.

GLR
  • 1,070
  • 1
  • 11
  • 29
  • To allow Angular to render elements that do not exists in the module, you'll have to pass on a schema to allow custom elements. Import `CUSTOM_ELEMENTS_SCHEMA` from `@angular/core` and add it to the schemas array of your Angular module. And try to load x3d before Angular runs. – Shane May 30 '17 at 10:52
  • I have tried to import `CUSTOM_ELEMENTS_SCHEMA`, but doing so leads the browser to raise a lot of errors (it complains about unknown elements refering to `x3d`, `material`, `appearance` ...). That is why I have imported `NO_ERRORS_SCHEMA`. – GLR May 30 '17 at 11:04
  • Sorry, I am a complete newbee to web development. How would I make the browser to load the x3d before the Angular application is run? I suspect that this is the problem, as you have suggested. – GLR May 30 '17 at 11:06
  • 1
    You can add multiple schemas, hence it is an array. Apparently x3d checks for x3d elements once the script, when the script is imported and using a selfcalling function, and the app component is loaded after due to compiling. It queries for all x3d elements and in my case I was wrong, you will have to load x3d **after** your angular component has rendererd. I will post the solution in an answer for more character space. – Shane May 30 '17 at 12:10

2 Answers2

1

The problem is that X3D checks for x3d elements when the script loads and because you're loading an Angular component which holds the x3d element, X3D can not find your element. Unfortunately X3D does not provide such method to check the DOM for (new) x3d elements.

I see that you're using Systemjs, you can use it easily load scripts during the load of your appcomponent. You will have to modify the configuration file for Systemjs and add an import statement in your component to load the X3D library. Best practice would be to load the library from the node modules. $ npm install --save x3dom

Systemjs.config:

(function (global) {
  System.config({
    paths: {
      // paths serve as alias
      'npm:': 'node_modules/'
    },
    // map tells the System loader where to look for things
    map: {
      // our app is within the app folder
      'app': 'app',

      // angular bundles
      ...

      // other libraries
      ...
      // load x3d from node modules
      ,'x3dom': 'npm:x3dom/x3dom.js'
    },
    // packages tells the System loader how to load when no filename and/or no extension
    packages: {
      ...
    }
  });
})(this);

To load the library with your component you can add an constructor to the class of the component and call an importstatement for x3d using Systemjs.

App.component

import { Component } from '@angular/core';

// simple declaration to prevent the tscompiler from causing an compiling error
declare var System : any;

@Component({
  selector: 'my-app',
  template: `<x3d width='600px' height='400px'>
                <scene>
                    <shape>
                        <appearance>
                            <material diffuseColor='1 0 0'></material>
                        </appearance>
                        <box></box>
                    </shape>
                </scene>
             </x3d>`,
})
export class AppComponent  {
  name = 'Angular';
  constructor() {
    System.import('x3dom')
      .then(() => {
        console.log('loaded');
      })
      .catch((e: any) => {
        console.warn(e);
      })
  }
}

Now everytime you bootstrap your app, Systemjs will try to import the x3dom library. And don't forget to remove the import of the x3dom library in your index.html, the library checks if window.x3dom already exists.

Shane
  • 3,049
  • 1
  • 17
  • 18
  • I have found a new problem. When I have a component 'A' like the one you described above which is accessed via the Angular routing, the first time that I access 'A', the X3D is rendered appropriately. However, the second time I access 'A', the X3D is not rendered until I refresh the website (pressing F5) . I think that this is due to the fact that the x3d.js is cached or something like that. Do you have any ideas on that? Should I open a new issue describing the problem with more detail? Thanks and sorry for my ignorance. – GLR May 31 '17 at 08:12
  • 1
    No, the problem is that you do not actually route but load new innerhtml for the element where routing is expected. Thus a reference to the X3D library is still active. You would have to reload the script by removing the library and reimport the library. I think it is easiest do `window.x3dom = undefined;` before importing, but that isn't really good practice. In my opinion it is a bad design of the X3D team to not implement such a method to reinitialize the library for all x3d elements. – Shane May 31 '17 at 08:24
  • You are right, before accessing 'A' `window.x3dom` is `undefined`, and after that it is defined. However, if I do `window.x3dom = undefined;` after that, the console raises an error continously because the application cannot access some properties of `undefined` (`getStyle` and `caps`), so I think that I should delete something more. Any ideas on that? – GLR May 31 '17 at 09:24
  • I fixed the problem by doing `window.x3dom.reload()` when `window.x3dom`is `undefined`. – GLR May 31 '17 at 10:29
  • If I use the X3D tag `inline` instead of defining the box in the template itself, something really strange happens. Every time that I refresh the website, the first time that I access the component 'A', the scene is empty. However, the subsequent times that I access this component via routing, the box is rendered inside the scene. Any ideas about this? Should I post a new question about this with an example code? – GLR Jun 01 '17 at 14:12
  • I can try to look at it for you later today, though I should add I am not formiliar with the X3D library. If you can host your example in a glitch, codepen, jsbin or plunkr I can try to take a go at it. – Shane Jun 01 '17 at 14:44
  • Thank you so much. I will ask a new question with the more important parts of the code to show the problem. I have tried to upload my code to plunker, but it is something very tedious as I need to upload a lot of files one by one. – GLR Jun 01 '17 at 15:59
  • I have added the relevant code here: https://stackoverflow.com/questions/44312860/angular-and-x3d-integration-strange-behaviour – GLR Jun 01 '17 at 16:34
0

Old thread but will add this for anyone in the future:

Adding a parent div with an *ngIf seems to stop the compiler complaining, I am not sure why but there it is. It can even be *ngIf="true".

<div *ngIf="true">
    <x3d>
        <scene>
            <box size='1 1 1'></box>
        </scene>
    </x3d>
 </div>

Adding x3dom's CDN to my index.html and using this method was all I needed to do to get it to render.