Styling dynamically generated SVG in Polymer - javascript

I am trying to wrap the Javascript charting library Chartist in Polymer elements. Everything works as expected except the styling. The chart appears unstyled (just the way every chartist example does when no css is loaded).
I created a style module as explained in https://www.polymer-project.org/1.0/docs/devguide/styling.html#style-modules, included it in my element with both a link[rel=import] and style tag and copied/pasted all contents of chartist.css into the style-module. Does not work in Firefox/Chrome.
To prove the style module is loaded and processed at all, I included a ul#message tag and styled it with a directive. Works like a charm.
I guess the problem is that chartist creates SVG charts. Does anyone know how to treat styling SVG or can point me to a direction?
Here is my code so far:
Style module:
<dom-module id="chartist-styles">
<template>
<style>
:host { display: inline-block; }
#messages { list-style-type: none; }
/* All the contents of chartist.css */
</style>
</template>
</dom-module>
Polymer element:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<!-- Includes chartist.js via script tag -->
<link rel="import" href="../chartist-import.html">
<link rel="import" href="../chartist-styles.html">
<dom-module id="test-charts-line">
<template>
<style include="chartist-styles"></style>
<div id="chartist" class="ct-chart"></div>
<ul id="messages"></ul>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'test-charts-line',
properties: {
chart: {
notify: true
},
data: {
type: Object,
value: function(){
return {};
}
},
options: {
type: Object,
value: function(){
{}
}
}
},
observers: [
'updateChart(data.*, options.*)'
],
updateChart: function(){
this.chart = null;
if(this.options == null){
this.chart = new Chartist.Line( '#chartist' , this.data );
} else {
this.chart = new Chartist.Line( '#chartist' , this.data, this.options );
}
let child = document.createElement('li');
child.textContent = 'blub';
Polymer.dom(this.$.messages).appendChild(child);
},
ready: function(){
// Taken from a getting-started example on
// https://gionkunz.github.io/chartist-js/examples.html
this.data = {
// A labels array that can contain any sort of values
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
// Our series array that contains series objects or
// in this case series data arrays
series: [
[5, 2, 4, 2, 0]
]
};
this.options = {
width: 300,
height: 200
};
}
});
})();
</script>
</dom-module>

Found the solution to my problem in the Polymer docs: styles for dynamically created DOM nodes can be applied by calling
ready: function() {
this.scopeSubtree(this.$.container, true);
}
where this.$.container references a DOM node in the template, in my above example it would be this.$.chartist.
Not for use on Polymer elements. If the subtree that you scope
contains any Polymer elements with local DOM, scopeSubtree will cause
the descendants' local DOM to be styled incorrectly.

Related

How to attach nuxeo-tree component to Polymer v1 app

