Ngx-scanner: "MediaDeviceInfo is not defined" on secure server

Created on 25 Feb 2020  路  21Comments  路  Source: zxing-js/ngx-scanner

Bug
When trying to serve on our secure (HTTPS & SSL ready) test environment we鈥檙e getting the following errors:

/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:431535
        Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__metadata"])("design:type", MediaDeviceInfo),
                                                                                ^

ReferenceError: MediaDeviceInfo is not defined
    at /Users/thisisme/Documents/Brand/Client/Project/dist/server.js:431535:81
    at Module.Object.defineProperty.value (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:431572:2)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:20:30)
    at Object.@zxing/ngx-scanner (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:310014:18)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:256388:30)
    at Object../node_modules/@zxing/ngx-scanner/zxing-ngx-scanner.ngfactory.js (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:264790:10)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:256388:30)
    at Object../src/app/barcode-scanner/barcode-scanner.component.ngfactory.js (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:296178:10)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:256388:30)
    at Object../src/app/quick-order/quick-order.component.ngfactory.js (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:305426:10)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:256388:30)
    at Object../src/app/app.server.module.ngfactory.js (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:295997:11)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:256388:30)
    at Object../src/main.server.ts (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:309469:37)
    at __webpack_require__ (/Users/thisisme/Documents/Brand/Client/Project/dist/server.js:256388:30)

The outcome of this, is that it won鈥檛 load the webpage at all (can鈥檛 handle request).

To Reproduce
We're running our pipelines via Azure, the projects gets pre-build with the right environment credentials, then deployed to our secure environment. I get that this is a little hard to reproduce, but upon reviewing this bug I came across one argument/solution; "Server is not secure, make sure it is". So I'm kinda out of options as far as online research goes.

Expected behavior
A working barcode scanner 馃帀

Additional context
As I said our building approach is a little bit different/complex than most. But I hope we can come up with a solution.

Most helpful comment

We are running into the same issue. The app would be able to run a SSR build if we just import the module without declaring the ZXingScannerComponent.

Once we use ZXingScannerComponent the build crashes with the same error.
As we used ZXingScannerComponent for typing of the ViewChild only, we switched it to any and the build would complete successfully.

All 21 comments

Hi @royvanderwesten,

I am also facing same issue, but trying to serve on insecure test environment. Have you found any solution?

Thanks,
Sudhakar.

Hi @royvanderwesten,

I am also facing same issue, but trying to serve on insecure test environment. Have you found any solution?

Thanks,
Sudhakar.

You should be able to serve on a secure test environment. And no, I haven't found a solution yet for my case.

Hi @royvanderwesten,
I am also facing same issue, but trying to serve on insecure test environment. Have you found any solution?
Thanks,
Sudhakar.

You should be able to serve on a secure test environment. And no, I haven't found a solution yet for my case.

Thanks for the quick response and I will try it out in a secured test environment.
Hope you will find the solution ASAP for your use case.

Thanks,
Sudhakar.

@odahcam bump

@royvanderwesten you get that this is not a bug with this library right?, I think you should provide at least browser info here so we could TRY to help. I cannot solve this for ya, sorry. I'll do my best to help as you need.

@odahcam Well the only threads I find when searching on this specific error leads to this library, and since I'm using it, my best guess is that this library has something to do with it. What browser info do you need? It won't load on any browser because it won't serve when building a prod version because of this error.

Appreciate your help man 馃憤

Actually it may be a bug with the library, are you using the UMD approach in your pipeline?

Actually it may be a bug with the library, are you using the UMD approach in your pipeline?

@odahcam Let me check that since I'm not the one that built the pipeline, when I know I'll get back to you!

Well, I don't think you understood, the library code is fine, and it works in both browser and server. You getting a common error you can only get when using the browser decoding classes, but you didn't mention how you are using the library, so I thought you were using something like phantomJS to test a webpage on your pipeline, so it would make sense, but it looks like you're using no browser (馃槙). Could you explain how you are using the library in your project?

Could you explain how you are using the library in your project?

Sure! You are right that the library is fine, the barcode scanner works as it should running locally so no problems there.

I'm using the barcode scanner to search for EAN codes in CommerceTools, I have the component with the barcode scanner which then fires the output event with the EAN as payload. So nothing fancy there.

This is the component.ts file:

import { OnInit, Component, ViewChild, Output, EventEmitter } from '@angular/core';
import { ZXingScannerComponent } from '@zxing/ngx-scanner';

@Component({
  selector: 'app-barcode-scanner',
  templateUrl: './barcode-scanner.component.html',
  styleUrls: ['./barcode-scanner.component.scss']
})
export class BarcodeScannerComponent implements OnInit {

  @ViewChild('scanner')
  scanner: ZXingScannerComponent;
  @Output() scanSuccess = new EventEmitter<string>();

  hasCameras = false;
  hasPermission: boolean;

  availableDevices: MediaDeviceInfo[] = null;
  selectedDevice: MediaDeviceInfo = null;

