I try to write HTML widget with JavaScript code in the thinger.io Dashboard. The data from the "thing" can be used in the HTML by inserting the next code {{value}} into some HTML tag body.
But, I cannot use it in the JavaScript block.
Pure HTML widget:
For example use in the HTML widget:
<h1>Millis data</h1>
<p>Millis value is </p><b>{{value}}</b>
The result of this
Widget with JavaScript block (don't work as needed):
I tried to use the same data for plotting (Plotly example).
I can use in the HTML tag id=data, but, I don't know how to use this data in the JavaScript block.
My try:
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="data" value={{value}}>Data - {{value}}</div>
<div id="tester" style="width:900px;height:300px;"></div>
<script>
TESTER = document.getElementById('tester');
Plotly.newPlot( TESTER, [{
x: [1, 2, 3, 4, 5],
y: [1, 2, 4, 8, 16] }], {
margin: { t: 0 } } );
</script>
And resulting widget:
- The data on the plot are hard coded for example only.
I need help with integrating the ```{{value}}`` data into a JavaScript code in the thinger.io HTML widget.
There doesn't appear to be documented way to get the value in the HTML widget.
Instead it is possible to use a MutationObserver to listen to changes to the data div. This can create the Plotly plot when there is an update to the div. (You can also use jQuery to handle the change but this approach uses fewer dependencies.)
<div id="data" value={{value}}>Data - {{value}}</div>
<div id="tester" style="width:900px;height:300px;"></div>
<script>
var createPlot = function(dataEl) {
let value = dataEl.getAttribute('value');
console.log(`creating plot with: ${value}`);
if (value) {
TESTER = document.getElementById('tester');
Plotly.newPlot( TESTER, [{
x: [1, 2, 3, 4, 5],
y: JSON.parse(value) }], {
margin: { t: 0 } }
);
}
}
var dataEl = document.getElementById('data');
var script = document.createElement('script');
script.onload = function () {
createPlot(dataEl);
};
script.src = "https://cdn.plot.ly/plotly-latest.min.js";
document.head.appendChild(script);
var observer = new MutationObserver(function(mutations) {
// create plot only if Plotly has loaded
if (Plotly) {
createPlot(dataEl);
}
});
// setup the observer
var target = document.querySelector('#data');
var config = { attributes: true, attributeFilter: [ 'value'] };
observer.observe(target, config);
</script>
The plotly script may be loaded after the div is updated. So the trigger to create the plot could be either the script loading or the div mutating. In either case both plotly needs to be loaded and the div needs to be updated.
The value attribute is also a string so it needs to be converted from a string using JSON.parse().
Testing
To test this, I created an HTTP device and added a property with the string value [20,41,63,83,103]:
The widget was configured using this property:
And the result:
Related
The react application received a visualization code, and I used the unescape function to get the HTML codes in string. The string has a div tag with the id of the visualization and script tag that consists of the javascript to generate the visualization.
import React from "react";
import parse from "html-react-parser";
export default function App() {
// This code is received from the backend
const vizCode =
"%3Cdiv%20id%3D'chartdiv_1614071920146'%3E%3C%2Fdiv%3E%3Cscript%20type%3D'text%2Fjavascript'%3E%20var%20width_1614071920146%20%3D%20700%3Bvar%20height_1614071920146%20%3D%20700%3Bvar%20chart_1614071920146%20%3D%20c3.generate(%7Bbindto%3A%20'%23chartdiv_1614071920146'%2C%20size%3A%20%7B%20width%3A%20(width_1614071920146%20-%2020)%2C%20height%3A%20(height_1614071920146%20-%2020)%20%7D%2C%20data%3A%20%7Bcolumns%3A%20%5B%20%5B'%20'%2C%201.0%2C%201.0%2C%203.0%2C%205.0%2C%205.0%5D%20%5D%2C%20type%3A%20'spline'%7D%2C%20legend%3A%20%7Bshow%3Afalse%7D%2C%20axis%3A%20%7B%20x%3A%20%7B%20type%3A%20'category'%2C%20categories%3A%20%5B'New%20Course'%2C'group%20course%201'%2C'Testkurs%20'%2C'test_course'%2C'Moodle4CollaborativeMOOCs'%5D%20%7D%20%7D%7D)%3B%3C%2Fscript%3E";
const unescapedVizCode = unescape(vizCode);
console.log(unescapedVizCode);
// Output -> <div id='chartdiv_1614071920146'></div><script type='text/javascript'> var width_1614071920146 = 700;var height_1614071920146 = 700;var chart_1614071920146 = c3.generate({bindto: '#chartdiv_1614071920146', size: { width: (width_1614071920146 - 20), height: (height_1614071920146 - 20) }, data: {columns: [ [' ', 1.0, 1.0, 3.0, 5.0, 5.0] ], type: 'spline'}, legend: {show:false}, axis: { x: { type: 'category', categories: ['New Course','group course 1','Testkurs ','test_course','Moodle4CollaborativeMOOCs'] } }});</script>
let objectData = parse(unescapedVizCode);
let divData = objectData[0];
let scriptData = objectData[1].props.dangerouslySetInnerHTML.__html;
return (
<div className="App">
{objectData}
</div>
);
}
Here I have provided the codesandbox link
I used the dangerouslySetInnerHtml to display the visualization but unable to visualize the chart. I have added a test.html in the public folder to show the visualization in a browser. Any help is much appreciated.
React/JSX doesn't deal with script tags.
You have to explicitly load it.
I'd suggest you use a hook when the component is available.
useEffect(() => {
const script = document.createElement('script');
script.innerHTML = scriptData;
document.getElementById('root').appendChild(script);
return () => {
document.getElementById('root').removeChild(script);
};
}, []);
Working example here: https://codesandbox.io/s/render-c3d3-charts-in-react-forked-4xkt9?file=/src/App.js
btw. Please don't use 'unescape', it is deprecated. Use 'decodeURIComponent' instead.
Okay this answer does what you want, but I have to say: Only use this if you know 100% where the vizCode comes from and that it can be trusted! be sure to check out the reasons why you should not use eval
Your problem is, that the script content is not being executed by the browser. I tried your sandbox and it immediately worked when I added
eval(scriptData)
Here's the entire code that's working for me:
export default function App() {
// This code is received from the backend
const vizCode =
"%3Cdiv%20id%3D'chartdiv_1614071920146'%3E%3C%2Fdiv%3E%3Cscript%20type%3D'text%2Fjavascript'%3E%20var%20width_1614071920146%20%3D%20700%3Bvar%20height_1614071920146%20%3D%20700%3Bvar%20chart_1614071920146%20%3D%20c3.generate(%7Bbindto%3A%20'%23chartdiv_1614071920146'%2C%20size%3A%20%7B%20width%3A%20(width_1614071920146%20-%2020)%2C%20height%3A%20(height_1614071920146%20-%2020)%20%7D%2C%20data%3A%20%7Bcolumns%3A%20%5B%20%5B'%20'%2C%201.0%2C%201.0%2C%203.0%2C%205.0%2C%205.0%5D%20%5D%2C%20type%3A%20'spline'%7D%2C%20legend%3A%20%7Bshow%3Afalse%7D%2C%20axis%3A%20%7B%20x%3A%20%7B%20type%3A%20'category'%2C%20categories%3A%20%5B'New%20Course'%2C'group%20course%201'%2C'Testkurs%20'%2C'test_course'%2C'Moodle4CollaborativeMOOCs'%5D%20%7D%20%7D%7D)%3B%3C%2Fscript%3E";
const unescapedVizCode = unescape(vizCode);
console.log(unescapedVizCode);
let objectData = parse(unescapedVizCode);
let divData = objectData[0];
let scriptData = objectData[1].props.dangerouslySetInnerHTML.__html;
useEffect(()=>{
eval(scriptData)
}, [scriptData])
return (
<div className="App">
{objectData}
</div>
);
}
I am not sure if it satisfies your requirement, but you can use window.open to render the HTML code within iFrame or in a new tab.
To open in iFrame use the following code:
const unescapedVizCode = unescape(vizCode);
window.open(unescapedVizCode, "myFrame");
return (
<div className="App">
<iframe name="myFrame"></iframe>
{objectData}
</div>
);
Or you can open in new tab:
const unescapedVizCode = unescape(vizCode);
window.open(unescapedVizCode, "_blank");
Try:
let objectData = parse(`<div className="App">${unescapedVizCode}</div>`);
return objectData;
I would like to use a javascript loop to create multiple HTML wrapper elements and insert JSON response API data into some of the elements (image, title, url, etc...).
Is this something I need to go line-by-line with?
<a class="scoreboard-video-outer-link" href="">
<div class="scoreboard-video--wrapper">
<div class="scoreboard-video--thumbnail">
<img src="http://via.placeholder.com/350x150">
</div>
<div class="scoreboard-video--info">
<div class="scoreboard-video--title">Pelicans # Bulls Postgame: E'Twaun Moore 10-8-17</div>
</div>
</div>
</a>
What I am trying:
var link = document.createElement('a');
document.getElementsByTagName("a")[0].setAttribute("class", "scoreboard-video-outer-link");
document.getElementsByTagName("a")[0].setAttribute("url", "google.com");
mainWrapper.appendChild(link);
var videoWrapper= document.createElement('div');
document.getElementsByTagName("div")[0].setAttribute("class", "scoreboard-video-outer-link");
link.appendChild(videoWrapper);
var videoThumbnailWrapper = document.createElement('div');
document.getElementsByTagName("div")[0].setAttribute("class", "scoreboard-video--thumbnail");
videoWrapper.appendChild(videoThumbnailWrapper);
var videoImage = document.createElement('img');
document.getElementsByTagName("img")[0].setAttribute("src", "url-of-image-from-api");
videoThumbnailWrapper.appendChild(videoImage);
Then I basically repeat that process for all nested HTML elements.
Create A-tag
Create class and href attributes for A-tag
Append class name and url to attributes
Append A-tag to main wrapper
Create DIV
Create class attributes for DIV
Append DIV to newly appended A-tag
I'd greatly appreciate it if you could enlighten me on the best way to do what I'm trying to explain here? Seems like it would get very messy.
Here's my answer. It's notated. In order to see the effects in the snippet you'll have to go into your developers console to either inspect the wrapper element or look at your developers console log.
We basically create some helper methods to easily create elements and append them to the DOM - it's really not as hard as it seems. This should also leave you in an easy place to append JSON retrieved Objects as properties to your elements!
Here's a Basic Version to give you the gist of what's happening and how to use it
//create element function
function create(tagName, props) {
return Object.assign(document.createElement(tagName), (props || {}));
}
//append child function
function ac(p, c) {
if (c) p.appendChild(c);
return p;
}
//example:
//get wrapper div
let mainWrapper = document.getElementById("mainWrapper");
//create link and div
let link = create("a", { href:"google.com" });
let div = create("div", { id: "myDiv" });
//add link as a child to div, add the result to mainWrapper
ac(mainWrapper, ac(div, link));
//create element function
function create(tagName, props) {
return Object.assign(document.createElement(tagName), (props || {}));
}
//append child function
function ac(p, c) {
if (c) p.appendChild(c);
return p;
}
//example:
//get wrapper div
let mainWrapper = document.getElementById("mainWrapper");
//create link and div
let link = create("a", { href:"google.com", textContent: "this text is a Link in the div" });
let div = create("div", { id: "myDiv", textContent: "this text is in the div! " });
//add link as a child to div, add the result to mainWrapper
ac(mainWrapper, ac(div, link));
div {
border: 3px solid black;
padding: 5px;
}
<div id="mainWrapper"></div>
Here is how to do specifically what you asked with more thoroughly notated code.
//get main wrapper
let mainWrapper = document.getElementById("mainWrapper");
//make a function to easily create elements
//function takes a tagName and an optional object for property values
//using Object.assign we can make tailored elements quickly.
function create(tagName, props) {
return Object.assign(document.createElement(tagName), (props || {}));
}
//document.appendChild is great except
//it doesn't offer easy stackability
//The reason for this is that it always returns the appended child element
//we create a function that appends from Parent to Child
//and returns the compiled element(The Parent).
//Since we are ALWAYS returning the parent(regardles of if the child is specified)
//we can recursively call this function to great effect
//(you'll see this further down)
function ac(p, c) {
if (c) p.appendChild(c);
return p;
}
//these are the elements you wanted to append
//notice how easy it is to make them!
//FYI when adding classes directly to an HTMLElement
//the property to assign a value to is className -- NOT class
//this is a common mistake, so no big deal!
var link = create("a", {
className: "scoreboard-video-outer-link",
url: "google.com"
});
var videoWrapper = create("div", {
className: "scoreboard-video-outer-link"
});
var videoThumbnailWrapper = create("div", {
className: "scoreboard-video--thumbnail"
});
var videoImage = create("img", {
src: "url-of-image-from-api"
});
//here's where the recursion comes in:
ac(mainWrapper, ac(link, ac(videoWrapper, ac(videoThumbnailWrapper, videoImage))));
//keep in mind that it might be easiest to read the ac functions backwards
//the logic is this:
//Append videoImage to videoThumbnailWrapper
//Append (videoImage+videoThumbnailWrapper) to videoWrapper
//Append (videoWrapper+videoImage+videoThumbnailWrapper) to link
//Append (link+videoWrapper+videoImage+videoThumbnailWrapper) to mainWrapper
let mainWrapper = document.getElementById('mainWrapper');
function create(tagName, props) {
return Object.assign(document.createElement(tagName), (props || {}));
}
function ac(p, c) {
if (c) p.appendChild(c);
return p;
}
var link = create("a", {
className: "scoreboard-video-outer-link",
url: "google.com"
});
var videoWrapper = create("div", {
className: "scoreboard-video-outer-link"
});
var videoThumbnailWrapper = create("div", {
className: "scoreboard-video--thumbnail"
});
var videoImage = create("img", {
src: "url-of-image-from-api"
});
ac(mainWrapper, ac(link, ac(videoWrapper, ac(videoThumbnailWrapper, videoImage))));
//pretty fancy.
//This is just to show the output in the log,
//feel free to just open up the developer console and look at the mainWrapper element.
console.dir(mainWrapper);
<div id="mainWrapper"></div>
Short version
Markup.js's loops.
Long version
You will find many solutions that work for this problem. But that may not be the point. The point is: is it right? And you may using the wrong tool for the problem.
I've worked with code that did similar things. I did not write it, but I had to work with it. You'll find that code like that quickly becomes very difficult to manage. You may think: "Oh, but I know what it's supposed to do. Once it's done, I won't change it."
Code falls into two categories:
Code you stop using and you therefore don't need to change.
Code you keep using and therefore that you will need to change.
So, "does it work?" is not the right question. There are many questions, but some of them are: "Will I be able to maintain this? Is it easy to read? If I change one part, does it only change the part I need to change or does it also change something else I don't mean to change?"
What I'm getting at here is that you should use a templating library. There are many for JavaScript.
In general, you should use a whole JavaScript application framework. There are three main ones nowadays:
ReactJS
Vue.js
Angular 2
For the sake of honesty, note I don't follow my own advice and still use Angular. (The original, not Angular 2.) But this is a steep learning curve. There are a lot of libraries that also include templating abilities.
But you've obviously got a whole project already set up and you want to just plug in a template into existing JavaScript code. You probably want a template language that does its thing and stays out of the way. When I started, I wanted that too. I used Markup.js . It's small, it's simple and it does what you want in this post.
https://github.com/adammark/Markup.js/
It's a first step. I think its loops feature are what you need. Start with that and work your way to a full framework in time.
Take a look at this - [underscore._template]
It is very tiny, and useful in this situation.
(https://www.npmjs.com/package/underscore.template).
const targetElement = document.querySelector('#target')
// Define your template
const template = UnderscoreTemplate(
'<a class="<%- link.className %>" href="<%- link.url %>">\
<div class="<%- wrapper.className %>">\
<div class="<%- thumbnail.className %>">\
<img src="<%- thumbnail.image %>">\
</div>\
<div class="<%- info.className %>">\
<div class="<%- info.title.className %>"><%- info.title.text %></div>\
</div>\
</div>\
</a>');
// Define values for template
const obj = {
link: {
className: 'scoreboard-video-outer-link',
url: '#someurl'
},
wrapper: {
className: 'scoreboard-video--wrapper'
},
thumbnail: {
className: 'scoreboard-video--thumbnail',
image: 'http://via.placeholder.com/350x150'
},
info: {
className: 'scoreboard-video--info',
title: {
className: 'scoreboard-video--title',
text: 'Pelicans # Bulls Postgame: E`Twaun Moore 10-8-17'
}
}
};
// Build template, and set innerHTML to output element.
targetElement.innerHTML = template(obj)
// And of course you can go into forEach loop here like
const arr = [obj, obj, obj]; // Create array from our object
arr.forEach(item => targetElement.innerHTML += template(item))
<script src="https://unpkg.com/underscore.template#0.1.7/dist/underscore.template.js"></script>
<div id="target">qq</div>
First of all I would like to say I have very few experience in Django and plotly, so excuse me if I may be asking something which makes no sense or has very few meaning.
In Django enviroment I know that programming logic takes place in the "Views.py" file, and all variables set on this file can be transfered to the "template.html" file and use them inside double curly braces "{{ VariableFromView }}".
I was wondering if there is any way, once the plot is created, to retrieve the limits of the displayed area in the plotly plot, either on the "view.py" or "template.py" files, and save them in a variable which could be used in the "template.html" file, such as:
Variable definition example:
xbrush = [plotly.xmin, plotly.xmax]
ybrush = [plotly.ymin, plotly.ymax]
Variable call in template
{{ xbrush }}
{{ ybrush }}
In my case I amb using Python code for the "Views.py" and Javascript and html for the "templates.html" file (as well as a little bit of Django template language for dealing with variables)
Thank you very much for your help!
There are multiple ways to achieve the same. I would suggest you to go through django tutorial once again to get better understanding of template context and variables. Since you already have variables xbrush and ybrush in your views.py, just make sure you return these in the context of your response. Like render('template.html', {'xbrush':xbrush, 'ybrush': ybrush}).Now in your template.html you can access these values using {{ xbrush }}. So now all you need now is create a script tag in your html file and now you can assign these values to javascript variables like var xbrush = {{ xbrush }}
You'll need to do this using JavaScript, since rendering and all interaction with a Plotly plot is done in the browser. There's a full example (including a CodePen) that's close to what you're trying to do in the Plotly documentation here: https://plot.ly/javascript/zoom-events/
var plot = document.getElementById('plot').contentWindow;
var plotTwo = document.getElementById('plotTwo').contentWindow;
document.getElementById('plot').onload = function() {
pinger = setInterval(function(){
plot.postMessage({task: 'ping'}, 'https://plot.ly')
}, 100);
};
window.addEventListener('message', function(e) {
var message = e.data;
if(message.pong) {
console.log('Initial pong, frame is ready to receive');
clearInterval(pinger);
// Listening for zoom events, but you can also listen
// for click and hover by adding them to the array
plot.postMessage( {
task: 'listen',
events: ['zoom']
}, 'https://plot.ly');
plotTwo.postMessage({
task: 'relayout',
'update': {
'xaxis.fixedrange': true,
'yaxis.fixedrange': true
},
}, 'https://plot.ly');
}
else if( message.type == 'zoom' ){
console.log('zoom', message);
drawRectangle( message['ranges'] );
}
else {
console.log(message);
}
});
function drawRectangle( ranges ){
var rect = {
'type': 'rect',
'x0': ranges['x'][0],
'y0': ranges['y'][0],
'x1': ranges['x'][1],
'y1': ranges['y'][1],
'fillcolor': 'rgba(128, 0, 128, 0.7)',
}
plotTwo.postMessage({
'task': 'relayout',
'update': { shapes: [rect] },
}, 'https://plot.ly');
}
function newPlot(){
var plotURL = document.getElementById('plotURL').value + '.embed';
var iframe = document.getElementById('plot');
iframe.src = plotURL;
var iframeTwo = document.getElementById('plotTwo');
iframeTwo.src = plotURL;
}
I am new to Javascript and trying to use Gridster with Knockout. I have a database with items, and I use knockout foreach to bind them to a UL. The UL is styled with the Gridster library. Everything works great unless I try to add additional elements to the UL via the ObservableArray in the viewmodel.
Can anyone help me understand the scope and order of operations here? It feels like the Gridster library isn't doing its styling to the new widgets.
This jsfiddle shows a working demo of the issue. Notice when you double click on a widget, it creates a new one but doesn't place it in the grid. Instead, it just kind of hangs out behind.
Here is the HTML
<div class="gridster">
<ul data-bind="foreach: myData">
<li data-bind="attr:{
'data-row':datarow,
'data-col':datacol,
'data-sizex':datasizex,
'data-sizey':datasizey
},text:text, doubleClick: $parent.AddOne"></li>
</ul>
</div>
Here is the Javascript
//This is some widget data to start the process
var gridData = [ {text:'Widget #1', datarow:1, datacol:1, datasizex:1, datasizey:1},
{text:'Widget #2', datarow:2, datacol:1, datasizex:1, datasizey:1},
{text:'Widget #3', datarow:1, datacol:2, datasizex:1, datasizey:1},
{text:'Widget #4', datarow:2, datacol:2, datasizex:1, datasizey:1}];
// The viewmodel contains an observable array of widget data to be
// displayed on the gridster
var viewmodel = function () {
var self = this;
self.myData = ko.observableArray(gridData);
//AddOne adds an element to the observable array
// (called at runtime from doubleClick events)
self.AddOne = function () {
var self = this;
myViewModel.myData.push({
text: 'Widget Added After!',
datarow: 1,
datacol: 1,
datasizex: 1,
datasizey: 1
});
};
};
var myViewModel = new viewmodel();
ko.applyBindings(myViewModel);
$(".gridster ul").gridster({
widget_margins: [5, 5],
widget_base_dimensions: [140, 140]
});
Here is a full example in JSfiddle. Here, I have highlighted just the delete function
self.deleteOne = function (item) {
console.log(item);
var widget = $("#" + item.id);
console.log(widget);
var column = widget.attr("data-col");
if (column) {
console.log('Removing ');
// if this is commented out then the widgets won't re-arrange
self.gridster.remove_widget(widget, function(){
self.myData.remove(item);
console.log('Tiles: '+self.myData().length);
});
}
};
The work of removing the element from the observable array is done inside the remove_widget callback. See gridster's documentation. Consequently, the removeGridster hook executed before a widget is removed, does no longer need to do the actual remove_widget call.
Here's a working solution that I think is more in line with the MVVM pattern:
http://jsfiddle.net/Be4cf/4/
//This is some widget data to start the process
var gridData = [
{id: "1", text:'Widget #1', datarow:1, datacol:1, datasizex:1, datasizey:1},
{id: "2", text:'Widget #2', datarow:1, datacol:2, datasizex:2, datasizey:1},
{id: "3", text:'Widget #3', datarow:1, datacol:4, datasizex:1, datasizey:1},
{id: "4", text:'Widget #4', datarow:2, datacol:1, datasizex:1, datasizey:2}];
// The viewmodel contains an observable array of widget data to be
// displayed on the gridster
var viewmodel = function () {
var self = this;
self.myData = ko.observableArray(gridData);
self.nextId = 5;
self.gridster = undefined;
// AddOne adds an element to the observable array.
// Notice how I'm not adding the element to gridster by hand here. This means
// that whatever the source of the add is (click, callback, web sockets event),
// the element will be added to gridster.
self.addOne = function () {
myViewModel.myData.push({
text: 'Widget Added After!',
datarow: 1,
datacol: 1,
datasizex: 1,
datasizey: 1,
id: self.nextId++
});
};
// Called after the render of the initial list.
// Gridster will add the existing widgets to its internal model.
self.initializeGridster = function() {
self.gridster = $(".gridster ul").gridster({
widget_margins: [5, 5],
widget_base_dimensions: [140, 140]
}).data('gridster');
};
// Called after the render of the new element.
self.addGridster = function(data, object) {
// This bypasses the add if gridster has not been initialized.
if (self.gridster) {
var $item = $(data[0].parentNode);
// The first afterRender event is fired 2 times. It appears to be a bug in knockout.
// I'm pretty new to knockout myself, so it might be a feature too! :)
// This skips the second call from the initial fired event.
if (!$item.hasClass("gs-w"))
{
// This removes the binding from the new node, since gridster will re-add the element.
ko.cleanNode(data[0].parentNode);
// Adds the widget to gridster.
self.gridster.add_widget($item);
// We must update the model with the position determined by gridster
object.datarow = parseInt($item.attr("data-row"));
object.datacol = parseInt($item.attr("data-col"));
}
}
};
};
var myViewModel = new viewmodel();
ko.applyBindings(myViewModel);
I still need to think about the remove and move events (a move in gridster should update the item's x and y values in the viewmodel). I started using knockout yesterday, so any help would be appreciated.
I couldn't find a cdn for the latest version of gridster. JSFiddle points to a temporary website I've added in Azure, I'll leave it up for a few days, but feel free to update it with your own link.
/------------------------------ UPDATE ----------------------------------/
I've updated my code to support deletions and moving widgets (http://jsfiddle.net/Be4cf/11/) but there's a small caveat: there's an open issue (https://github.com/knockout/knockout/issues/1130) that knockout cleans out jquery data before calling the beforeRemove event. This causes gridster to crash since the data needed to move the other items around is kept in a data element. A workaround could be to keep a copy of the data and to re-add it to the element later, but I've chosen the lazy way and commented the offending line in knockout.
Add class="gs_w" to ur li in gridster it should work
You should do something like below. addNewGridElement is called - with the rendered DOM element which is important in Gridster's case as gridster.add_widget accepts a DOM element as its first argument - once you've added something to the Knockout observable. After this, it's just a matter of then adding domNode to Gridster.
view.html:
<div class="gridster">
<ul data-bind="foreach: { myData, afterAdd: $root.addNewGridElement }">
<li data-bind="attr:{
'data-row':datarow,
'data-col':datacol,
'data-sizex':datasizex,
'data-sizey':datasizey
},text:text, doubleClick: $parent.AddOne"></li>
</ul>
</div>
view.js:
self.addNewGridElement = function (domNode, index, newTile) {
// Filter non li items - this will remove comments etc. dom nodes.
var liItem = $(domNode).filter('li');
if ( liItem.length > 0 ) {
// Add new Widget to Gridster
self.gridster.add_widget(domNode, newTile.x, newTile.y, newTile.row, newTile.col);
}
};
I have simple JavaScript snippet:
var obrazek = [{nazwa: "Sniadanie", wiek: 100, autor: "Alicja"},{nazwa: "Kolacja", wiek: 10, autor: "Misiek"}];
function galeria(nazwa, wsad) {
this.nazwa = nazwa;
this.wsad = wsad;
this.print = function(element) {
for (var i=0;i<this.wsad.length;i++) {
var text = "<li>"+this.wsad[i].nazwa+"</li>"
element.append(text);
}
}
}
$(document).ready(function() {
gal = new galeria('test', obrazek);
gal.print($('#galeriaTest'))
});
It gives me:
<ul id="galeriaTest>
<li>Sniadanie</li>
<li>Kolacja</li>
</ul>
What I want is simple method that will return object after click event:
Object { nazwa="Sniadanie", wiek=100, autor="Alicja"} (in FireBug)
How to code it?
As long as your data set is static, you can just associate the object to the DOM element using the data() function.
Here's an example.
If your data set is dynamic, you could still associate a reference to the Galeria and some ID type of information to get a similar albeit improved result.
$("selector").on('click', function(e){
console.log( obrazek ); // would put object in a console, you can check it via firebug
});