I'm trying to integrate Angular 2 in a Sails Js application. I'm new to both. I have been following this official tutorial here. It works fine in standalone mode with a static http server but when i try to integrate into sails app i get following problems:
1 - How do i refer to angular2 js inside the local node_modules folder. Everytime i do, sails interprets it as a route and gives me a 404 for my scripts. For instance:
<script src="node_modules/angular2/dist/angular2.min.js"></script>
I was able to overcome above issue using cdnjs links for now but i would like to know a better/proper solution.
2 - I added the tsc and tsc -w scripts to my package.json, but even with sails lift --verbose i do not get any output or error. Here is how I added the script to json file:
"scripts": {
"tsc": "tsc",
"tsc:w": "tsc -w",
"debug": "node debug app.js",
"start": "node app.js"
}
In the end i had to install typescript with -g and compile manually. That worked, but again, it's expected to work with the scripts. It would be great to know what I'm missing.
3 - After jumping through hoops to get rid of the above errors, when i lift the server again, it gives me more 404 error which seem to be coming from system.src.js and that I am unable to figure out. Please see the console screengrab below.
I think I might be making a mistake setting up the angular application directories within sails. Just to make sure we cover everything, here is the directory structure I'm using. The sails app does not have anything in it yet. Which is why the below paths are just for angular related artifacts and assets.
Within the assets folder:
app
│ ├── app.component.ts
│ └── main.ts
Of course the .ts files get compiled to js.
In the sails views folder I have layout.ejs which has following contents:
.
.
.
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.34.2/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.17/system-polyfills.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.1/angular2-polyfills.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.17/system.src.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.7/rx.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/2.0.0-beta.1/angular2.dev.js"></script>
<script>
System.config({
packages: {
app: {
format: 'register',
defaultExtension: 'js'
}
}
});
System.import('/app/main')
.then(null, console.error.bind(console));
</script>
.
.
.
<my-app>Loading...</my-app>
In addition to above files, I have also added the tsconfig in the sails root folder.
I have followed the code and directory structure guidelines from the official quickstart tutorial.
So, for anyone interested, i have resolved the issues like so:
1 - For providing static access to node_modules i created an express middleware (probably use policies for this as well?):
(config/express.js)
var express = require('express');
module.exports.http = {
customMiddleware: function (app) {
app.use('/node_modules', express.static(process.cwd() + '/node_modules'));
}
};
2 - I was able to compile already so all good there
3 - For the rxjs related errors, i did some research and found out that rxjs is no longer bundled with angular 2. Therefor, i had to modify the systemjs config a bit to add mapping for rxjs, like so:
(views/layout.ejs)
System.config({
map: {
rxjs: 'node_modules/rxjs' // added this map section
},
packages: {
app: {
format: 'register',
defaultExtension: 'js'
},
'rxjs': {defaultExtension: 'js'} // and added this to packages
}
});
System.import('/app/main')
.then(null, console.error.bind(console));
You need to setup access to your static files. You can check out how, right here.
http://sailsjs.org/documentation/concepts/assets
So put those node modules into an asset folder, for which you can then have static access.
However, are you sure you want to do this with Sails? As far as I know Sails is a fullblown MVC framework, which you won't really need if you only want to use it as a backend for Angular. I'd recommend using something like Express instead.
You can use the JavaScript files that are provided in the folder node_modules/angular2/bundles:
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="node_modules/rxjs/bundles/Rx.js"></script>
<script src="node_modules/angular2/bundles/angular2.dev.js"></script>
<script src="node_modules/angular2/bundles/router.dev.js"></script>
<script src="node_modules/angular2/bundles/http.dev.js"></script>
This should answer to your first question.
Regarding the second question, elements you put in the scripts block of your package.json file are aliases for commands. The tsc -w one waits for updates in TypeScript files and automatically compiles them. This command must be started in background... For example with: npm run tsc:w.
Hope it helps you,
Thierry
(1) For the first question:
The problem with 404 angular files not found is that when Sails lifts Grunt deletes all the files in .tmp then rebuilds the project from scratch, so what happens is Sails server starts before the build is finished and the files are not there that's why you get 404. If you wait for a little while your page should load without any errors.
If waiting get too long use these scripts from CDN:
<script src="https://rawgithub.com/systemjs/systemjs/0.19.6/dist/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/Rx.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/angular2.dev.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/router.dev.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.1/http.dev.js"></script>
(2) Second question:
Run tsc in a separate console window like this:
npm run tsc:w
(3) The third problem by adding the following to components where it's needed:
import 'rxjs/add/operator/map';
Related
Intro
I have a website where i serve the storybook static of my stable branch (https://mydomain.co) and it works fine, but i also deploy previews environments on each pull requests via my CI.
The upload of each pull request is 'dynamic' meaning, that i slugify the name of the branch and end up building and uploading the storybook static to this pattern:
https://mydomain.co/preview/{slugified-branch-name}
The problem
The problem im facing is, in the iframe.html generated by storybook we can find this:
<script type="module" crossorigin src="/assets/iframe.1767e7d6.js"></script>
<link rel="stylesheet" href="/assets/iframe.4ea3770b.css">
And on my apache server this will resolve to https://mydomain.co/assets/iframe.x, with that in mind, the storybook instance won't be able to find it and i end up with an endless loading loop.
Hacky workaround
When i connect with ssh and remove the leading slash / in front of assets the storybook instance get delivered without trouble.
Today, to automate that,i have a hacky workaround in my CI. I will use sed to remove that slash after the build.
Via Storybook config ?
However, i would like to know if any of you had a solution to solve that problem directly with the storybook config itself and have my bundle output the src without the leading slash to end up with this in my iframe.html
<script type="module" crossorigin src="assets/iframe.1767e7d6.js"></script>
<link rel="stylesheet" href="assets/iframe.4ea3770b.css">
Thanks in advance
In your .storybook/main.(ts|js):
import mergeConfig
add the viteFinal method
import { mergeConfig } from "vite";
export default {
// ...
async viteFinal(config, options) {
return mergeConfig(config, {
base: "./",
});
},
};
viteFinal is used to merge your project level vite.config.js with the vite.config.js used by storybook.
In our current situation, this piece of code will change the base path from an absolute path to a relative one when built. This should help resolve your issue and help your web server resolve the asset path correctly.
Reference: https://vitejs.dev/config/shared-options.html#base
I have a webpack bundled widget that I pack into a single file using webpack, and can use as follows:
<script type="text/javascript" src="my-bundle.js"></script>
<script type="text/javascript">
(function() {
MyBundle.render();
}) ();
</script>
This works fine, but I want to use this widget in my main Rails app. So I've copied the my-bundle.js file into my main project directory and required it.
When I run webpack on my main app, I can see that the code in my-bundle is being included in the resulting js file, but I cannot access the code. i.e. calling MyBundle gives a not defined error.
How can I access it?
EDIT - it looks like I can just use script-loader to run the my-bundle.js file once (which defines a MyBundle function). This doesn't feel like the best way to do it though
I want to use a javascript library called fuse.js. (https://fusejs.io/) in my vanilla javascript app.
I npm install fuse and then add
const Fuse = require('fuse.js')
...other javascript functions here
to the top of my script.js file. (i have trie removing the .js but the lib is called fuse.js in the node_modules folder)
I then run:
browserify script.js -o bundle.js
then I add the bundle.js to my index.html
<script type="module" src="bundle.js"></script>
then, no matter what I do I can't access the Fuse object or any of my other javascript functions...It does load the bundle...but it's completely "closed", I can't use any functions in it.
I have seen that the guys export the bundle to the window object, but there are a lot of people saying this isn't best practice?
Are there any good resources anyone can recommend that I can study up to understand the concept of
I see a useful library on npm, and I use it in my front-end code
(fuse.js does have a cdn that I can just include in the script tag, but I want to know for future use how to use npm modules in my front-end workflow?)
For the first time I am using Yarn. I have installed the latest version of Laravel Boilerplate (http://laravel-boilerplate.com/) and there is used Yarn.
My need is to include the JS library DataTables (https://datatables.net/).
Unfortunately I am new to Yarn and I am not sure if I am making everything right, because I get the error:
[Show/hide message details.] ReferenceError: $ is not defined
which is on the this line:
$(document).ready(function() {
...
This is telling me that it cannot find the jquery library, but it should be there.
Here is the webpack.mix.js code:
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.setPublicPath('public');
mix.sass('resources/sass/frontend/app.scss', 'css/frontend/frontend.css')
.sass('resources/sass/backend/app.scss', 'css/backend/backend.css')
.js('resources/js/frontend/app.js', 'js/frontend/frontend.js')
.js([
'resources/js/backend/before.js',
'resources/js/backend/app.js',
'resources/js/backend/after.js'
], 'js/backend/backend.js')
.extract([
'jquery',
'datatables.net-dt',
'bootstrap',
'popper.js/dist/umd/popper',
'axios',
'sweetalert2',
'lodash',
'#fortawesome/fontawesome-svg-core',
'#fortawesome/free-brands-svg-icons',
'#fortawesome/free-regular-svg-icons',
'#fortawesome/free-solid-svg-icons'
]);
if (mix.inProduction() || process.env.npm_lifecycle_event !== 'hot') {
mix.version();
}
Every time I call the command "yarn prod" in order to create the CSS and js files, but the DataTables are not working.
Did I miss something?
Thanks in advance!
It's not because of yarn. Yarn is a package manager, it doesn't run any part of your application's code so cannot generate an error like yours. Yarn is just for downloading packages and manage their dependencies.
Then comes Laravel Mix for you, which is just a wrapper around Webpack. Webpack reads your application code, handles your require and import commands in your .js files and then generates your bundles.
How to make it work:
I suppose you did run the yarn command (without params) in your project root once when you installed Laravel Boilerplate. There should be a lot of packages inside your node_modules directory (more than 900).
Then you did run yarn add -D datatables.net-dt also. Now you should have a datatables.net and a datatables.net-dt folder inside node_modules.
I see you've added datatables.net-dt in your webpack.mix.js, this is OK! You don't need any other require( 'datatables.net-dt' )( window, $ ); as said in the documentation. That one line in your webpack.mix.js is enough! DataTable will be inside your vendor.js.
Now create an example table with attribute id="example" in your index.blade.php then add this code to the bottom of your resources\js\frontend\app.js:
$(document).ready(function() {
$('#example').DataTable();
});
Then run yarn dev to let Webpack generate your bundles (compiled js files) and view your site in the browser. Following these, it should be working on a fresh install install of Laravel Boilerplate, without any error. I've just tested id now, works like charm.
Your possible bug:
$ is not defined tells that some part of your code is trying to use jQuery before it has been loaded.
It's important that you must write your codes using jQuery (shortened $) inside your resources\js\frontend\app.js or in a separate .js which is later required/imported into this file! It's because jQuery and other vendor packages like DataTable are stored in vendor.js, which must be loaded before any calls to them.
So don't use custom <script> tags in your html's <head> tag for your app code because that will be loaded and executed before any other defined in the bottom of your <body> tag!
Have a look at this file resources\views\frontend\layouts\app.blade.php. In the bottom of the body tag you'll see this:
<!-- Scripts -->
#stack('before-scripts')
{!! script(mix('js/manifest.js')) !!}
{!! script(mix('js/vendor.js')) !!}
{!! script(mix('js/frontend.js')) !!}
#stack('after-scripts')
Your resources\js\frontend\app.js and all its imported scripts will be compiled to this js/frontend.js file.
How jQuery is imported in Laravel Boilerplate:
This is done well by default, you don't have to bother with it. Open your resources\js\bootstrap.js and see these two lines:
import $ from 'jquery';
window.$ = window.jQuery = $;
This file will then imported by frontend/app.js. So write your code here and you'll be fine...
PS.: If this doesn't helps you to make it work, you should edit your question and provide more info on your sources. A screenshot for example taken from your Chrome DevTools, showing the lines of your JavaScript where the error occurred.
I have quite a basic question, that unfortunately I can't find answer for in tutorials.
My entry point to require.js is js/main.js. I've configured build.js :
({
baseUrl: "js",
paths: {
// bower dependencies go here
},
name: "main",
out: "dist/main.min.js"
})
added build to package.json:
"build": "bower install --allow-root && node node_modules/requirejs/bin/r.js -o build.js"
and got my minified layer, which seems to include all my js files.
However, the information that fails is what to do with that layer afterwards.
When I've added:
<script src="dist/main.min.js" type="text/javascript"></script>
to my index.html before
<script data-main="js/main" src="bower_components/requirejs/require.js"></script>
the whole js files are loaded one by one by require.js. When I remove require.js, nothing happens (the code from the compiled layer doesn't call the app initialization routine).
What I'm missing here?
It looks like the answer is as trivial as replacing js/main layer declaration with dist/main.min:
<script data-main="dist/main.min" src="bower_components/requirejs/require.js"></script>
or even better, simply overwriting js/main with minimized layer in build script when preparing distribution directory.
Thanks to https://stackoverflow.com/a/20004336/5479362
Honestly, I don't understand why that information is missing from requireJs tutorial...