I want to add the <nuxeo-tree> component to my Polymer v1 app, but I'm seeing an error in the console. This is the code I've tried:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/nuxeo-ui-elements/nuxeo-tree/nuxeo-tree.html">
<link rel="import" href="./myVerySpecialLib-import.html">
<dom-module id="my-app">
<template>
tree:<br/>
<nuxeo-tree data="[ title: 'root', children: [ { title: 'a', children: [] }, { title: 'b', children: [ {title: 'x'}, {title: 'y'} ] } ]]]" controller="[[controller]">
<template>
<template is="dom-if" if="[[!opened]]">
<iron-icon icon="hardware:keyboard-arrow-right" toggle></iron-icon>
</template>
<template is="dom-if" if="[[opened]]">
<iron-icon icon="hardware:keyboard-arrow-down" toggle></iron-icon>
</template>
<span select>My title is: [[item.title]]</span>
<span>Am I a leaf? [[isLeaf]]</span>
</template>
</nuxeo-tree>
</template>
<script>
Polymer({
is: 'my-app',
properties: {
data: {
type: String,
value: "[ title: 'root', children: [{ title: 'a',children: []},{title: 'b',children: [{title: 'x'},{title: 'y'}]}]]",
},
opened: {
type: Boolean,
value: true,
},
},
controller: {
// How to get children of a node. Returns a promise.
getChildren: function(node) {
return Promise.resolve(node.children);
},
// Logics you may want to have to control if a node is a leaf.
isLeaf: function(node) {
return node.children.length === 0;
}
},
});
</script>
</dom-module>
And the myVerySpecialLib-import.html file:
controller = {
// How to get children of a node. Returns a promise.
getChildren: function(node) {
return Promise.resolve(node.children);
},
// Logics you may want to have to control if a node is a leaf.
isLeaf: function(node) {
return node.children.length === 0;
}
};
This is the console error:
TypeError: this.controller.isLeaf is not a function
I tried to add the JSON data as a property and also directly into the data field, but neither had a positive effect. How do I fix this?
The myVerySpecialLib-import.html seems to contain a global variable declaration, but that doesn't really help you because <nuxeo-tree> expects controller on the container element (not in a global variable).
Also, your data binding for <nuxeo-tree>.controller is malformed (it's missing a ] at the end):
<nuxeo-tree controller="[[controller]">
And controller probably should be declared as a property if you're binding it. It's currently declared outside the properties object.
// DON'T DO THIS
/*
properties: {...},
controller: {...}
*/
// DO THIS
properties: {
controller: {...}
}
I recommend setting this.controller in the ready() callback of the parent element of <nuxeo-tree> (where this is the container). You could also set <nuxeo-tree>.data via a binding to simplify your HTML template, and that property could be initialized in ready() as well.
ready: function() {
this.data = /* insert data object here */;
this.controller = /* insert controller object here */;
}
demo

How to add html label to cytoscape graph node

I use cytoscape.js for show relations between nodes.
I want to create different stylish labels for one node.
I want more complicate stylish labels, then in the cytoscape.org official example.
How can i do it?
Sample image of my problem:
I solved my problem with extention for create html labels for cytoscape.
Extention on github: cytoscape-node-html-label
Extention demo: demo
cy.nodeHtmlLabel(
[
{
query: 'node',
tpl: function(data){
return '<p class="line1">line 1</p><p class="line1">line 2</p>'}
}
]
);
.line1{
font-size: 10px;
}
.line1{
font-size: 12px;
}
First, there must be an area to draw the graph. Add a tag to index.html, then within the body section, add a div element named "cy", like so: . This creates the body of the webpage, which in turn holds a div element named cy. Naming the element makes it easy to later access and modify this element for styling and passing to Cytoscape.js.
index.html should now look like this:
<!doctype html>
<html>
<head>
<title>Tutorial 1: Getting Started</title>
<script src='cytoscape.js'></script>
</head>
<body>
<div id="cy"></div>
</body>
</html>
Next, the style of the graph area must be slightly modified via CSS (putting a graph into a 0 area div element is rather uninteresting). To accomplish this, add the following CSS code between and :
<style>
#cy {
width: 100%;
height: 100%;
position: absolute;
top: 0px;
left: 0px;
}
</style>
How about making the graph look nicer? Cytoscape.js provides a multitude of styling options for changing graph appearance. The initialization of the graph may be modified to change default style options, as follows:
var cy = cytoscape({
container: document.getElementById('cy'),
elements: [
{ data: { id: 'a' } },
{ data: { id: 'b' } },
{
data: {
id: 'ab',
source: 'a',
target: 'b'
}
}],
style: [
{
selector: 'node',
style: {
shape: 'hexagon',
'background-color': 'red'
}
}]
});
Next up is displaying labels in the graph so that nodes can be identified. Labels are added via the 'label’ property of style. Since labels are already provided (via the id property of data), we’ll use those. If other data properties are provided, such as firstname, those could be used instead.
style: [
{
selector: 'node',
style: {
shape: 'hexagon',
'background-color': 'red',
label: 'data(id)'
}
}]
The final common component of a graph in Cytoscape.js is the layout. Like style, elements, and containers, layout is also specified as a part of the object passed to cytoscape during construction. To the existing cy object, add (after elements):
layout: {
name: 'grid'
}
check this out, it will help you - http://blog.js.cytoscape.org/2016/05/24/getting-started/

