I am trying to setup a dev environment for developing widgets. In the book Third Party JavaScript They describe how to simulate multiple domains by modifying your hosts file and configuring Apache web server to create two virtual hosts. I was wondering if something similar is possible using the rollup-plugin-dev development server which uses the fastify-http-proxy.
Rollup Config:
...
const devPlugin: Plugin = dev({
dirs: ['dev'],
port: 5000,
// How to configure this?
proxy: []
});
...
Basically I want to serve the widget IIFE code from one domain, say widget.dev and a test html file which loads the script code on another domain, say publisher.dev. The HTML page at publisher.dev would have a <script> tag which loads the widget:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Widget Test Page</title>
<link href="/widget.css" rel="stylesheet" type="text/css"/>
<script async src="http://widget.dev"></script>
</head>
<body>
<div id="widget" data-border></div>
<script>
window.widget = {
// widget config...
}
</script>
</body>
</html>
One way to solve this problem for development is to serve up your widget on some port and then create a separate project that will reach out to grab the js files. I created a separate react app that loaded the script files from the port they are served on. So to keep it extremely simple, in my App.js file of the dev host project I did something like this:
App.js
import React, { useState, useEffect } from 'react';
export const loadScript = (src, id, onError) => {
const script = document.createElement('script');
script.src = src;
script.async = true;
script.onerror = (e) => onError(e, 'script', 'could not load script');
script.setAttribute('data-id', id);
document.head.appendChild(script);
};
export const loadStyle = (href, id, onError) => {
const link = document.createElement('link');
link.href = href;
link.rel="stylesheet";
link.type="text/css";
link.onerror = (e) => onError(e, 'link', 'could not load style');
link.setAttribute('data-id', id);
document.head.appendChild(link);
};
const App = () => {
// run this effect one time only
useEffect(() => {
loadStyle('http://localhost:5100/myWidget.css', 'widget-style');
loadScript('http://localhost:3001/myWidget.js', 'widget-id');
}, []);
// ...
};
Depending on your use case, there are (probably) better ways to achieve this.
Related
I made a static website hosted on an AWS s3 bucket.
I do not know the tools and technology around web development, but I took an example of index.html code allowing me to plot data from a single file named "my_data_file1.csv".
<!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>Coding Train: Data and APIs Project 1</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js#2.8.0"></script>
</head>
<body>
<h1>Global Temperatures</h1>
<canvas id="myChart" width="400" height="200"></canvas>
<script>
window.addEventListener('load', setup);
async function setup() {
const ctx = document.getElementById('myChart').getContext('2d');
const globalTemps = await getData();
const myChart = new Chart(ctx, {
type: 'line',
data: {
labels: globalTemps.years,
datasets: [
{
label: 'Temperature in °C',
data: globalTemps.temps,
fill: false,
borderColor: 'rgba(255, 99, 132, 1)',
backgroundColor: 'rgba(255, 99, 132, 0.5)',
borderWidth: 1
}
]
},
options: {}
});
}
async function getData() {
const response = await fetch('my_data_file1.csv');
const data = await response.text();
const years = [];
const temps = [];
const rows = data.split('\n').slice(1);
rows.forEach(row => {
const cols = row.split(',');
years.push(cols[0]);
temps.push(parseFloat(cols[2]));
});
return { years, temps };
}
</script>
</body>
</html>
All of my data is split into multiple files, so I would like to be able to account for all the CSV files in a directory, rather than just one. The name of my files is variable, so I cannot list them one by one.
Is it possible to use a filter or RegEx as "*.csv"?
Is it possible to use a filter or RegEx as "*.csv"?
No.
While URLs can be generated by mapping files and directories from a filesystem to URLs, a URL isn't a directory.
There is no way to glob URLs.
You could ensure that the server, when asked for ./ returns a list of URLs and then parse and filter it with client-side JS, and then request each of those URLs (probably using Promise.all to determine when you had a response for every one of those requests).
You could also write server-side code to concatenate all the CSV data so you only have to make one request.
I have been following a video tutorial which apparently using JSBin to show its code, when I tried out the code locally then it does not work for me. Could someone please help me to figure out what is the issue.
Below is the code
<!DOCTYPE html>
<html>
<head>
<title>Redux basic example</title>
<script src="https://unpkg.com/redux#latest/dist/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.0/react.min.js" type = "text/babel"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.0/react-dom.min.js" type = "text/babel"></script>
</head>
<body>
<div id='root'>
</div>
<script>
const counter = (state = 0, action) => {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
};
const Counter = ({ value}) => (<div>{value}</div>);
const { createStore } = Redux;
var store = createStore(counter);
const render = () => {
ReactDOM.render(
<Counter value={store.getState()} onIncrement = {
() => store.dispatch({type: 'INCREMENT'})
}
onDecrement = {
() => store.dispatch({type: 'DECREMENT'})
} />,
document.getElementById('root')
);
};
store.subscribe(render);
render();
</script>
</body>
</html>
You are using JSX in your code, which needs to be transpiled into standard javascript before executing it in the browser.
const Counter = ({ value}) => (<div>{value}</div>);
Look into Babel
The browser is complaining about the JSX code. You should transpile it to regular Javascript before including it in your page. There are several ways to do: Webpack, Babel...
Have a look to create-react-app npm package to get started fast: https://github.com/facebookincubator/create-react-app
This is the error: Error: [$injector:modulerr] Failed to instantiate module angularjsNodejsTutorial due to:
Error: [$injector:nomod] Module ‘angularjsNodejsTutorial’ is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.” exception. from browser or postman when I hit localhost:3000/dirPath then I get the data back but not through this html file.
//here are the files: index.html, app.js(angularjs) and index.js(node)
//index.html
<!DOCTYPE html>
<html ng-app="angularjsNodejsTutorial">
<head>
<title>Integrating AngularJS with NodeJS</title>
<script src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.2/angular.js"></script>
<script src="../node_modules/angular-route/angular-route.min.js"></script>
</head>
<body >
<div ng-controller="myController">
<ul >
<li> The Files Are: {{data}}</li>
</ul>
</div>
<script src="../public/javascripts/app.js" type="text/javascript"></script>
<script src="../node_modules/angular-route/angular-route.js"></script>
</body>
</html>
//(AngularJS Client-Side)app.js
var app = angular.module('angularjsNodejsTutorial',['ngRoute']);
app.controller('myController', function($scope, $http) {
$scope.data = [];
var request = $http.get('/dirPath');
request.success(function(data) {
console.print("The files from this directory are:", data);
$scope.data = data;
});
request.error(function(data){
console.log('Error: ' + data);
});
});
//Server-side node.js index.js file
var express = require('express');
var router = express.Router();
var path = require('path');
/* GET home page. */
router.get('/', function(req, res, next) {
res.sendFile(path.join(__dirname, '../', 'views', 'index.html'));
});
router.get("/dirPath", function(req, res) {
var fs = require("fs");
var dir = '/Users/swapnil/Documents/Test';
fileList = [];
var files = fs.readdirSync(dir);
for(var i in files){
if (!files.hasOwnProperty(i)) continue;
var name = dir+'/'+files[i];
if (!fs.statSync(name).isDirectory()){
fileList.push(name);
}
}
return res.send(fileList);
});
module.exports = router;
I think you are over thinking your route process, this will do the basics:
function config ($routeProvider, _) {
$routeProvider.
when('/Order', {
templateUrl: '../modern/sections/views/view.html',
controller: 'Controller as ctrl',
caseInsensitiveMatch: true,
resolve: {
data: function(Factory){
var view = window.location.href.match(/OrderID=(.*)#/) || 'undefined';
if(Id === 'undefined'){
return Factory.createOrder();
}else{
return Factory.currentOrder(OrderId[1]);
}
}
}
})
.otherwise({
redirectTo: '/'
});
}
FYI: Resolve calls Factories where i have services setup to get my API data.
Sorry if this is not direct, i copied this over from a current working project i have.
Resolved the issue by exposing the server side functionality as a RESTful API instead of node and modified my controller by adding appropriate header info for content type and implemented CORS filter on the server side.
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
response.addHeader("Access-Control-Allow-Origin", "*");
// CORS "pre-flight" request
response.addHeader("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE");
response.addHeader("Access-Control-Allow-Headers",
"X-Requested-With,Origin,Content-Type, Accept");
filterChain.doFilter(request, response);
}
Thanks everyone for taking time to look at my issue and offering help. Appreciate.
Check your modules and make sure they are being loaded appropriately. It's a very common error when you start out. Check your html file and make sure all the modules names are spelled correctly.
If your modules are sharing logic it's common to accidentally miss spell the module name. This case I normally copy and past it over.
Also look at your logic behind the module and make sure you are declaring it properly.
Html not properly loading css file.
Here is my html file:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<h1>TEST</h1>
</body>
</html>
and my style.css file is in the same folder as my .html file shown above.
Here is my style.css file:
body {
background: red;
}
When I inspect the "Network" tab of the Chrome developer tools, my style.css file is listed as "pending".
Any idea how to fix this? I have tried disabling AdBlock and clearing the cache.
My server is being run on node.js, not sure if that's relevant here...
Here is my server.js:
var http = require("http");
// server sends all requests to router file
var router = require("./router.js");
// set the port #
port = "8080";
// server to listen for requests
http.createServer(function (request, response) {
router.home(request, response);
}).listen(port);
// Console will print the message
console.log('Server running at http://127.0.0.1:' + port + '/');
and here is my router.js file:
var renderer = require("./renderer.js");
var url = require("url");
var htmlHeader = {'Content-Type': 'text/html'};
function home(request, response) {
if (request.url === "/") {
if (request.method.toLowerCase() === "get") {
response.writeHead(200, htmlHeader);
renderer.view("header", {}, response);
renderer.view("footer", {}, response);
response.end();
}
}
}
module.exports.home = home;
and finally the renderer.js file:
// to read contents of [view].html files
var fs = require('fs');
// insert contents into [view].html file
function mergeValues(values, content) {
// cycle over keys
for (var key in values) {
// replace all {{key}} with the value from the values object
content = content.replace("{{" + key + "}}", values[key]);
}
// return merged content
return content;
}
// handle the view passed as an argument
function view(templateName, values, response) {
// find the [view].html file in the /views/ folder
var fileContents = fs.readFileSync('./views/' + templateName + '.html', {encoding: "utf8"});
// insert values in to the content of the view file
fileContents = mergeValues(values, fileContents);
// write out contents to response
response.write(fileContents);
}
module.exports.view = view;
Thanks
As static files are requested just like any other HTTP request, the server will not locate your css file because you have no route for it.
You will need to add something like:
if (request.url === "/style.css") {
fs.readFile('style.css', function (err, data) {
response.writeHead(200, {'Content-Type': 'text/css', 'Content-Length': data.length});
response.write(data);
response.end();
});
}
There are of course better ways to serve static files with module that locates existing files automatically for you. This is ment as a simple answer only.
Have you privileges to access to the css file? Try:
chmod 777 style.css
I am trying to dynamically add css to my html head using angular js. Here is sample code
<div ng-repeat="stylesheet in stylesheets">
<link href="/myapp/{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" media="{{stylesheet.media}}" title="{{stylesheet.title}}" />
</div>
This code works as expected, but when browser loads the page, it tries to fetch css resources with raw angularjs templates and I see "404 not found error" in network tab of firebug.
Eg: request http://localhost:8080/myapp/%7B%7Bstylesheet.href%7D%7D, status 404
When page is completely loaded, it does substitution of template values and loads proper css.
Is there a way to avoid 404 error and make it load css after angularjs processing?
You should use ng-href instead of href.
<link ng-repeat="stylesheet in stylesheets" ng-href="{{stylesheet.href}}" type="{{stylesheet.type}}" rel="stylesheet" />
Example
I made a AngularJS service to use easily the #Artem solution.
It's here on GitHub.
There's a another option using $route.resolve and promises. This will wait until the CSS is actually loaded not only added to the head (after that the browser just starts retrieving the file and depending on CSS size can cause page reflow).
// Routing setup
.config(function ($routeProvider) {
$routeProvider
.when('/home', {
controller: 'homeCtrl',
templateUrl: 'home.tpl.html'
}).when('/users', {
controller: 'usersCtrl',
controllerAs: 'vm',
templateUrl: 'users.tpl.html',
resolve: {
load: ['injectCSS', function (injectCSS) {
return injectCSS.set("users", "users.css");
}]
}
}).otherwise({
// default page
redirectTo: '/home'
});
})
Service implementation
.factory("injectCSS", ['$q', '$http', 'MeasurementsService', function($q, $http, MeasurementsService){
var injectCSS = {};
var createLink = function(id, url) {
var link = document.createElement('link');
link.id = id;
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
return link;
}
var checkLoaded = function (url, deferred, tries) {
for (var i in document.styleSheets) {
var href = document.styleSheets[i].href || "";
if (href.split("/").slice(-1).join() === url) {
deferred.resolve();
return;
}
}
tries++;
setTimeout(function(){checkLoaded(url, deferred, tries);}, 50);
};
injectCSS.set = function(id, url){
var tries = 0,
deferred = $q.defer(),
link;
if(!angular.element('link#' + id).length) {
link = createLink(id, url);
link.onload = deferred.resolve;
angular.element('head').append(link);
}
checkLoaded(url, deferred, tries);
return deferred.promise;
};
return injectCSS;
}])
You could add a timeout using tries if this is something you would like to include.
Check out this post for more details:https://medium.com/angularjs-meetup-south-london/angular-dynamically-injecting-css-file-using-route-resolve-and-promises-7bfcb8ccd05b
I've created a very simple example of how make a conditionaly css addition
<link rel="stylesheet" ng-if="lang_direction == 'rtl'" ng-href="{{lang_direction == 'rtl' ? 'css/rtl.css' : ''}}" >
For anyone wishing to create truly dynamic CSS at runtime with AngularJS this is what I used.
index.html
<head>
<style type="text/css" ng-bind-html="styles"></style>
</head>
cssService
this.$rootScope.myStyles = ".test { color : red; }";
This is just an example, it may be better for you to put the styles into an indexController if you have one and keep it off the $rootScope