How to configure raw-loader in Angular 7 to load text files? - javascript

I want to use a raw-loader with my Angular 7 to import text files into my TypeScript source code. I've found plenty of information on the subject and spent a few hours trying to get it to work, but I can not get it to work.
I start by creating a new project
ng new example --defaults --minimal
I create a text file at /src/app/example.txt with the message "Hello World"
I then modify the app.component.ts file to import this text file.
import {Component} from '#angular/core';
import str from 'example.txt';
alert(str);
#Component({
selector: 'app-root',
template: ``,
styles: []
})
export class AppComponent {
}
I see a Cannot load module "example.txt" error in WebStorm, and when I run ng build and I get the following error.
ERROR in src/app/app.component.ts(2,17): error TS2307: Cannot find module 'example.txt'
So I Googled how to resolve this issue and I found this answer.
How to import JSON File into a TypeScript file?
I then add a /src/typings.d.ts file with the following contents, and this fixes the error in WebStorm.
declare module "*.txt" {
const value: string;
export default value;
}
I run ng build again and the error message changes to say it can't find the module.
Module not found: Error: Can't resolve 'example.txt' in 'C:\work\temp\example\src\app'
After some lengthy Googling, I figure that I need to add a raw-loader to Angular using a custom webpack configuration. Which leads me to this blog post with instructions.
https://codeburst.io/customizing-angular-cli-6-build-an-alternative-to-ng-eject-a48304cd3b21
So I install custom-webpack
npm i -D #angular-builders/custom-webpack
I edit my angular.json so that the build uses extra-webpack.config.js like so.
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./extra-webpack.config.js"
},
I create the extra-webpack.config.js in the same folder as angular.json with the following contents.
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
};
I try building again ng build but get the same error.
Module not found: Error: Can't resolve 'example.txt' in 'C:\work\temp\example\src\app'
I have been unable to get past this point, and everything I've Googled so far seems to imply that this is how it's supposed to be done. Module not found error messages are very broad and I can not find anything specific to Angular 7.

I figured it out, and the answer was on the raw-loader documentation page. At the bottom it explains that you have to prefix the import path with raw-loader!
https://webpack.js.org/loaders/raw-loader/#examples
import {Component} from '#angular/core';
import str from 'raw-loader!./example.txt';
alert(str);
#Component({
selector: 'app-root',
template: ``,
styles: []
})
export class AppComponent {
}
I found this very difficult to figure out. You have to figure out how to get TypeScript to recognise the modules, then you have to configure Angular to use the loader and then you have to know how to correctly import the file.
None of the Google search results showed everything together as it related to Angular 7. So I found this turns up in search results for other people.

Working in Angular 9,10 with Ivy
So I finally got it working, from this comment on an issue, here's the steps I took, if anyone else is looking for help.
yarn add -D #angular-builders/custom-webpack raw-loader
Change angular.json to use #angular-builders/custom-webpack
...
"architect": {
"build": {
"builder": "#angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.partial.js"
},
...
"serve": {
"builder": "#angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "PROJECTNAME:build"
},
...
add file webpack.partial.js next to angular.json
module.exports = {
module: {
rules: [
{ test: /\.(txt|md)$/, loader: 'raw-loader' }
]
}
};
add type declaration in file ./types/textfile.d.ts
declare module '!raw-loader!*' {
const contents: string;
export = contents;
}
make sure that your tsconfig file knows about textfile.d.ts
{
"compilerOptions": {
...
"typeRoots": ["node_modules/#types", "./types"],
... // ^ this is important
}
import your text files using require syntax
import { Component } from '#angular/core';
const myText = require('!raw-loader!./my-text-file.txt');
#Component({
template: `<pre>{{myText}}</pre>`,
})
export class ChangeLogComponent {
myText = myText;
}
Done! The project should serve/build in angular 9,10 now

Related

VueJS compile failing when importing json data

