Polymer v1.0 - Data Binding changes not propagating - javascript

Trying my hand at Polymer v1.0, Am currently loading 2 datasets. The first set will set the view and repeat my custom elements. The 2nd set is async loaded, upon loading it will try to match via the routeid if available. On match it will insert itself as an object into the 1st object.
When i do this, the view doesn't get updated, but if i do a console.log and trace it, the data is there.
I discovered if I were to clear the 1st object, use async and set it back later, the view gets updated, but this would cause my display to be blank for a while.
How can i force it to redraw with the updated content?
First one sets the view of items.
[
{
"anchors":[
{"anchorid":1,
"routes":[
{"name":"route 1", "routeid":1 },
{"name":"route 2", "routeid":2 },
{"name":"route 3", "routeid":3 }
]
},
{"anchorid":2,
"routes":[
{"name":"route 4", "routeid":4 },
{"name":"route 5", "routeid":5 },
{"name":"route 6", "routeid":6 }
]
}
]
},
{
"anchors":[
{"anchorid":3,
"routes":[
{"name":"route 7", "routeid":7 },
{"name":"route 8", "routeid":8 },
{"name":"route 9", "routeid":9 }
]
},
{"anchorid":4,
"routes":[
{"name":"route 10", "routeid":10 },
{"name":"route 11", "routeid":11 },
{"name":"route 12", "routeid":12 }
]
}
]
}
]
2nd dataset
[
{"routeid":3, "status":2},
{"routeid":5, "status":3},
{"routeid":1, "status":1}
]
My element codes
<dom-module id="anchor-listdebug">
<template>
<h1>Anchor list</h1>
<template is="dom-repeat" items="{{data}}">
<h2>Anchor Group </h2>
<template is="dom-repeat" items="{{item.anchors}}" as="anchordata">
<anchor-item anchor="{{anchordata}}"></anchor-item>
</template>
</template>
</template>
<script>
Polymer ({
is:"anchor-listdebug",
ready : function () {
//data is loaded here via iron-ajax
this.data = data;
//data2 is loaded on data response
this.data2 = data2;
// emulate 2nd set of data loading, if this.processData is called immediately, content updates fine.
this.async (this.processData, 1000);
},
processData: function () {
this.data.forEach (function (element) {
element.anchors.forEach (function (anchorElement){
anchorElement.routes.forEach (function (routeElement){
var myID = routeElement.routeid;
var data = this.data2.filter(function (record){
return record.routeid == myID;
});
if (data.length >0) {
data = data[0];
routeElement.data = data;
}
});
});
});
}
});
</script>
</dom-module>
<dom-module id="anchor-item">
<template>
<h2>Anchor ID: {{anchor.anchorid}} </h2>
<template is="dom-repeat" items="{{anchor.routes}}" as="routedata">
<route-item route="{{routedata}}"></route-item>
</template>
</template>
<script>
Polymer ({
is:"anchor-item",
properties: {
anchor:{ type:Object, notify:true}
}
});
</script>
</dom-module>
<dom-module id="route-item">
<template>
<h3>Route id: <span>{{route.routeid}}</span></h3>
<h3>Route name: <span>{{route.name}}</span></h3>
<h3>Route status: <span>{{route.data.status}}</span></h3>
</template>
<script>
Polymer ({
is:"route-item",
properties: {
route:{ type:Object, notify:true}
}
});
</script>
</dom-module>

Make sure that you use the set API for manipulating your objects (docs). Instead of this.data.anchors = someValue use this.set("data.anchors", someValue).

First, you will need to use this.set("data.anchors", someValue) as was previously stated. You will also need to change your template binding, or the resulting notification will be ignored. Instead of using anchor="{{anchordata}}", you'd either have to use anchor="{{anchordata.*}}" to listen to all record changes, or anchor="{{anchordata.SPECIFIC_KEY}}" if you want the changes to respond to just one specific key within the data.

Updated my forEach loops to include the index to use to construct the path to use for the set() function.
<script>
Polymer ({
is:"anchor-listdebug",
ready : function () {
//data is loaded here via iron-ajax
this.data = data;
//data2 is loaded on data response
this.data2 = data2;
this.async (this.processData, 1000);
//this.processData();
},
processData: function () {
var scope = this;
this.data.forEach (function (element, index1) {
element.anchors.forEach (function (anchorElement, index2){
anchorElement.routes.forEach (function (routeElement, index3){
var myID = routeElement.routeid;
var data = this.data2.filter(function (record){
return record.routeid == myID;
});
if (data.length >0) {
data = data[0];
var tpath = "data."+index1+".anchors."+index2+".routes."+index3+".data";
scope.set(tpath, data);
}
});
});
});
}
});
</script>