How to Troubleshooting Binding

I found a cool project (RoboJS), and I forked it: Forked Repo. My plan was to try to add a nice front end with Polymer 1.0 and learn a little in the process.
What I am having trouble with is getting the binding to show in my component. I've built a really simple "robot" component to show the status of the robot during the game.
To start, all I want to do is to show the name in the title, but it comes out blank. Here's the component:
<dom-module id="robojs-robot-status">
<template>
<div>Robot Name <span>[[robot]]</span><span>{{test}}</span></div>
</template>
</dom-module>
<script>
Polymer({
is: "robojs-robot-status",
properties: {
robot: {
type: String,
value: "testing"
},
test: {
type: String,
value: "testing2"
}
},
ready: function() {
},
init: function() {
console.log(this.robot);
console.log(this.test);
}
});
</script>
On the parent component, I set the robot attribute:
Here's the attribute:
<link rel="import" href="robojs-robot-status.html">
<robojs-robot-status robot="{{robot}}"></robojs-robot-status>
And, I have a script that, for now, sets the value on the ready event:
Polymer({
is: "robojs-arena",
properties: {
robot: {
type: String,
value: "hello"
}
},
ready: function() {
this.games = window.roboJS.games;
console.log(this.games);
//this.robot = {name: "hello"};
this.robot = "hello";
},
init: function() {
console.log("******* init *******");
console.log(this.robot);
document.querySelector("robojs-robot-status").init();
},
pause: function() {
window.roboJS.pause();
},
start: function() {
console.log(window.roboJS);
window.roboJS.resume();
}
});
[[robot]] is blank. {{test}} binds to "testing2".
Using {{robot}} or [[robot]] doesn't make a difference. So, that doesn't have an impact.
If I remove, the "robot" attribute in the parent component, the value works. It shows "testing". So, it is binding, but not with the actual value.
Beyond figuring out what I am doing wrong in this instance, is there a good way to troubleshoot? I am having similar issues in other places in the app.
If this were Angular + jQuery, I would do something like this:
$('robotjs-robot-status').scope().$eval("robot")
I could type that into the developer console in Chrome and see what it said and troubleshoot. I could also use the Batarang extension in Chrome.
With Polymer, I am not sure where to start. Any help/ideas?
If the parent snippet is posted here exactly as it appears in the code, then it's probably to blame. The
<link rel="import" href="robojs-robot-status.html">
should be outside , like
<dom-module id="robojs-robot">
<link rel="import" href="robojs-robot-status.html">
<template>
<robojs-robot-status robotname="{{robotname}}"></robojs-robot-status>
</template>
<script>
Polymer({
is: "robojs-robot",
ready: function() {
console.log('setting to Dilly');
this.robotname = "Dilly";
},
properties: {
robotname: {
type: String,
value: "hello"
}
},
});
</script>
</dom-module>
and then if status is
<dom-module id="robojs-robot-status">
<template>
<div>Robot Name <span>[[robotname]]</span></div>
</template>
<script>
Polymer({
is: "robojs-robot-status",
properties: {
robotname: {
type: String,
value: "testing",
observer: '_robotnameChanged'
}
},
_robotnameChanged: function(newValue, oldValue) {
console.log('_robotnameChanged: newValue='+newValue+' oldValue='+oldValue)
}
});
</script>
</dom-module>
everything works for me.
PS: properties seem to be not really needed here as binding is unidirectional.

Can I use iron-localstorage and iron-ajax with highcharts