I'm trying to import a static .json file in the <script> section of a .Vue file like so import Test from '#assets/test.json'
Based on what I've read about webpack, this should work out of the box. I've also tried explicitly defining both the .json extension in webpack resolve and the json loader under loaders.
However, for me it fails with the error:
ERROR Failed to compile with 1 errors 9:14:24 AM
This dependency was not found:
* #assets/test.json in ./node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/babel-loader/lib!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/components/Settings.vue?vue&type=script&lang=js&
To install it, you can run: npm install --save #assets/test.json
That seems to be strange as it's not a dependency I'd be able to install?
My test.json file just contains {}, but I've also tried it with the real data file and the same outcome.
Thanks in advance for any help.
Edit: Thanks to #tao for helping me figure it out on chat. The error was indeed trivial: import Test from '#assets/test.json' should have been import Test from '#/assets/test.json'
For typescript support you have to add
"resolveJsonModule": true,
to compilerOptions in tsconfig.json.
And add
declare module '*.json' {
const value: unknown;
export default value;
}
to your shims-vue.d.ts
In Javascript, you don't need to do anything special. This should work:
test.json:
{
"foo": "bar"
}
Your *.vue SFC file:
<script>
import * as json from './test.json';
export default {
data: () => ({
json
}),
mounted() {
console.log(this.json)
}
}
</script>

.modal() function not working in angular 8

On Click, "$("#Popup").modal('show')" is not working.
HTML
<a class="btn-profile-login" data-target="#Popup" (click)="loginBtn()">{{SignText}}</a>
TS
import { Component, OnInit, ViewChild } from '#angular/core';
import * as $ from 'jquery';
import * as bootstrap from "bootstrap";
export class SampleComponent implements OnInit{
loginBtn() {
$("#Popup").modal('show');
}
}
Error Message:
TypeError: jquery__WEBPACK_IMPORTED_MODULE_9__(...).modal is not a
function
You can follow these step
npm install jquery --save
npm install #types/jquery --save
Then in scripts section in architect => build of angular.json file we add path for jquery lib
"scripts": [
"node_modules/jquery/dist/jquery.min.js"
]
Then modify tsconfig.app.json
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/app",
"types": ["jquery"] // addd jquery here
},
"exclude": ["test.ts", "**/*.spec.ts"]
}
Then you can remove this import since you already have type definition of jquery
import * as $ from 'jquery';
Use ngx-bootstrap https://valor-software.com/ngx-bootstrap/ It is Angular Bootstrap components that do not require jQuery. It is the official way to use Bootstrap with Angular, you should avoid to include jquery in angular apps when possible
All the best.
you need to include jQuery in index.html and in your component you need to
declare var $: any;
"scripts": [ "node_modules/bootstrap/dist/js/bootstrap.min.js" ]
This worked for me in angular.json

Absolute imports with rollup

