I am trying to learn Angular 2 with ES5. But I am stuck at routing. I followed the tutorial in angular.io and added the following scripts in my index.html -
<script src="node_modules/es6-shim/es6-shim.min.js"></script>
<script src="node_modules/angular2/bundles/angular2-polyfills.js"></script>
<script src="node_modules/rxjs/bundles/Rx.umd.js"></script>
<script src="node_modules/angular2/bundles/angular2-all.umd.js"></script>
It worked all fine until I tried adding
<script src="node_modules/angular2/bundles/router.dev.js"></script>
After adding this I get the following error
System is undefined
I can see that router.dev.js uses System variable which is not defined.
How can I solve this?
After adding
<script src="node_modules/systemjs/dist/system.src.js"></script>
I am getting the following error -
EXCEPTION: No provider for Route! (class3 -> Route)
main.js
(function (app) {
document.addEventListener('DOMContentLoaded', function () {
ng.platform.browser.bootstrap(app.AppComponent, [ng.router.ROUTER_PROVIDERS]);
});
})(window.app || (window.app = {}));
app.component.js
(function (app) {
//Heroes Detail Component
app.HeroDetailComponent = ng.core.Component({
selector: 'my-hero-detail',
templateUrl: 'hero-detail.html',
inputs: ['hero']
}).Class({
constructor: function () {
}
});
//Heroes Component
app.HeroesComponent = ng.core.Component({
selector: "my-heroes",
templateUrl: 'heroes.html',
styleUrls: ['style.css'],
directives: [app.HeroDetailComponent]
}).Class({
constructor: [app.HeroService, function (_heroService) {
this.title = 'Tour of Heroes';
this._heroService = _heroService;
}],
ngOnInit: function () {
this.getHeroes();
},
onSelect: function (hero) {
this.selectedHero = hero;
},
getHeroes: function () {
this._heroService.getHeroes().then(heroes => this.heroes = heroes);
}
});
//App Component
app.AppComponent = ng.core.Component({
selector: 'my-app',
templateUrl: 'app.html',
directives: [app.HeroesComponent, ng.router.ROUTER_DIRECTIVES],
providers: [app.HeroService]
}).Class({
constructor: [ng.router.Route, function (_router) {
this.title = 'Tour of Heroes';
this._router = _router;
}]
});
//Route config
app.AppComponent = ng.router.RouteConfig([
{
path: '/heroes',
name: 'Heroes',
component: app.HeroesComponent
}
])(app.AppComponent);
})(window.app || (window.app = {}));
app.service.js
(function (app) {
app.HeroService = ng.core.Class({
constructor: function () {
this.HEROES = [
{ "id": 11, "name": "Mr. Nice" },
{ "id": 12, "name": "Narco" },
{ "id": 13, "name": "Bombasto" },
{ "id": 14, "name": "Celeritas" },
{ "id": 15, "name": "Magneta" },
{ "id": 16, "name": "RubberMan" },
{ "id": 17, "name": "Dynama" },
{ "id": 18, "name": "Dr IQ" },
{ "id": 19, "name": "Magma" },
{ "id": 20, "name": "Tornado" }
];
},
getHeroes: function () {
return Promise.resolve(this.HEROES);
},
getHeroesSlowly: function () {
return new Promise(resolve =>
setTimeout(() => resolve(this.HEROES), 2000) // 2 seconds
);
}
});
})(window.app || (window.app = {}));
I am trying to convert the Heroes Tutorial in ES5 from angular.io.
TL;DR/Solution
ES5 or angular2-all.umd.js doesn't require router.dev.js
Error was caused by misspelling. ng.router.Route should be ng.router.Router
You should try to include SystemJS in your HTML entry file:
<script src="node_modules/systemjs/dist/system.src.js"></script>
That said, I used routing with ES5 (see this plunkr: https://plnkr.co/edit/w61Ecbmuj7EfDnsYEHOS?p=info) but I didn't need to include the router.dev.js. The latter is for Angular2 applications written TypeScript or ES6 applications...
Related
Working on an application bootstrapped with create-react-app.
I need to export amChart to jpg/png/pdf formats.
I'm trying to export like in this codepen I've found: codepen export amchart example
var chart = AmCharts.makeChart("chart-header", {
"type": "pie",
"theme": "light",
"dataProvider": [ {
"country": "Czech Republic",
"litres": 301.9
}, {
"country": "Austria",
"litres": 128.3
}, {
"country": "UK",
"litres": 99
}, {
"country": "The Netherlands",
"litres": 50
}],
"valueField": "litres",
"titleField": "country",
"export": {
"enabled": true,
"menu": []
}
})
chart["export"].capture({}, function() {
this.toPNG({}, function(base64) {
console.log(base64)
})
})
But it doesn't work in my case. It gives me: Uncaught TypeError: chart.export.capture is not a function all the time.
So.. right or wrong, below is the simplified solution I've found.
On click "export" button, I make this.setState({ shouldExportChart: true }). In the "render" event listener I check whether it's true, if so, perform export and set to false.
In the same way, calling this.toJPG() we can export to JPG format.
For PDF generation I used jspdf, for archiving - jszip.
Dependencies in package.json:
"#amcharts/amcharts3-react": "^3.0.3",
"amcharts3": "^3.21.13",
"amcharts3-export": "github:amcharts/export"
"file-saver": "^1.3.3",
"fabric-webpack": "^1.6.0-rc.1"
Component.jsx:
import 'amcharts3'
import AmCharts from '#amcharts/amcharts3-react'
import 'amcharts3-export'
import 'fabric-webpack'
import FileSaver from 'file-saver'
const chartConfig = {
options: {
...,
"export": {
"enabled": true,
"menu": []
},
"listeners": [
{
event: 'rendered',
method: (e) => {
if (window.fabric) {
e.chart.export.capture({}, function () {
this.toPNG({}, function (base64) {
this.toBlob({
data: base64,
type: 'image/png'
}, (blob) => {
FileSaver.saveAs(blob, 'chart.png')
})
})
})
}
}
}
]
}
}
<AmCharts.React {...chartConfig} />
I am currently working on AM Charts Maps. I want to implement below link in angularjs. How do I convert into angular js? Should i have to implement directive?
link
<div id="chartdiv"></div>
You can add the following files for the component.
map.component.ts
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './map.component.html',
styleUrls: ['./map.component.css']
})
export class MapComponent {
private id = "mapdiv";
private options;
makeChartConfig() {
return {
"type": "map",
"theme": "light",
"colorSteps": 10,
"dataProvider": {
"map": "indiaLow",
"areas": [{
"id": "IN-AN",
"value": 4447100
}, {
"id": "IN-AP",
"value": 626932
}, {
"id": "IN-KA",
"value": 5130632
}]
},
"areasSettings": {
"autoZoom": true
},
"valueLegend": {
"right": 10,
"minValue": "little",
"maxValue": "a lot!"
}
};
}
ngOnInit() {
this.options = this.makeChartConfig();
}
}
map.component.html
<amCharts [id]="id" [options]="options" [style.width.%]="100" [style.height.px]="500"></amCharts>
I am using amcharts as a custom directive. Now I want to fetch the dataProvider of this AmChart from the output of the $http.get service using webservices.
But I am unable to get this dynamically assigned to the dataprovider of the amChart.
var app=angular.module('mainApp',[]);
app.service('dataProviderService',function($http){
this.getData= function(){
return $http.get('jaxrs/WebService/getJSONData');
};
});
app.directive('myChart',['dataProviderService',function (dataProviderService) {
return {
restrict: 'E',
replace:true,
template: '<div id="chartdiv" style="min-width: 310px; height: 400px; margin: 0 auto"></div>',
link: function (scope, element, attrs) {
scope.data={};
dataProviderService.getData().then(function(response){
scope.data=response.data;
},function(error){
scope.status="Show error";
});
var chart = false;
var initChart = function() {
if (chart) chart.destroy();
var config = scope.config || {};
chart = AmCharts.makeChart( "chartdiv", {
"theme": "none",
"type": "serial",
"dataProvider": dataProviderService.newData(),
"categoryField": "OT",
"depth3D": 20,
"angle": 30,
"categoryAxis": {
"labelRotation": 90,
"gridPosition": "start"
},
"valueAxes": [ {
"title": "availability"
} ],
"graphs": [ {
"valueField": "availability",
"colorField": "color",
"type": "column",
"lineAlpha": 0.1,
"fillAlphas": 1
} ],
"chartCursor": {
"cursorAlpha": 0,
"zoomable": false,
"categoryBalloonEnabled": false
},
"export": {
"enabled": true
}
} );
};
initChart();
}//end watch
}
}]) ;
Assuming your newData() method is an asynchronous $http.get() request like your getData() method, you need to either call makeChart after the request resolves itself or tell the chart to redraw itself by calling validateData() when the request resolves itself. Here's an example of doing this inside the chart's init event:
chart = AmCharts.makeChart( "chartdiv", {
// ... omitted ...
"dataProvider": [],
// ... omitted ...
"listeners": [{
"event": "init",
"method": function(e) {
dataProviderService.newData().then(function(response) {
e.chart.dataProvider = response;
e.chart.validateData();
}, function(error) {
// do something else
});
}
}]
} );
I have a two-level navbar that works quite well.
Top level displays main menu.
Second level displays menu for any subcontent. This is hidden when no sub items are found.
<div id="nav-wrap">
<nav id="nav" class="site-nav" data-ng-class="{'sub-nav': hasSubRoutes()}">
<div class="row">
<ul>
<li ng-repeat="route in routes">{{route.config.title}}
<ul data-ng-class="{'active': isCurrentRoute(route)}">
<li ng-repeat="subItem in route.subItems" data-ng-class="{'ui-tabs-selected ui-state-active': isCurrentSubRoute(subItem)}"><span>{{subItem.config.title}}</span>
</li>
</ul>
</li>
</ul>
</div>
</nav>
</div>
And this is what I'm replacing the above with:
<my-navbar />
And here is the code for everything else
(function () {
"use strict";
var newRoutes = {
routes:
[
{ "url": "/layout", "config": { "title": "Layout", "templateUrl": "pageContent/layout.html" } },
{ "url": "/commandments", "config": { "title": "Commandments", "templateUrl": "pageContent/commandments.html" } },
{ "url": "/release", "config": { "title": "Release Notes", "templateUrl": "pageContent/release.html" } },
{ "url": "/resources", "config": { "title": "Resources", "templateUrl": "pageContent/resources.html" } },
{
"url": "/whatever", "redirectTo": "/whatever/resources1", "config": { "title": "Whatever" },
"subItems":
[
{ "url": "/whatever/resources1", "config": { "title": "Resources", "templateUrl": "pageContent/resources.html" } },
{ "url": "/whatever/release1", "config": { "title": "Release", "templateUrl": "pageContent/release.html" } }
]
}
],
otherwise: "/layout"
};
angular.module("themeApp", ["ngRoute"])
.constant("routes", newRoutes.routes)
.constant("otherwise", newRoutes.otherwise)
.config(function ($routeProvider, routes, otherwise) {
routes.forEach(function (r) {
if (r.redirectTo) {
$routeProvider.when(r.url, {
redirectTo: r.redirectTo
});
}
if (r.subItems) {
r.subItems.forEach(function (sr) {
$routeProvider.when(sr.url, sr.config);
});
} else {
$routeProvider.when(r.url, r.config);
}
});
$routeProvider.otherwise({ redirectTo: otherwise });
})
.controller("themeController", ["$scope", "$location", "routes", function ($scope, $location, routes) {
//Public Parameters
$scope.routes = routes;
}])
.directive("myNavbar", [function () {
return {
restrict: "E",
template: '<div id="nav-wrap"><nav id="nav" class="site-nav" data-ng-class="{\'sub-nav\': hasSubRoutes()}"><div class="row"><ul><li ng-repeat="route in routes">{{route.config.title}}<ul data-ng-class="{\'active\': isCurrentRoute(route)}"><li ng-repeat="subItem in route.subItems" data-ng-class="{\'ui-tabs-selected ui-state-active\': isCurrentSubRoute(subItem)}"><span>{{subItem.config.title}}</span></li></ul></li></ul></div></nav></div>',
replace: true,
controller: function ($scope, $location) {
//COPIED FROM themeController
//Public Methods
$scope.isCurrentRoute = isCurrentRoute;
$scope.isCurrentSubRoute = isCurrentSubRoute;
$scope.hasSubRoutes = hasSubRoutes;
//Private Variables
var hasSubItems;
//Private Methods
function hasSubRoutes() {
return hasSubItems;
}
function isCurrentRoute(route) {
var current = getCurrent(route);
hasSubItems = false;
if (current) {
hasSubItems = route.subItems && route.subItems.length > 0;
}
return current;
}
function isCurrentSubRoute(route) {
return getCurrent(route);
}
function getCurrent(route) {
var path = route.url;
return $location.path().substr(0, path.length) === path;
}
}
};
}]);
}())
Here's the problem:
Right now, the menu bar shows up and I can click the menu commands, the code to manipulate the menu tabs works great! However, I get no content.
When I restore the HTML (use the 'nav-wrap' HTML from above, and copy the javascript from the directive controller into the themeController and remove '<my-navbar>'), it works perfectly. The menu works and I get the content.
Thanks,
Duane
While I didn't get any JavaScript errors, I did get a warning that I finally paid attention to.
The browser didn't like my self-closing <my-navbar />, though I swear I've done that before with other directives...but I could be wrong.
Once I changed it to:
<my-navbar></my-navbar>
everything worked.
Thank you Khanh for working with me!!!!
I have the following angular app to create a menu of sections/products.
at present when rendered and hitting the 'add' button that is rendered within each li I want to add a section/product as a sub of that section however multiple new children are created.
ultimately I wish to display a form which when submitted will create the child but that is the next step. Right now I need to limit the scope to the current section and not have multiple bound clicks.
If you need more information please state and I will post in an edit.
Some sample data data.
{
"sections":[
{
"name":"Flags",
"sections":[
{
"name":"Europe",
"sections":[],
"products":[
{ "name": "France" },
{ "name": "Germany" },
{ "name": "Ireland" },
{ "name": "England" }
]
},
{
"name": "Africa",
"sections":[],
"products":[
{ "name": "Egypt" },
{ "name": "Nigeria" },
{ "name": "Chad" }
]
},
{
"name": "South America",
"sections":[],
"products": [
{ "name": "Brasil" },
{ "name": "Argentina" },
{ "name": "Peru" }
]
}
],
"products":[]
},
{
"name": "Maps",
"sections":[
{
"name": "Africa",
"sections":[],
"products":[
{ "name": "Egypt" },
{ "name": "Nigeria" },
{ "name": "Chad" }
]
},
{
"name": "South America",
"sections":[],
"products": [
{ "name": "Brasil" },
{ "name": "Argentina" },
{ "name": "Peru" }
]
}
],
"products":[]
}
],
"products":[]
}
The app.
'use strict';
var menuApp = angular.module('menuApp', []);
menuApp
.directive('sections', function () {
return {
restrict: "E",
replace: true,
scope: {
sections: '='
},
template: '<ul><section ng-repeat="section in sections" section="section" /></ul>'
};
})
.directive('section', function ($compile) {
return {
restrict: "E",
replace: true,
scope: {
section: '=section'
},
template: '<li class="section">{{section.name}} <button ng-click="addSub(section)">Add</button></li>',
link: function (scope, element, attrs, controller) {
if (angular.isArray(scope.section.sections)) {
element.append("<sections sections='section.sections'></sections>");
$compile(element.contents())(scope);
}
if(angular.isArray(scope.section.products)){
element.append("<products products='section.products'></products>");
$compile(element.contents())(scope);
};
},
controller : function($scope){
console.log($scope);
$scope.addSub = function (section){
//console.log(section,'Adding Sub');
section.sections.push({"name":"Section","sections":[],"products":[]});
};
}
};
})
.directive('products', function () {
return {
restrict: "E",
replace: true,
scope: {
products: '='
},
template: '<ul><product ng-repeat="product in products" product="product"></product></ul>'
};
})
.directive('product', function ($compile) {
return {
restrict: "E",
replace: true,
scope: {
product: '='
},
template: '<li class="product">{{product.name}}</li>'
};
});
menuApp.controller('menuCtrl', function menuCtrl($scope,$http) {
$http.get('/ajax/getvenuesmenu?venueID='+venueMenu.venueId).success(function(resp) {
$scope.sections = resp;
});
$scope.add = function(data){
data.push({"name":"Section","sections":[]});
};
});
Took me a bit to figure it out but here's the basic problem, you are compiling the full contents of section 2 extra times and each compile seems to add a new event handler.
Instead of compiling the contents of element each time you make an append of new template, compile the template itself (outside of the DOM) and then append the compiled template. This way the ng-click handler doesn't get compiled again other than initial scope creation
Here's an abbreviated version with one template appended:
link: function (scope, element, attrs, controller) {
if (angular.isArray(scope.section.sections)) {
/* compile outside of the DOM*/
var subsections = $compile("<sections sections='section.sections'></sections>")(scope);
/* append compilation*/
element.append(subsections);
}
DEMO
Another approach would be to create a complete template string in link by checking for subsections and products, then compiling everything all at once....instead of using template option
Code for alternate approach compiling complete section at once:
.directive('section', function ($compile, $timeout) {
return {
restrict: "E",
scope: {
section: '=section'
},
link: function (scope, element, attrs, controller) {
var template = '<li class="section">{{section.name}} <button ng-click="addSub(section)">Add</button>';
if (angular.isArray(scope.section.sections)) {
template += "<sections sections='section.sections'></sections>";
}
if (angular.isArray(scope.section.products)) {
template += "<products products='section.products'></products>";
};
template += '</li>';
var compiledTemplate = $compile(template)(scope);
element.replaceWith(compiledTemplate);
scope.addSub = function (section) {
section.sections.push({ "name": "Section", "sections": [], "products": []
});
};
}
};
})
DEMO-Alt