Inlining single file components - angularjs

I'm considering vue.js as a migration target for an AngularJS (1.x) webapp.
This app loads a lot of "widgets" in its main page
<script optimize-inline src="data/widgets/solargraph/solargraph.js"></script>
<script optimize-inline src="data/widgets/sensorgraph/sensorgraph.js"></script>
<script optimize-inline src="data/widgets/astralgraph/astralgraph.js"></script>
Each one of these widgets is a custom directive which defines a custom tag.
For example data/widgets/astralgraph/ contains the following files
data/widgets/astralgraph/astralgraph.css
data/widgets/astralgraph/astralgraph.html
data/widgets/astralgraph/astralgraph.js
and is instanced as <astralgraph class="stickies-container"></astralgraph> in the main page.
.css is pulled in by the .html file via the following line
<link optimize-inline
rel="stylesheet"
type="text/css"
href="data/widgets/astralgraph/astralgraph.css?reload_times={## when ##}">
and the .html file is pulled in via templateUrl: 'data/widgets/astralgraph/astralgraph.html' in the .js file.
This normally causes the browser to pull in each .js file, which then pull in the .html files and so on.
Now comes the important part.
There are these optimize-inline markers.
These are not used by JavaScript, but are used by the Python server to inline the widgets into the main page. This way only one file is returned and no widget files (no .js, no .html and no .css) need to be loaded by the browser, since they are all in the (now big) main page.
First the content of the .js files is loaded by the server, inserted into the main page into a <script>-tag, but before writing that <script>-tag the templateUrl line is parsed, the content of the .html file written into a <script type="text/ng-template">-tag, like <script type="text/ng-template" id="data/widgets/astralgraph/astralgraph.html">...HTML IS HERE...</script>
This is the way the webapp works, it may have its drawbacks vs using webpack or the like, but it has its benefits which I appreciate a lot.
Now I've been checking out the feasibility of moving over to vue.js, and using single file components (astralgraph.vue) together with httpVueLoader solves the issue of being able to create widgets and load them without requiring a build system like webpack.
Now I'm still missing the ability to inline these .vue files into one big main page. How can I archive this without resorting to webpack, but by using the with open('widgets/astralgraph.vue') as file: ... in the Python server? By this I don't mean how do I implement this in Python, but rather how do I have to structure the resulting page so that it is a valid vue.js app.
Like in AngularJS I have the transformation from
/main.html
<script src="widget/example1.js"/>
<script src="widget/example2.js"/>
/widget/example1.js
/widget/example1.html
/widget/example1.css
/widget/example2.js
/widget/example2.html
/widget/example2.css
to
/main.html
<!-- example1 -->
<style>content of example1.css</style>
<script type="text/ng-template" id="example1.html">content of example1.html</script>
<script>content of example1.js</script>
<!-- example2 -->
<style>content of example2.css</style>
<script type="text/ng-template" id="example2.html">content of example2.html</script>
<script>content of example2.js</script>
In vue.js it would be from
/main.html
components: {
'example1': httpVueLoader('widget/example1.vue'), // optimize-inline
'example2': httpVueLoader('widget/example1.vue') // optimize-inline
},
/widget/example1.vue
/widget/example2.vue
to
// this I don't know
As for the httpVueLoader('widget/...') lines, I would regex them out and adapt it accordingly. It also wouldn't be an issue to load the .vue file into an XML-parser like BeautifulSoup to modify the .vue file contents as necessary before writing it into the main page. They could have an // optimize-inline comment in order to tell the server which components should get inlined and which not. In essence it would be a very rudimentary bundler which bundles the files on each page fetch.
For those who wonder what this astralgraph widget looks like:

