Hopefully I can explain this clearly.
I have a Blazor WASM project that is referencing a Razor library (everything fine here).
The Razor library compiles a JavaScript bundle using webpack (everything works here).
One of the components I am trying to create has one simple function that returns the revesion of a package (threejs) as a string.
Now the problem.
When I try to call the function from the Blazor project I keep receiving that the function does not exist.
Error
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Atom.Web.Client</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="Atom.Web.Client.styles.css" rel="stylesheet" />
</head>
<body>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
Reload
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="./_content/Atom.Web.Viewer.Components/js/atom.bundle.js"></script>
</body>
</html>
Webpack config
const path = require("path");
module.exports = {
devtool: 'source-map',
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
},
output: {
path: path.resolve(__dirname, '../wwwroot/js'),
filename: "atom.bundle.js",
library: "Atom"
}
};
the component code
private readonly Lazy<Task<IJSObjectReference>> moduleTask;
public ThreeViewer(IJSRuntime jsRuntime)
{
moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
"import", "./_content/Atom.Web.Viewer.Components/js/atom.bundle.js").AsTask());
}
public async ValueTask<string> GetRevision()
{
var module = await moduleTask.Value;
var rev = await module.InvokeAsync<string>("Atom.GetCurrentThreeRevision");
return rev;
}
the Program.cs
using Atom.Web.Client;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Atom.Web.Viewer.Components;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<ThreeViewer>();
await builder.Build().RunAsync();
the Index.razor
#inject IJSRuntime JSRuntime;
#inject ThreeViewer threeViewer;
#page "/"
#using Newtonsoft.Json
<PageTitle>Index</PageTitle>
#if(revisionMessage != string.Empty)
{
<h1>#revisionMessage</h1>
}
<SurveyPrompt Title="How is Blazor working for you?" />
#code {
string revisionMessage = string.Empty;
protected override async Task OnInitializedAsync()
{
revisionMessage = await JSRuntime.InvokeAsync<string>("Atom.GetCurrentThreeRevision");
var rev = await threeViewer.GetRevision();
}
}
The interesting part is, if I call the function directly using jsruntime revisionMessage it works fine, if I try to call it from the component library, it doesn't.
Anyone can be of any help?
Ok I have figured this out myself.
I had to remove dynamic loading of the library within the Razor component and assume that this library will be loaded in the main Blazor app.
public async Task<string> GetRevision()
{
return await JSRuntime.InvokeAsync<string>("Atom.GetCurrentThreeRevision");
}
public async Task<Scene> InitiateScene()
{
string text = JsonConvert.SerializeObject((object)new { Scene, ViewerSettings, Camera, OrbitControls }, SerializationHelper.GetSerializerSettings());
return await JSRuntime.InvokeAsync<Scene>("Atom.InitiateScene");
}
everything else remains the same basically.
Related
I'm currently trying to get started using webpack and ran into "Uncaught ReferenceError: printMe is not defined" error when trying to call my "init" method from HTML using the onload tag despite exporting the methods. My files are as follows:
print.js (located in /resources/static):
export function printMe() {
console.log("Hi");
}
index.js (located in /resources/static):
import {printMe} from "./print";
export function component() {
const element = document.createElement('div');
const btn = document.createElement('button');
btn.innerHTML = 'Click me and check the console!';
btn.onclick = printMe;
element.appendChild(btn);
return element;
}
export function init() {
console.log("init ran");
}
document.body.appendChild(component());
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './resources/static/index.js',
},
devtool: 'inline-source-map',
plugins: [
new HtmlWebpackPlugin({
title: 'Output Management',
}),
],
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean:true,
},
};
index.html (located in /dist):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Output Management</title>
<meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="index.bundle.js"></script></head>
<body onload="init()">
</body>
</html>
index.bundle.js (located in /dist) I'm not going to include this file as I don't believe it to be necessary to the solution, although is important to note that it is actually there. Any help is appreciated
Even though you say export, I think that webpack isn't including it in your bundle since you never use the function in the JavaScript code. You'll either need a plugin to monitor your HTML, or use addEventListener on the body to add an "onLoad" event handler.
The simplest way to do this is with the event listener (don't include parentheses after init; see Why is the method executed immediately when I use setTimeout?):
document.body.addEventListener('load', init);
while using the ES6 module language in nodejs it gives me an error of
" IMPORTS CANNOT BE USED OUTSIDE THE MODULE" ERROR IN CHROME BROWSER.
I am trying to build my project using Node js express mongoose morgan express-handlebars and ES6
but while I run the project it gives me an error for the same
I tried using .babelrc and webpack.config.js but not able to resolve it.
can anyone help me to achieve it?
I am putting images of my project for your reference.
Thank You
enter image description here
enter image description here
Babelrc:
{
"presets": [
["#babel/env", {
"useBuiltIns": "usage",
"corejs": "3",
"targets": {
"browsers": [
"last 5 versions",
"ie >= 8"
]
}
}]
]
}
webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: ['./index.js'],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/bundle.js'
},
devServer: {
contentBase: './dist'
},
plugins: [
new HtmlWebpackPlugin({
title: 'Custom template using Handlebars',
filename: 'index.html',
template: 'main.hbs'
})
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
],
loaders: [
{ test: /\.hbs$/, loader: "handlebars-loader" }
]
}
};
main.js:
enter code here
import Search from './models/search';
import Movie from './models/Movie'
import User from './models/user'
import * as searchView from './views/searchView'
import * as movieView from './views/movieView'
import { elements , renderLoader, clearLoader } from './views/base'
const state = {};
const controlSearch = async () => {
// const query = searchView.getInput();
const query = 'avengers';
if (query) {
searchView.clearInput();
searchView.clearResult();
state.search = new Search(query);
state.user = new User();
searchView.clearInput();
searchView.clearResult();
renderLoader(elements.searchRes);
await state.search.getResult();
await state.user.userSignUp();
clearLoader();
console.log(state.search.result);
searchView.renderResults(state.search.result);
}
};
elements.searchForm.addEventListener('submit', e => {
e.preventDefault();
controlSearch();
});
main.hbs
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Movie Recherer</title>
<link rel="stylesheet" href="/css/bootstrap.min.css"/>
<link rel="stylesheet" href="/css/all.min.css"/>
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/home.css" />
</head>
<body>
<div class="container">
<div class="row">
{{{body}}}
</div>
</div>
<script src="/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script language="javascript" src="/js/main.js"></script>
<script language="javascript" src="/js/models/Movie.js"></script>
<script language="javascript" src="/js/models/search.js"></script>
<script language="javascript" src="/js/views/base.js"></script>
<script language="javascript" src="/js/views/movieView.js"></script>
<script language="javascript" src="/js/views/searchView.js"></script>
</body>
</html>
You need to include you import inside a module:
Try this:
<script type="module"> import btn from './helper' </script>
Add type="module" in every script tag of your main.hbs file
Apologies if this question was answered, I tried searching but couldn't find what I'm looking for.
I want to build one .mjs file that has a library class code that I can use on both browser and Node.js. I don't want to use browsify or any 3rd party library. I want to use native JS Modules (.mjs). I'm willing to use Node 13.x so I completely replace my require statements with import and running experimental mode.
Here is an example, I want to use "node-fetch" for NodeJS and the native fetch API for the browser. So I called my variable "fetch". But I want to do an if statement so that if I'm running in the browser I can just use the native fetch, but If I'm in Node, I want to load the fetch api from node-fetch package.
fetch.mjs -shared code
//if node then do this.. else fetch will be defined in the browser
import fetch from "node-fetch"
export class AwesomeClass {
constructor(url)
{
this.url= url;
}
load () {
return fetch(this.url);
}
getAwesome() {}
}
index.mjs Node JS Client
import { AwesomeClass } from "./fetch.mjs"
const a = new AwesomeClass();
index.html browser Client
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script type = 'module'>
import { AwesomeClass } from "./fetch.mjs"
const a = new AwesomeClass();
</script>
</body>
</html>
Thanks to #Heretic Monkey for introducing me to dynamic import I was able to write a single .mjs file that works on both browser and node js. Here is the code for those interested.
It actually forced me to only load the fetch when I need it.
fetch.mjs library
export class AwesomeClass {
constructor(url)
{
this.url= url;
}
load () {
return import ("node-fetch").then(nodeFetch => nodeFetch.default(this.url)).catch(e=> fetch(this.url))
}
getAwesome() {}
}
index.mjs (node.js client)
import {AwesomeClass} from './fetch.mjs'
const ac = new AwesomeClass("https://api.github.com");
(async () => {
try{
const res = await ac.load();
console.log(await res.json());
}
catch(e)
{console.error(e)}
})()
index.html (browser)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script type = 'module'>
import {AwesomeClass} from './fetch.mjs'
const ac = new AwesomeClass("https://api.github.com");
(async () => {
try{
const res = await ac.load();
console.log(await res.json());
}
catch(e)
{console.error(e)}
})()
</script>
</body>
</html>
const lang = "the lang html attribute value";
var languages = {
"fr": require("./languages/fr.json"),
"en": require("./languages/en.json")
};
var configuration = Object.keys(languages).map(function(language) {
const loadedLang = (lang.indexOf("fr") > -1) ? "fr" : "en";
return {
name: loadedLang,
entry: './assets/js/main.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'assets/js/dist')
},
plugins: [
new I18nPlugin(
languages[loadedLang]
)
]
}
});
So I am trying to get the <html lang="en-US"> lang attribute and I know that I have no access to the DOM from the webpack config file.
My question is:
Does someone have an idea about how to do this or how to have access to the local storage from webpack?
PS:
I tried a node-localstorage package but it doesn't do the job I want
An answer to your question would be to load the file with the fs api and then use an regex to extract your language:
const fs = require('fs');
const content = fs.readFileSync('PATH_TO_INDEX.HTML', options: { encoding: 'utf8' });
const lang = content.match(/<html lang="(.*)"/)[1];
But I would recommend to do it the "webpack" way:
Create a separate folder for every language, that contains the index.html and the js file specific to each language language.
For this, add the following:
output: {
path: path.resolve(__dirname, `assets/js/dist/${language}/`)
},
plugins: [
new HtmlWebpackPlugin({
template: 'PATH_TO_YOUR_TEMPLATE',
lang: language,
title: 'your page title',
})
]
Please don't forget to install the html-webpack-plugin and require it at the top of your config.
And create a new file index.html with the following content:
<!DOCTYPE html>
<html lang="<%= htmlWebpackPlugin.options.lang %>">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
So now for every language a separate index.html with the right language and the right js file for that language is created in every language folder.
So not the html file dictates the language set, but the webpack config.
I am trying to insert both my style and bootstrap in my React JS project. But I get the error: Refused to apply style from 'http://localhost:8000/bootstrap-3.3.7-dist/css/bootstrap.min.css' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
I want it to be inserted through the index.html because it's what I am used to. I know the way to insert bootstrap by npm and importing it but I still just want it to be inserted through the index.html.
Hierarchy of files
Currently the following are my codes:
INDEX.HTML
<!DOCTYPE html>
<html lang="en">
<head>
<title>Ticketing</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Tell the browser to be responsive to screen width -->
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" type="text/css" href="./bootstrap-3.3.7-dist/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="./style/style.css" />
</head>
<body>
<div id="app"></div>
<script src="/app/bundle.js"></script>
</body>
</html>
WEBPACK.CONFIG.JS
var webpack = require("webpack");
var path = require("path");
var DIST_DIR = path.resolve(__dirname, "dist");
var SRC_DIR = path.resolve(__dirname, "src");
var config = {
entry: SRC_DIR + "/app/index.js",
output: {
path: DIST_DIR + "/app",
filename: "bundle.js",
publicPath: "/app/"
},
module:{
loaders: [
{
test: /\.js?/,
include: SRC_DIR,
loader: "babel-loader",
query:{
presets: ["react", "es2015", "stage-2"]
}
}
]
}
};
module.exports = config;
I think you should move the css and style folder into the src folder. Or try changing ./ to ../.
With following steps you can get it:
1st install bootstrap from npm (npm i bootstrap),
go to your app.js file add
#import '../node_modules/bootstrap/dist/css/bootstrap.min.css'
#import 'YOUR_CSS_FILE.css'