Related

How to fetch json data using vuejs component?

I want to access json data from external file using vue component but I am not able to get any output in web page.The below is my code which I have tried.Can anyone help me out?
The below is Json data that included the models which I want to display on web page
{
"models": [
{
"title": "IRIS",
"project": "ABC",
"category": "SINGLES",
"bedrooms": 3
},
{
"title": "LILAC",
"project": "ABC",
"category": "DOUBLE",
"bedrooms": 4
},
{
"title": "ASTER",
"project": "ABC",
"category": "SINGLES",
"bedrooms": 4
}
]
}
Vue.component('single-model', {
data: function() {
return {
myData: []
}
},
template: `<div v-for="model in myData">
<p>{{model.title}}</p>
<hr>
</div>`,
created: function() {
this.fetchData();
},
methods: {
fetchData: function() {
var url = 'j.json';
axios.get(url)
.then(function(res) {
this.myData = res.data.models;
});
}
}
});
var vm = new Vue({
el: '#app',
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<div id="app">
<single-model></single-model>
</div>
As you might have noticed white running the provided snippet, template can have only one child element, using a v-for on the outermost element will create multiple children.
this in your case is not referring to the vue-component in fetchData function.
methods:{
fetchData() {
var url = '';
axios.get(url)
.then((res) => {
this.myData = res.data;
});
}
},
Try replacing with the above snippet in your code.
this on your code is not referring to your Vue Component.
I think, the easiest way to solve this issue is by creating a new variable to refer to your Vue Component
fetchData: function() {
var url = 'j.json';
var self = this;
axios.get(url)
.then(function(res) {
self.myData = res.data.models;
});
}

ZingChart X-axis labels showing as numbers instead of strings

I am using the ZingChart library to graph results from an API call. When I pass in a normal array for the "values" field of the chart data object, everything works fine. However, when I pass in an array made from Object.keys(titleSet) (where titleSet is a normal Javascript object), the graph displays as follows:
Example Chart
As you can see, the x-axis is now labeled with numbers instead of the array of strings. But when I print out the the result of Object.keys(titleSet) vs. passing in a normal array, they both appear to be the same in the console. Can anyone help me figure out what I'm doing wrong?
//List of movies inputted by the user
var movieList = [];
var movieSet = {};
var IMDBData = {
"values": [],
"text": "IMDB",
};
var metascoreData = {
"values": [],
"text": "Metascore"
};
var RTMData = {
"values": [],
"text": "Rotten Tomatoes Meter"
};
var RTUData = {
"values": [],
"text": "Rotten Tomatoes User"
};
var chartData = {
"type":"bar",
"legend":{
"adjust-layout": true
},
"plotarea": {
"adjust-layout":true
},
"plot":{
"stacked": true,
"border-radius": "1px",
"tooltip": {
"text": "Rated %v by %plot-text"
},
"animation":{
"effect":"11",
"method":"3",
"sequence":"ANIMATION_BY_PLOT_AND_NODE",
"speed":10
}
},
"scale-x": {
"label":{ /* Scale Title */
"text":"Movie Title",
},
"values": Object.keys(movieSet) /* Needs to be list of movie titles */
},
"scale-y": {
"label":{ /* Scale Title */
"text":"Total Score",
}
},
"series":[metascoreData, IMDBData, RTUData, RTMData]
};
var callback = function(data)
{
var resp = JSON.parse(data);
movieSet[resp.Title] = true;
//Render
zingchart.render({
id:'chartDiv',
data:chartData,
});
};
Full Disclosure, I'm a member of the ZingChart team.
Thank you for updating your question. The problem is you have defined your variable movieSet before the variablechartData. When parsing the page, top down, it is executing Object.keys({}) on an empty object when creating the variable chartData. You should just directly assign it into your config later on chartData['scale-x']['values'] = Object.keys(moviSet).
var callback = function(data)
{
var resp = JSON.parse(data);
movieSet[resp.Title] = true;
//Render
zingchart.render({
id:'chartDiv',
data:chartData,
});
};
There is a problem with the above code as well. It seems you are calling render on the chart every time you call this API. You should have one initial zingchart.render() and then from there on out use our API. I would suggest setdata method as it replaces a whole new JSON packet or modify method.
I am making some assumptions on how you are handling data. Regardless, check out the following demo
var movieValues = {};
var myConfig = {
type: "bar",
scaleX:{
values:[]
},
series : [
{
values : [35,42,67,89,25,34,67,85]
}
]
};
zingchart.render({
id : 'myChart',
data : myConfig,
height: 300,
width: '100%'
});
var callback = function(data) {
movieValues[data.title] = true;
myConfig.scaleX.values = Object.keys(movieValues);
zingchart.exec('myChart', 'setdata', {
data:myConfig
})
}
var index = 0;
var movieNamesFromDB = ['Sausage Party', 'Animal House', 'Hot Rod', 'Blazing Saddles'];
setInterval(function() {
if (index < 4) {
callback({title:movieNamesFromDB[index++]});
}
},1000)
<!DOCTYPE html>
<html>
<head>
<!--Assets will be injected here on compile. Use the assets button above-->
<script src= "https://cdn.zingchart.com/zingchart.min.js"></script>
<script> zingchart.MODULESDIR = "https://cdn.zingchart.com/modules/";
</script>
<!--Inject End-->
</head>
<body>
<div id='myChart'></div>
</body>
</html>
If you noticed in the demo, the length of scaleX.values determines how many nodes are shown on the graph. If you change values to labels this wont happen.

Update array order in Vue.js after DOM change

I have a basic Vue.js object:
var playlist = new Vue({
el : '#playlist',
data : {
entries : [
{ title : 'Oh No' },
{ title : 'Let it Out' },
{ title : 'That\'s Right' },
{ title : 'Jump on Stage' },
{ title : 'This is the Remix' }
]
}
});
HTML:
<div id="playlist">
<div v-for="entry in entries">
{{ entry.title }}
</div>
</div>
I also am using a drag and drop library (dragula) to allow users to rearrange the #playlist div.
However, after a user rearranges the playlist using dragula, this change is not reflected in Vue's playlist.entries, only in the DOM.
I have hooked into dragula events to determine the starting index and ending index of the moved element. What is the correct way to go about updating the Vue object to reflect the new order?
Fiddle: https://jsfiddle.net/cxx77kco/5/
Vue's v-for does not track modifications to the DOM elements it creates. So, you need to update the model when dragula notifies you of the change. Here's a working fiddle: https://jsfiddle.net/hsnvweov/
var playlist = new Vue({
el : '#playlist',
data : {
entries : [
{ title : 'Oh No' },
{ title : 'Let it Out' },
{ title : 'That\'s Right' },
{ title : 'Jump on Stage' },
{ title : 'This is the Remix' }
]
},
ready: function() {
var self = this;
var from = null;
var drake = dragula([document.querySelector('#playlist')]);
drake.on('drag', function(element, source) {
var index = [].indexOf.call(element.parentNode.children, element);
console.log('drag from', index, element, source);
from = index;
})
drake.on('drop', function(element, target, source, sibling) {
var index = [].indexOf.call(element.parentNode.children, element)
console.log('drop to', index, element, target, source, sibling);
self.entries.splice(index, 0, self.entries.splice(from, 1)[0]);
console.log('Vue thinks order is:', playlist.entries.map(e => e.title ).join(', ')
);
})
}
});
I created a Vue directive that does exactly this job.
It works exactly as v-for directive and add drag-and-drop capability in sync with underlying viewmodel array:
Syntaxe:
<div v-dragable-for="element in list">{{element.name}}</div>
Example: fiddle1, fiddle2
Github repository: Vue.Dragable.For

Retrieving a display results from a search

I'm relatively new to meteor.js and I'm trying to get a search form to work. So far I'm not even trying to get the params to work, but it will come later.
I'm basically trying to get a bunch of lifts to display.
lib/router.js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
notFoundTemplate: 'notFound',
waitOn: function() {
return Meteor.subscribe('lifts');
}
});
Router.route('/', { name: 'liftsList' });
Router.route('/lifts/search/:from-:to-:when', {
name: 'liftsSearch',
waitOn: function() {
return Meteor.subscribe('liftsSearch');
}
});
server/publications.js
Meteor.publish('liftsSearch', function() {
var query = { fromLoc: { $near : {
$geometry: {
type : "Point" ,
coordinates: [ 6.11667, 45.9 ]
} },
$maxDistance : 50
}};
return Lifts.find(query);
});
If I try to display the results with Lifts.find(query).fetch(), it returns actual results.
client/lifts_search.html
<template name="liftsSearch">
<div class="container">
<h3>Lifts search results {{hi}}</h3>
<div class="lifts">
{{#each lifts}}
hi
{{> liftItem}}
{{/each}}
</div>
</div>
</template>
Here I simply got no lifts displaying, not even the little "hi" string.
Thanks
Unless there's code that you haven't included, {{#each lifts}} isn't rendering because you're not defining lifts anywhere. Just because you're populating the Lifts collection, the template doesn't automatically known that lifts refers to it (largely because that would be totally arbitrary - what exact query would it refer to?).
So, you need to define lifts in either a router data function:
Router.route('/lifts/search/:from-:to-:when', {
name: 'liftsSearch',
waitOn: function() {
return Meteor.subscribe('liftsSearch');
},
data: function() {
return {
lifts: Lifts.find() // add a query if you want
}
}
});
Or in a template helper:
Template.liftsSearch.helpers({
lifts: function() {
return Lifts.find(); // add a query if you want
}
});

kendo ui - create a binding within another binding?

In my pursuit to get a binding for an associative array to work, I've made significant progress, but am still blocked by one particular problem.
I do not understand how to create a binding from strictly javascript
Here is a jsFiddle that shows more details than I have posted here:
jsFiddle
Basically, I want to do a new binding within the shown $.each function that would be equivalent to this...
<div data-template="display-associative-many" data-bind="repeat: Root.Items"></div>
Gets turned into this ...
<div data-template="display-associative-single" data-bind="source: Root['Items']['One']"></div>
<div data-template="display-associative-single" data-bind="source: Root['Items']['Two']"></div>
<div data-template="display-associative-single" data-bind="source: Root['Items']['Three']"></div>
And I am using the repeat binding to create that.
Since I cannot bind to an associative array, I just want to use a binding to write all of the bindings to the objects in it.
We start again with an associative array.
var input = {
"One" : { Name: "One", Id: "id/one" },
"Two" : { Name: "Two", Id: "id/two" },
"Three" : { Name: "Three", Id: "id/three" }
};
Now, we create a viewModel that will contain that associative array.
var viewModel = kendo.observable({
Name: "View Model",
Root: {
Items: input
}
});
kendo.bind('#example', viewModel);
Alarmingly, finding the items to bind was pretty easy, here is my binding so far;
$(function(){
kendo.data.binders.repeat = kendo.data.Binder.extend({
init: function(element, bindings, options) {
// detailed more in the jsFiddle
$.each(source, function (idx, elem) {
if (elem instanceof kendo.data.ObservableObject) {
// !---- THIS IS WHERE I AM HAVING TROUBLE -----! //
// we want to get a kendo template
var template = {};// ...... this would be $('#individual-item')
var result = {}; // perhaps the result of a template?
// now I need to basically "bind" "elem", which is
// basically source[key], as if it were a normal HTML binding
$(element).append(result); // "result" should be a binding, basically
}
});
// detailed more in the jsFiddle
},
refresh: function() {
// detailed more in the jsFiddle
},
change: function() {
// detailed more in the jsFiddle
}
});
});
I realize that I could just write out the HTML, but that would not perform the actual "binding" for kendo to track it.
I'm not really sure what you are attempting to do, but it seemed to me that the custom "repeat" binding was unnecessary. Here's what I came up with. Is this on track with what you are trying to do?
Here is a working jsFiddle example.
HTML
<div id="example">
<div data-template="display-associative-many" data-bind="source: Root.Items"></div>
</div>
<script type="text/x-kendo-template" id="display-associative-many">
#for (var prop in data) {#
# if (data.hasOwnProperty(prop)) {#
# if (data[prop].Id) {#
<div><span>${data[prop].Id}</span> : <span>${data[prop].Name}</span></div>
# }#
# }#
#}#
</script>
JavaScript
$(function () {
var input = {
"One" : { Name: "One", Id: "id/one" },
"Two" : { Name: "Two", Id: "id/two" },
"Three" : { Name: "Three", Id: "id/three" }
};
var viewModel = new kendo.data.ObservableObject({
Id: "test/id",
Root: {
Items: input
}
});
kendo.bind('#example', viewModel);
});

Categories

Resources