This is possible, but you'll lose the ability to use scoped css.
The normal, non-optimized setup will look like this:
app.html:
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/http-vue-loader"></script>
</head>
<body>
<div id="my-app">
<my-component></my-component>
</div>
<script type="text/javascript">
new Vue({
el: '#my-app',
components: {
'my-component': httpVueLoader('my-component.vue')
}
});
</script>
</body>
</html>
my-component.vue:
<template>
<div class="hello">src: Hello {{who}}</div>
</template>
<style>
.hello {
background-color: #ffe;
}
</style>
<script>
module.exports = {
data: function() {
return {
who: 'world'
}
}
}
</script>
The optimize-inline'd file will then need to look like this:
app.html
<html>
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<!--============== my-component begin ==============-->
<template id="my-component-template">
<div class="hello">src: Hello {{who}}</div>
</template>
<style>
.hello {
background-color: #ffe;
}
</style>
<script type="text/javascript">
Vue.component('my-component', {
template: '#my-component-template',
data: function() {
return {
who: 'world'
}
}
});
</script>
<!--============== my-component end ==============-->
<div id="my-app">
<my-component></my-component>
</div>
<script type="text/javascript">
new Vue({
el: '#my-app',
components: {
//'my-component': httpVueLoader('my-component.vue')
}
});
</script>
</body>
</html>
So basically the server needs to modify the content of the .vue file in the following way:
rename the <template> to <template id='my-component-template'>
replace module.exports = { with Vue.component('my-component',
{ template: '#my-component-template', and add a closing ); to the last line of the script.
Then insert this modified content into the app.html file and comment out the 'my-component': httpVueLoader('my-component.vue') line, optionally also removing the <script src="https://unpkg.com/http-vue-loader"></script> line.

Related

Issue with Routing and ng-view

I have a problem with my single Page Application. When I start my Project with the keyuser.html as first site(home page) the Table from the connected Database is shown with the Data. When I use the normal home.html as entry Point for my Program, I can click the Hyperlink to my keyuser.html file, the controller does the necessary routing and I am on my keyuser.html site. But here is just the update Button, but not my Table with my Data.
+++++++++ keyuser.html ++++++
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button onclick="update()">Update</button>
<div id="flexGrid"></div>
</body>
</html>
<script>
var cv = new wijmo.collections.CollectionView();
var flexGrid = new wijmo.grid.FlexGrid('#flexGrid');
flexGrid.itemsSource = cv;
// Get Data
wijmo.httpRequest("/api/Colors", {
success: function (xhr) {
cv.sourceCollection = JSON.parse(xhr.response);
}
});
</script>
++++++++++++ control.js ++++++++++++++
var app = angular.module("myApp", ["ngRoute"]);
app.config(function ($routeProvider) {
$routeProvider
.when("/keyuser", {
templateUrl: "keyuser.html"
});
++++++++++++ home.html ++++++++++++++
<!DOCTYPE html>
<html>
<head>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.min.js"></script>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.grid.min.js"></script>
</head>
<body>
<div ng-app="myApp">
Stammdaten
<div ng-view></div>
</div>
There are multiple things wrong here.
First of when loading HTML templates with routing the entire template gets loaded into the ng-view element. Which means that in your case the doctype tag, html tag, body tag and all the other tags are loading twice which might break your page.
Secondly when using AngularJS do not write your javascript in script tags, instead start using controllers, directives, componentens and services.
And last, make sure to include the correct AngularJS scripts like angular.js and angular-route.js
In general i highly recommend just going through the basics of AngularJS before proceeding because i feel like you are missing those.

AngularJS controller reference becomes undefined

I've just started learning AngularJS and built a small yet simple application, which consists of multiple controllers as shown here:
I have a SiteMaster.html page, inside this page I use ng-view as shown here:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="Scripts/angular.min.js"></script>
<script src="Scripts/angular-route.min.js"></script>
<script src="Scripts/app.js"></script> <!-- Consists of Routing -->
<script src="Templates/Params/paramsController.js"></script>
<title>Angular Routing Tutorial</title>
</head>
<body ng-app="mainApp">
<h1>sitemaster Template</h1>
<div ng-view></div>
</body>
</html>
As you can see the last script tag is the controller for the params page, Ideally I don't want this script tag reference on the SiteMaster instead I would like to place it inside the params.HTML page that way it's only loaded when it's required, now I've moved this script tag from the SiteMaster.Html to the params.Html page as shown here:
<script src="paramsController.js"></script>
<div ng-controller="paramsCtrl">
<h1>
Passed a parameter via url
</h1>
</div>
Yet when I run the project I get the following error:
Error: [ng:areq] http://errors.angularjs.org/1.5.2/ng/areq?p0=paramsCtrl&p1=not%20a%20function%2C%20got%20undefined
So my question is, how do I get this paramsController.js reference to work within the params.html page? instead of having it on the SiteMaster.html page?
Please note, when the paramsController.js is placed inside the SiteMaster.Html page it works as expected.
Any help would be appreciated.
Update
After adding ocLazyLoad I have done the following:
Added the script reference to sitemaster.html
Inside my app.js I have done the following:
var app = angular.module('mainApp', ['ngRoute', "oc.lazyLoad"]);
Inside my paramsController.js I have the following:
angular.module("mainApp").controller('paramsCtrl', function ($scope, $routeParams, $ocLazyLoad) {
console.log($routeParams);
var param1 = $routeParams.page_number;
alert(param1);
});
And now I've hit a road block I'm unsure where or how I go about loading this controller via ocLazyLoad ? I following this documentation : https://oclazyload.readme.io/docs
First of all, your path is wrong. Change
<script src="paramsController.js"></script>
to
<script src="Templates/Params/paramsController.js"></script>
Also, include the following in your <head> section:
<base href="/">
And the second thing, I'm not sure, even that will not work as Angular has already been initialized. So you need to use a library like ocLazyLoaded to dynamically load resources.
Also, consider reading this post https://stackoverflow.com/a/17675432/2405040
Update:
In your params.html
<!-- Remove this line
<script src="paramsController.js"></script>
-->
<div ng-controller="paramsCtrl">
<h1>
Passed a parameter via url
</h1>
</div>
And now modify your $routeProvider configuration:
(From the docs https://oclazyload.readme.io/docs/with-your-router)
$routeProvider.
when('/foo', {
templateUrl: 'Templates/Params/params.html',
resolve: {
loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
// you can lazy load files for an existing module
return $ocLazyLoad.load('Templates/Params/paramsController.js');
}]
}
})

Angular call to other js functions

I am using ng-include to call the header content and the footer,
like this :
<div ng-app="app">
<div ng-include="'pages/header.html'"></div>
<script src="js/Listenrs.js"></script>
<!-- more content -->
</div>
I have a native js file with event listeners that is in the js folder, without the ng-include all the listeners are working but when using ng-include all the event listeners are ignored, a check alert inside the script shows the browser did not ignore the file but besides the alert the listeners dont work.
You should include JQuery library before AngularJS library in your index file for any lazy-loaded scripts to be compiled and added to the JS namespace automatically. Please look at the following code.
[index.html]
<head>
<script src=jquery.js>
<script src=angular.js>
</head>
<body>
<ng-include src="templateurl"></ng-include>
</body>
[template.html]
<head>
<script>
alert(" script loaded")
</script>
</head>
You will get the alert in the above configuration.

JavaScript of HTML is loaded before than ng-include finished

My problem is that my html load the .js and .css before ng-include finished,
Then this libraries not found the elements that are loaded in include.
Example:
<html>
<head>
<link href="assets/css/modern.min.css" rel="stylesheet" type="text/css">
<script src="assets/js/modern.min.js"></script>
</head>
<body>
<div ng-include="html_2"></div>
</body>
</html>
when modern.js is loaded, html_2 is no finished so this not working because modern no found the element of html_2
Where are you loading your angular application script? Maybe you can use a library like headJS or labjs to ensure the order of your scripts load.
e.g.
head.load(modernjsScript, function(){
head.load(angularjs, function(){
head.load(yourApplicationsScript)
});
})

AngularJS writing an app with no server

I'm trying to write an AngularJS client side only app.
I thought I might be able to load it from chrome by typing in the address bar:
file:///C:/path/to/project//index.html
I also tried to invoke chrome with the flag --allow-file-access-from-files
Unfortunatly nothing happened - just the busy sign on the tab name is working.
Why does is not loading my app?
I'm using the following code:
index.html:
<!doctype html>
<html ng-app="app">
<head>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-route.js"></script>
<script src="app.js"></script>
<title></title>
</head>
<body style="background-color: white;">
<h1>Index</h1>
<a id="link" href="/login">Go to login</a>
<ng-view></ng-view>
</body>
</html>
app.js:
angular.module('app', ['ngRoute']).
config(function($routeProvider) {
$routeProvider.
when('/', {controller: HomeCtrl, templateUrl: 'app.html'}).
when('/login', {controller: LoginCtrl, templateUrl: 'login.html', resolve: function() {}}).
otherwise({redirectTo:'/'});
});
function HomeCtrl($scope) {
$scope.numbers = [1,2,3,4,5];
}
function LoginCtrl($scope) {
}
app.html:
<div ng-controller="HomeCtrl">
<ul ng-repeat="number in numbers" >
<li>{{number}}</li>
</ul>
</div>
Edit:
2 possible solutions:
Close all chrome instances and run from command line: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --allow-file-access-from-files --allow-file-access
Run Firefox which does not have this restriction (Like #GeekBoy mentioned)
As far as I know google chrome does not allow javascripts to be run from file system. But I did a quick google search and found this. Might be useful
Link
On the flipside you can use firefox. Firefox doesn't have such restrictions as far as I know
Try changing the following lines:
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-route.js"></script>
to
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular.min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.16/angular-route.js"></script>
I think since you're using a file-based way to get at index.html, it's assuming the // also points to a local system resource. By specifically indicating http://, it will look at the actual locations.
I know that this is an old question but for anyone that might still be interested, here is a small project that demonstrates how to write an angularjs client-side app. It is a complete AngularJS 1.63 single page application with routing that does not need a web server: https://github.com/jlinoff/aspa-nows.
There were two key challenges to getting it working: getting the the page href references right because of the newly introduced default hash prefix: ! (see aa077e8 for details), and embedding the template HTML code into index.html as ng-template scripts to avoid CORS errors. Once those fixes were made, it worked as expected.
The project README.md explains what needed to be done in detail and the full source code is available.

Resources