  ngOnInit(): void {
    this.scanner.camerasFound.subscribe((devices: MediaDeviceInfo[]) => {
      this.hasCameras = true;
      this.availableDevices = devices;
    });

    this.scanner.camerasNotFound.subscribe((devices: MediaDeviceInfo[]) => {
      console.error('An error has occurred when trying to enumerate your video-stream-enabled devices.');
    });

    this.scanner.permissionResponse.subscribe((answer: boolean) => {
      this.hasPermission = answer;
    });
  }

  scanSuccessHandler(event) {
    this.scanSuccess.emit(event);
  }
}

The library is installed in the app.module as described in your wiki.

I just ran a normal ng build, that worked fine, but as I try to run node dist/server it gives me the error.

@odahcam Quick update, it happens when using the server side rendering (run serve:ssr).

Solution

When using this library and trying to serve:ssr, you can add the global variable to your _server.ts_ file.

This is what that piece of code looks like: global['MediaDeviceInfo'] = null;.

Understood, so you can't always run something on server-side and expect browser APIs to be available, even in Angular SSR you must avoid running some pieces of code and, for that, you can use the isPlatformBrowser or isPlatformServer built-in funcions. I still a little confused about why this happened to you, if I found something I will update this issue's status.

Solution

When using this library and trying to serve:ssr, you can add the global variable to your _server.ts_ file.

This is what that piece of code looks like: global['MediaDeviceInfo'] = null;.

This did not work for me:

import 'zone.js/dist/zone-node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';

import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
import * as cookieParser from 'cookie-parser';
import * as xhr2 from 'xhr2';

const Event = null;
const MediaDeviceInfo = null;
global['Event'] = null;
global['KeyboardEvent'] = null;
global['Element'] = {};
global['Element'].prototype = {};
global['Element'].prototype.remove = undefined;
global['MediaDeviceInfo'] = null;

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';

  xhr2.prototype._restrictedHeaders = {};

  server.use(cookieParser());

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // app.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run() {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

Still the same error...

@ThePatzen Try to delete const MediaDeviceInfo = null;, the error triggered on this aswell (in my project).

@ThePatzen Try to delete const MediaDeviceInfo = null;, the error triggered on this aswell (in my project).

Hi!

@royvanderwesten thanks! But still the same error.

        Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__metadata"])("design:type", MediaDeviceInfo),
                                                                                ^

ReferenceError: MediaDeviceInfo is not defined
import 'zone.js/dist/zone-node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';

import { AppServerModule } from './src/main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
import * as cookieParser from 'cookie-parser';
import * as xhr2 from 'xhr2';

const Event = null;
global['Event'] = null;
global['KeyboardEvent'] = null;
global['Element'] = {};
global['Element'].prototype = {};
global['Element'].prototype.remove = undefined;
global['MediaDeviceInfo'] = null;

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
  const server = express();
  const distFolder = join(process.cwd(), 'dist/browser');
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index';




  xhr2.prototype._restrictedHeaders = {};

  server.use(cookieParser());

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule,
  }));

  server.set('view engine', 'html');
  server.set('views', distFolder);

  // Example Express Rest API endpoints
  // app.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }));

  // All regular routes use the Universal engine
  server.get('*', (req, res) => {
    res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
  });

  return server;
}

function run() {
  const port = process.env.PORT || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run();
}

export * from './src/main.server';

@ThePatzen What does your component.ts file look like?

@royvanderwesten i just use it in the html part of my component

<zxing-scanner *ngIf="!isApp && platform.isBrowser"
        [formats]="['QR_CODE']"
        (camerasFound)="camerasFoundHandler($event)"
        (camerasNotFound)="camerasNotFoundHandler($event)"
        (scanSuccess)="scanSuccessHandler($event)"
        (scanError)="scanErrorHandler($event)"
        (scanFailure)="scanFailureHandler($event)"
        (scanComplete)="scanCompleteHandler($event)"
      ></zxing-scanner>

@ThePatzen Shouldn't you define the scanner somewhere? Like my comment of 8 days ago?

There is a Plunkr of how this is setup. I have done that and deleted the, for me, unnecessary bit. I recon you should try that. The most important part is that you should include:

availableDevices: MediaDeviceInfo[] = null;
selectedDevice: MediaDeviceInfo = null;

Beyond that, check if you're using MediaDeviceInfo elsewhere in your project and make sure you assign null. That's the best I can do for you now.

We are running into the same issue. The app would be able to run a SSR build if we just import the module without declaring the ZXingScannerComponent.

Once we use ZXingScannerComponent the build crashes with the same error.
As we used ZXingScannerComponent for typing of the ViewChild only, we switched it to any and the build would complete successfully.

The behavior @mt-webdev described is not the expected, some action will be taken to fix that.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ngeor picture ngeor  路  4Comments

werthdavid picture werthdavid  路  4Comments

ramdanegie picture ramdanegie  路  6Comments

mhosman picture mhosman  路  4Comments

fitness23 picture fitness23  路  5Comments