I have a polymer element that uses iron-ajax to create a highchart. Could I now incorporate iron-localstorage so the chart will render from the data in ls unless ls is empty in which case it will call iron-ajax to load the data from an api?
My working element is as follows:
<dom-module id="sales-chart">
<template>
<iron-ajax id="ajax" url="{{url}}" last-response="{{data}}"></iron-ajax>
<div id="container" style="max-width: 600px; height: 360px;"></div>
</template>
<script>
Polymer({
is: "sales-chart",
properties: {
url: String,
data: Object
},
observers: [
// These functions only run once the observed properties contain
// something other than undefined.
'_requestData(url)',
'_chartData(data)'
],
_requestData: function(url) {
// Note: Use `generateRequest()` instead of the `auto` property
// because `url` may not be available when your element is
// first created.
this.$.ajax.generateRequest();
},
_chartData: function (data) {
$(this.$.container).highcharts({
chart: {
type: 'spline',
renderTo: 'container'
},
series: [{
data: (data.series)
}]
});
}
});
</script>
</dom-module>
Something along these lines should work (did't test it tough):
<dom-module id="sales-chart">
<template>
<iron-ajax id="ajax" url="{{url}}" last-response="{{data}}"></iron-ajax>
<div id="container" style="max-width: 600px; height: 360px;"></div>
<iron-localstorage name="{{url}}"
value="{{data}}"
on-iron-localstorage-load-empty="_requestData">
</iron-localstorage>
</template>
<script>
Polymer({
is: "sales-chart",
properties: {
url: String,
data: Object
},
observers: [
// These functions only run once the observed properties contain
// something other than undefined.
'_chartData(data)'
],
_requestData: function() {
// Note: Use `generateRequest()` instead of the `auto` property
// because `url` may not be available when your element is
// first created.
this.$.ajax.generateRequest();
},
_chartData: function (data) {
$(this.$.container).highcharts({
chart: {
type: 'spline',
renderTo: 'container'
},
series: [{
data: (data.series)
}]
});
}
});
</script>
</dom-module>

How do you make reusable/template charts in c3js

I'm trying to use C3.js(c3js.org) to make charts, but I want to specify everything but the data(and any other minor deviations unique to that chart) once then reuse that for all charts of that variation(a specific configuration of a chart).
All the documentation and all examples I've found for C3.js only deal with how you make a single chart. Applying that to multiple charts means a lot of repeated code and doesn't ensure consistency when making changes.
The only thing related to this that I've found is a concept on making reusable charts in D3.js(d3js.org), the underlying library used by C3.js, and an implementation inspired by that concept. That doesn't really help me because I want the higher-level abstraction that C3.js provides but these may give you an idea what I'm looking for.
I have found no info on this but one idea is to make a chart type that is based on an existing type but that also include the extra configuration(for example make a new chart type called 'horizontalbar' based on the existing 'bar' chart type).
Here is a chart I've made, bindto and columns are the unique parts of this chart, the rest should be part of a template, but I don't know how.
var chart = c3.generate({
bindto: '#chart',
data: {
columns: [
['data1', 125.2],
['data2', 282.7],
['data3', 3211.1],
['data4', 212.2],
['data5', 131.1],
['data6', 329.7]
],
type: 'pie',
order: null
},
pie: {
label: {
format: function (value, ratio, id) {
return d3.format('.1f')(ratio*100)+'%'; //percent with one decimal
}
}
},
tooltip: {
format: {
value: function (value, ratio, id, index) {
return value+'mkr ('+d3.format('.1f')(ratio*100)+'%)'; //example: 155.2mkr (3.3%)
}
}
},
legend: {
item: {
onclick: function () {} //disable clicking to hide/show parts of the chart
}
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.9/c3.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.9/c3.min.js"></script>
<div id="chart"></div>
I have this in my html:
<script src="../static/js/test.js"></script> <!-- this is the js file contains the drawChart function -->
<div class='chart'>
<div id='chart1'></div>
</div>
<script>drawChart('chart1','pathToCsvData',ture, 200);</script>
in my js code:
function drawChart(toChart,dataURL,showLegend,chartHeight)
{
var chart1 = c3.generate({
bindto: toChart,
data: {
url: dataURL,
labels: false
},
color: {pattern: ['green','black']},
zoom: {enabled: false},
size: {height: chartHeight},
transition: {duration: 0},
legend: {show: showLegend}
});
}
the js code serve as a template, and I can as many different template I want, put them in functions, with customized chart parameters, and the call the js function in html code.

Categories