I'm trying to get imports like
import { startup } from "applicationRoot/renderUI";
to work, from anywhere in my application. I thought the rollup-plugin-alias would be a good fit for this. I tried configuring
alias({
applicationRoot: "applicationRoot/"
})
in my plugins array. This came close, but the extension is dropped, so I get the error:
c:\path\to\applicationRoot\renderUI doesn't exist.
Adding in
alias({
resolve: [".js"],
applicationRoot: "applicationRoot/"
}),
did not change anything.
You can use rollup-plugin-includepaths.
Add this to your Rollup configuration:
import includePaths from 'rollup-plugin-includepaths';
export default {
...
plugins: [
...
includePaths({ paths: ["./"] })
]
};
That will tell Rollup to also resolve imports from the root of your application, so things like
import { startup } from "applicationRoot/renderUI";
will appropriately find an applicationRoot folder where it is.
This is not the answer to the original question, but if you are coming here from search results and are using Typescript, you can set the compilerOptions.baseUrl in tsconfig and it will just work.
{
...
compilerOptions: {
...
"baseUrl": "./"
},
}
Then if you have a file like `src/lib/util.ts' you can import it:
import util from 'src/lib/util'

use Chart.js in Aurelia

I'd like to use chart.js in an aurelia project, but I'm getting errors. How do I add 3rd party node packages to an aurelia app?
I'm using aurelia-cli, BTW
Here's what I've done
npm install --save chart.js
In aurelia.json I added the following
"dependencies": [
...,
{
"name": "chart.js",
"path": "../node_modules/chart.js/dist",
"main": "Chart.min.js"
}
]
In app.html I then add the line
<require from="chart.js"></require>
But, I get the error:
vendor-bundle.js:1399 Unhandled rejection Error: Load timeout for modules: template-registry-entry!chart.html,text!chart.html
I've tried various things like injecting the Chart into the app.html
// DIDN'T WORK :-(
// app.js
import {inject} from 'aurelia-framework';
import {Chart} from 'chart.js';
export class App {
static inject() { return [Chart]};
constructor() {
this.message = 'Hello World!';
}
}
And, then, in app.html, I added the following require statement
<require from="Chart"></require>
HERE'S THE SOLUTION
You can checkout a working example here. Initially, I thought you had to use the aurelia-chart module, however, it's very difficult to use, and so, I'd recommend you just use Chart.JS package instead. Here's how to incorporate the chart.js module into your Aurelia app:
npm install --save chart.js
In aurelia.json add the following line to the prepend section
"prepend": [
...,
"node_modules/chart.js/dist/Chart.min.js"
],
In the app.js file (or any other model-view file), add the line
import {Chart} from 'node_modules/chart.js/dist/Chart.js';
For, example, if you wanted to display a chart on the home page:
// app.js
import {Chart} from 'node_modules/chart.js/dist/Chart.js';
export class App {
...
}
And that's it!
1. Problem with require
First of all, don't use <require from="Chart"></require> in your app.html project. That is the source of your error message, since it's trying to load an Aurelia module and chart.js is not an Aurelia module (view/viewmodel) in your source code.
2. Alternate import syntax
Skip the inject lines in app.js, but try one of the following (try them one at a time) in either app.js or in each module you'll be using Chart. One of these imports is likely to work.
import { Chart } from 'chart.js';
import * from 'chart.js';
import 'chart.js';
3. Legacy prepend
If none of the above works, import it as a legacy repo using the prepend section of aurelia.json (before the dependencies section) like this:
"prepend": [
// probably a couple other things already listed here...
"node_modules/chart.js/dist/Chart.min.js"
],
Update for Aurelia-Chart: (added for any later viewers)
Since you ended up going with aurelia-chart (by grofit), here's the dependency code for aurelia.json:
"dependencies": [
...,
{
"name": "chart.js",
"path": "../node_modules/chart.js/dist",
"main": "Chart.min.js"
},
{
"name": "aurelia-chart",
"path": "../node_modules/aurelia-chart/dist/amd",
"main": "index",
"deps": ["chart.js"]
}
]
I just got this working with an aurelia cli project and it required some extra modifications.
I used au install chart.js but there is an open issue that states it is not intelligent enough yet to add references to package dependencies.
To make things work I added the following to my aurelia.json dependencies:
"moment",
"chartjs-color",
"chartjs-color-string",
{
"name": "chart.js",
"main": "src/chart.js",
"path": "../node_modules/chart.js",
"deps": ["chartjs-color", "moment"]
},
{
"name": "color-convert",
"path": "../node_modules/color-convert",
"main": "index"
},
{
"name": "color-name",
"path": "../node_modules/color-name",
"main": "index"
}
I was then able to import { Chart } from 'chart.js'; in my view model and run the chart.js quick start example from the attached viewmodel lifecycle method.
In the chart.js docs they mention including the minified version can cause issues if your project already depends on the moment library.
The bundled version includes Moment.js built into the same file. This version should be used if you wish to use time axes and want a single file to include. Do not use this build if your application already includes Moment.js. If you do, Moment.js will be included twice, increasing the page load time and potentially introducing version issues.
This solution may help if you are in that position.

Integrate the ng2-ace library into a freshly created angular-cli (angular2) project using SystemJS

I just created an angular2 project with the latest angular-cli tool. I now want to get the ace editor up and running using the ng2-ace library. I want to do it in a clean approach using SystemJS as the module loader.
I did
npm install --save ng2-ace
then I added the following two lines to angular-cli-builds.js to the vendorNpmFiles array
'ng2-ace/index.js',
'brace/**/*.js
then I added the following to system-config.ts
const map: any = {
'ng2-ace': 'vendor/ng2-ace',
'brace': 'vendor/brace'
};
/** User packages configuration. */
const packages: any = {
'brace': {
format: 'cjs',
defaultExtension: 'js',
main: 'index.js'
},
'ng2-ace': {
format: 'cjs',
defaultExtension: 'js',
main: 'index.js'
}
};
Now I tried importing the directive from a component
import { AceEditorDirective } from 'ng2-ace';
This makes the compiler ng serve aborting with the following error:
The Broccoli Plugin: [BroccoliTypeScriptCompiler] failed with:
Error: Typescript found the following errors:
Cannot find module 'ng2-ace'.
I tried to follow the Readme from angular-cli and got the google material design library working. However, I don't know what I do wrong when trying to load the ng2-ace library.
I think the reason this is so hard is that there's no typings library provided. I was able to get the rough equivalent of this working by adding a few things. My version has a pretty static configuration but you can enhance it.
system-config needs this:
const map:any = {
'brace': 'vendor/brace',
'w3c-blob': 'vendor/w3c-blob',
'buffer': 'vendor/buffer-shims'
};
it may also need:
const packages:any = {
'w3c-blob': {
format: 'cjs',
defaultExtension: 'js',
main: 'index.js'
},
'brace': {
format: 'cjs',
defaultExtension: 'js',
main: 'index.js'
},
'buffer': {
format: 'cjs',
defaultExtension: 'js',
main: 'index.js'
}
};
Then you also need to add these things as npm dependencies in angular-cli-build.js:
module.exports = function(defaults) {
return new Angular2App(defaults, {
vendorNpmFiles: [
/* your stuff goes here, then add: */
'brace/**/*.js',
'w3c-blob/index.js',
'buffer-shims/index.js'
]
});
That pretty much gets you everything you need as far as dependencies are concerned. At this point I added my own directive. The important parts are here:
import { Directive, ElementRef, EventEmitter } from '#angular/core';
Now import brace itself plus whatever modes and themes you will be using:
import 'brace';
declare let ace;
import 'vendor/brace/mode/javascript';
import 'vendor/brace/theme/monokai';
The 'declare let ace' lets you have access to brace even though there's no typings and it's not a real typescript module.
I named my directive 'js-editor' and you attach it to a tag of an appropriate height and width. The docs for brace say to apply a 'block' style to the div it as well. Then declare the directive:
#Directive({
selector: '[js-editor]',
inputs: ['text'],
outputs: ['textChanged', 'editorRef']
})
export class JsEditor {
editor : any;
textChanged : EventEmitter<any>;
editorRef : EventEmitter<any>;
value : string;
set text(value) {
// if(value === this.oldVal) return;
// this.editor.setValue(value);
// this.editor.clearSelection();
this.editor.focus();
}
constructor(elementRef : ElementRef) {
this.textChanged = new EventEmitter();
this.editorRef = new EventEmitter();
const el = elementRef.nativeElement;
el.classList.add('editor');
Setting the base path is the key element to brace being able to find modes and themes. This is really the wrong place to set it - it should be done globally, and ONLY ONCE ... but this was just an experiment to see if it would work so I did it here:
ace.config.set('basePath', 'vendor/brace');
Finally, create the editor:
this.editor = ace.edit(el);
and then set your mode and theme. Note that these modes/themes LOOK like paths, but they really are not. Ace (or perhaps Brace) will use these strings to generate the path using the basePath above:
this.editor.getSession().setMode('ace/mode/javascript');
this.editor.setTheme('ace/theme/monokai');
setTimeout(() => {
this.editorRef.next(this.editor);
});
this.editor.on('change', () => {
/* do whatever you want here */
});
}
}
That's the general idea. It really needs to be wrapped up into a nice configurable directive along the lines of ng2-ace but I'm not the right guy to do that, I just wanted to get you headed in the right direction.
--Chris

Categories