A rails 6 application calls from a legacy application being migrated:
<%= javascript_pack_tag 'zxing.js' %>
<script type="text/javascript">
window.addEventListener('load', function () {
let selectedDeviceId;
const codeReader = new ZXing.BrowserMultiFormatReader()
console.log('ZXing code reader initialized')
codeReader.listVideoInputDevices()
.then((videoInputDevices) => {
const sourceSelect = document.getElementById('sourceSelect')
selectedDeviceId = videoInputDevices[0].deviceId
if (videoInputDevices.length >= 1) {
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement('option')
sourceOption.text = element.label
sourceOption.value = element.deviceId
sourceSelect.appendChild(sourceOption)
})
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
const sourceSelectPanel = document.getElementById('sourceSelectPanel')
sourceSelectPanel.style.display = 'block'
}
document.getElementById('startButton').addEventListener('click', () => {
codeReader.decodeFromVideoDevice(selectedDeviceId, 'video', (result, err) => {
if (result) {
console.log(result)
document.getElementById('result').textContent = result.text
let formData = new FormData();
let CodeParams = {
code_data: result.text,
shopkeeper_id: <%= current_user.id %>
};
formData.append("code_json_data", JSON.stringify(CodeParams));
$.ajax({
url: "new_moviment",
type: "post",
data: formData,
processData: false,
contentType: false,
});
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err)
document.getElementById('result').textContent = err
}
})
console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
})
document.getElementById('resetButton').addEventListener('click', () => {
codeReader.reset()
document.getElementById('result').textContent = '';
console.log('Reset.')
})
})
.catch((err) => {
console.error(err)
})
})
The javascript is loading, but the script for the page is not Uncaught ReferenceError: ZXing is not defined
THe ZXing documentation does provide different code bases according to whether one is Use on browser with ES6 modules
or AMD
, UMD
, or CommonJS
.
I am under the impression that with the move to webpacker, Rails is pre-processing under ES6. But that impression is confused by the fact that there is browser-based code and module-based code.
How should this script be edited (and are there - for JS-knotheads - online tools to allow to identify and change syntax for ES6 ?)
Update
After a yarn add @zxing/library
packs/application.js edited to
require("@zxing/library")
import("../src/qr_scan")
src/qr_scan.js is now as follows:
import { BrowserMultiFormatReader, NotFoundException } from '@zxing/library';
window.addEventListener('load', function () {
let selectedDeviceId;
const codeReader = new BrowserMultiFormatReader() // note the updated module reference
console.log('ZXing code reader initialized')
codeReader.listVideoInputDevices()
.then((videoInputDevices) => {
const sourceSelect = document.getElementById('sourceSelect')
selectedDeviceId = videoInputDevices[0].deviceId
if (videoInputDevices.length >= 1) {
videoInputDevices.forEach((element) => {
const sourceOption = document.createElement('option')
sourceOption.text = element.label
sourceOption.value = element.deviceId
sourceSelect.appendChild(sourceOption)
})
sourceSelect.onchange = () => {
selectedDeviceId = sourceSelect.value;
};
const sourceSelectPanel = document.getElementById('sourceSelectPanel')
sourceSelectPanel.style.display = 'block'
}
document.getElementById('startButton').addEventListener('click', () => {
codeReader.decodeFromVideoDevice(selectedDeviceId, 'video', (result, err) => {
if (result) {
console.log(result)
document.getElementById('result').textContent = result.text
let formData = new FormData();
let CodeParams = {
code_data: result.text
};
formData.append("code_json_data", JSON.stringify(CodeParams));
$.ajax({
url: 'new_user',
type: "post",
data: formData,
processData: false,
contentType: false,
});
}
if (err && !(err instanceof ZXing.NotFoundException)) {
console.error(err)
document.getElementById('result').textContent = err
}
})
console.log(`Started continous decode from camera with id ${selectedDeviceId}`)
})
document.getElementById('resetButton').addEventListener('click', () => {
codeReader.reset()
document.getElementById('result').textContent = '';
console.log('Reset.')
})
})
.catch((err) => {
console.error(err)
})
})
and the view page has no more javascript references. Loading the page however leads to the following error in the console:
Uncaught TypeError: _zxing_library__WEBPACK_IMPORTED_MODULE_0__.BrowserMultiFormatReader is not a constructor
pointing to codeReader
line. The page was processed with webpacker attempting to compile.
Although js/zxing-3544fb3b633f87715d29.js 528 KiB zxing [emitted] [immutable] zxing
is successfully built ([./app/javascript/packs/zxing.js] 524 KiB {zxing} [built]
), both the import functions failed:
ERROR in ./app/javascript/src/qr_scan.js 4:23-47
"export 'BrowserMultiFormatReader' was not found in '@zxing/library'
@ ./app/javascript/packs/application.js
ERROR in ./app/javascript/src/qr_scan.js 47:36-53
"export 'NotFoundException' was not found in '@zxing/library'
@ ./app/javascript/packs/application.js
searches on those strings in the zxing compiled file only reveal two instances:
e.BrowserMultiFormatReader = s;
e.NotFoundException = l["default"];
leading naturally to a console error:
window.addEventListener('load', function () {
var selectedDeviceId;
var codeReader = new _zxing_library__WEBPACK_IMPORTED_MODULE_0__["BrowserMultiFormatReader"](); // note the updated module reference
Webpacker finally compiled when reverting to the library's documentation referenced command
const codeReader = new BrowserQRCodeReader() // note the updated module reference
// const codeReader = new BrowserMultiFormatReader() // note the updated module reference
but now, there is a console error:
Uncaught ReferenceError: BrowserQRCodeReader is not defined
pointing to var codeReader = new BrowserQRCodeReader();
JavaScript modules in webpack are not exported to the global scope, i.e., when using Webpacker with Rails, you can't reference something like ZXing
from a <script>
tag in an ERB template by default.
Instead, you would move all your JavaScript to the app/javascript
directory and update your ZXing (and other library) references to use module imports.
Rails will treat everything in the "packs" folder as a separate bundle. Start with just an application pack that points to your source code:
app/
javascript/
packs/
application.js
src/
my_barcode.js
// app/javascript/packs/application.js
import '../src/my_barcode'
// app/javascript/src/my_barcode.js
// Based off the module imports described in the ZXing README
import { BrowserMultiFormatReader, NotFoundException } from '@zxing/library/esm';
window.addEventListener('load', function () {
let selectedDeviceId;
const codeReader = new BrowserMultiFormatReader() // note the updated module reference
// and so on ...
And in your application layout:
<%= javascript_pack_tag 'application' %>