Very new to Polymer and Polymerfire. I couldn't find an answer here so hoping I can get help here. The basic question I have is "how do I work with the data that polymerfire/firebase-query sends?" Note I'm using polymerfire version 0.9.4, and polymer is version 1.4.0.
I can load my data from Firebase no problem using Firebase query, however some of the values are raw numbers that I need to convert to user friendly information. For example I have time stored in ms that I want to convert to a date, and a numeric field that indicates the "type" of data that is stored and I want to show an icon for it, not just a raw number. I figured my best option would be to use the transactions-complete promise or an observer. Both fire but neither seems to give me access to the data. The Observer's newData is an empty array, and transactions-complete.. well I don't really know what to do with that when the promise fires. Below is my relevant code. I also tried using notify: true, but I seem to not be grasping the concept correctly.
<firebase-query
id="query"
app-name="data"
path="/dataPath"
transactions-complete="transactionCompleted"
data="{{data}}">
</firebase-query>
<template is="dom-repeat" items="{{data}}">
<div class="card">
<div>Title: <span>{{item.title}}</span></div>
<div>Date Created: <span>{{item.dateCreated}})</span></div>
<div>Date Modified: <span>{{item.dateModified}}</span></div>
<div>Status: <span>{{item.status}}</span></div>
</div>
</template>
Polymer({
is: 'my-view1',
properties: {
data: {
notify: true,
type: Object,
observer: 'dataChanged'
}
},
dataChanged: function (newData, oldData) {
console.log(newData[0]);
// do something when the query returns values?
},
transactionCompleted: new Promise(function(resolve, reject) {
// how can I access "data" here?
})`
I wound up going another way entirely, which seemed to be a cleaner approach to what I was doing anyways. I broke it down into separate components. This way when the detail component was loaded, the ready function would allow me to adjust the data before it got displayed:
list.html:
<firebase-query
id="query"
app-name="data"
path="/dataPath"
data="{{data}}">
</firebase-query>
<template is="dom-repeat" items="{{data}}">
<my-details dataItem={{item}}></my-details>
</template>
details.html
<template>
<div id="details">
<paper-card heading="{{item.title}}">
<div class="card-content">
<span id="description">{{item.description}}</span><br/><br/>
<div class="details">Date Created: <span id="dateCreated">{{item.dateCreated}}</span><br/></div>
<div class="details">Last Modified: <span id="dateModified">{{item.dateModified}}</span><br/></div>
<div class="status"><span id="status">{{item.status}}</span><br/></div>
</div>
</paper-card>
</template>
Then in the javascript ready function I can intercept and adjust the data accordingly:
Polymer({
is: 'my-details',
properties: {
item: {
notify: true,
},
},
ready: function() {
this.$.dateModified.textContent = this.getDate(this.item.dateModified);
this.$.dateCreated.textContent = this.getDate(this.item.dateCreated);
this.$.status.textContent = this.getStatus(this.item.status);
},
Try the following changes:
Take out the transactions-completed attribute - it is only relevant when the query is updating data to Firebase
Change the dom-repeat template to get it's items attribute from convertedData - this allows you to do the data conversions to## Heading ## the results of the firebase-query
<firebase-query
id="query"
app-name="data"
path="/dataPath"
data="{{data}}">
</firebase-query>
<template is="dom-repeat" items="{{convertedData}}">
<div class="card">
<div>Title: <span>{{item.title}}</span></div>
<div>Date Created: <span>{{item.dateCreated}})</span></div>
<div>Date Modified: <span>{{item.dateModified}}</span></div>
<div>Status: <span>{{item.status}}</span></div>
</div>
</template>
Add a convertedData property to do your data conversions from data which has the raw data
Change the observer syntax as per the example. This sets up the observer to to observe for changes to deep property values which results in the observer method being fired - see: https://www.polymer-project.org/1.0/docs/devguide/observers#deep-observation
In the observer method you can populate the convertedData object from the data object which should then render the content
Polymer({
is: 'my-view1',
properties: {
data: {
notify: true,
type: Object
},
convertedData: {
notify: true,
type: Object
}
},
// observer syntax to monitor for deep changes on "data"
observers: [
'dataChanged(data.*)'
]
dataChanged: function (newData, oldData) {
console.log(newData);
// convert the "newData" object to the "convertedData" object
}
}
Related
I'm using v-validate with Vue. I'm trying to figure out how to force v-validate to update rules. For example, I have something like this:
<template>
<div v-for="field in fields">
<input :name="field.name" v-validate="field.rules">
</div>
</template>
<script>
export default {
data() {
fields: [
{
name: "city",
rules: {
included: []
}
}
]
}
}
</script>
As you can see, my "included" array is empty on page load. I get the array from an AJAX request, and then I update my data:
this.fields[0].rules.included = cities
But v-validate doesn't seem to acknowledge the newly-added array. It only works if I hardcode the cities into my data. How can I force v-validate to respond to the updated rules?
Vue.js is unable to track updates on nested reference types.
Try:
let fields = [...this.fields]
fields[0].rules = cities
this.fields = fields
Use Vue.set to track changes : https://v2.vuejs.org/v2/guide/reactivity.html
Vue.set(this.fields[0], 'rules', cities);
I am trying to get help with a particular vue-tables-2 package implementation. In order to do so, I am trying to set up a jsfiddle and keep getting a t is undefined error for even the most basic implementation. Has anyone else run into this error? I suspect it has to do with the importing of dependencies, but cant seem to resolve it.
I appreciate any suggestions on how to get the jsfiddle up and running.
HTML
<div id="app">
<div class="col-md-8 col-md-offset-2">
<div id="people">
<v-server-table url="https://jsonplaceholder.typicode.com/users" :columns="columns" :options="options">
</v-server-table>
</div>
</div>
</div>
JavaScript
Vue.use(VueTables.ServerTable);
new Vue({
el: "#people",
data: {
columns: ['name', 'username'],
options: {
// see the options API
}
}
});
https://jsfiddle.net/kbpq5vb3/35/
It seems the server is not providing the correct data format vue-tables-2 is requiring as stated in the docs:
You need to return a JSON object with two properties:
data : array - An array of row objects with identical keys.
count: number - Total count before limit.
If you can't change what the server returns, you probably have to use the client table where you can grab the data with axios.
A minimal client table example using axios to grab data.
https://jsfiddle.net/kbpq5vb3/38/
If you can't change what the server returns, you probably have to use
the client table where you can grab the data with axios.
Not necessarily. You can stick with the server component and use the requestAdapter and responseAdapter options to mould the request and the response to the expected format.
For Example (Using Github's API):
<div id="app">
<h3>
Vue Tables 2 - Server Side Demo
</h3>
<div class="col-md-8 col-md-offset-2">
<div id="people">
<v-server-table url="https://api.github.com/users/matfish2/repos" :columns="columns" :options="options">
</v-server-table>
</div>
</div>
</div>
JavaScript
Vue.use(VueTables.ServerTable);
new Vue({
el: "#people",
methods: {
formatDate(date) {
return moment(date).format('DD-MM-YYYY HH:mm:ss');
}
},
data: {
columns: ['name', 'created_at', 'updated_at', 'pushed_at'],
tableData: [],
options: {
perPage: 25,
perPageValues: [25],
orderBy: {
column: 'name',
ascending: true
},
requestAdapter(data) {
return {
sort: data.orderBy,
direction: data.ascending ? 'asc' : 'desc'
}
},
responseAdapter({data}) {
return {
data,
count: data.length
}
},
filterable: false,
templates: {
created_at(h, row) {
return this.formatDate(row.created_at);
},
updated_at(h, row) {
return this.formatDate(row.updated_at);
},
pushed_at(h, row) {
return this.formatDate(row.pushed_at);
}
}
}
}
});
https://jsfiddle.net/matfish2/js4bmdbL/
I've been trying to work this out but sofar have been unable to find an answer. My Polymer element loads a base template JSON file, which is then run through a dom-repeat to create a basic HTML page.
Then another JSON text-file is loaded, which completes the various areas of the HTML with a JS function.
Upon button-click form child, a function is run that triggers the loading of another JSON file that adds additional info. This all works fine.
But when I go out of the page and back in it, it has remembered all my settings but does not display things correctly. It displays the translatedText well and the html code is there, but it does not complete the html code for the originalText.
It seems to want to load the last JSON file before the DOM is properly rendered. So I want it to refresh the whole DOM, but how do I do this?
My MWE:
<template>
<iron-ajax
auto
url="basetext.json"
handle-as="json"
last-response="{{baseText}}"></iron-ajax>
<div class="textcontent">
<template is="dom-repeat" items="{{baseText.lines}}" as="line">
<div class="lineblock">
<div class="line" id="line{{line.lineid}}" inner-h-t-m-l="{{line.linetext}}"></div>
<template is="dom-if" if="[[extraShowEnabled]]">
<div class="linepi" id='linepi{{line.lineid}}' inner-h-t-m-l="{{line.linetext}}"></div>
</template>
</div>
</template>
</div>
<template is="dom-if" if="[[extraLoadEnabled]]">
<iron-ajax
auto
url="originaltext.json"
handle-as="json"
last-response="{{originalText}}"></iron-ajax>
</template>
<iron-ajax
auto
url="translatedtext.json"
handle-as="json"
last-response="{{translatedText}}"></iron-ajax>
</template>
<script>
Polymer({
is: 'text-page',
properties: {
translatedText: Object,
originalText: Object,
extraShowEnabled: {
type: Boolean,
value: false
},
extraLoadEnabled: {
type: Boolean,
value: false
},
showViewer: {
type: String,
value: "none"
}
},
observers: [
'setView(showViewer)',
' _computeSegments(translatedText,".line")',
' _computeSegments(originalText,".linepi")'
],
ready: function() {
this.addEventListener('eventFromChild', this.changeView);
},
changeView: function(event) {
this.showViewer = event.detail.selectedView;
},
setView: function(showViewer) {
\\ first some code here to reset all css.
if (showViewer === "none") {
this.extraShowEnabled = false;
this.extraLoadEnabled = false;
}
if (showViewer === "sidebyside") {
this.extraShowEnabled = true;
this.extraLoadEnabled = true;
this._computeSegments(this.originalText,".linepi");
this._addSideBySideCode();
}
},
_computeSegments: function(inputText,linetype) {
if (inputText) {
Array.from(this.querySelectorAll(linetype+" sc-segment")).forEach(item => item.innerHTML = inputText.segments[item.id]);
}
},
_addSideBySideCode: function() {
\\ this function just adds some css.
},
});
</script>
I think You should try to use a compute function result as a dom-repeat item source, something like this:
<template is="dom-repeat" items="{{itmesByParamsCompute(baseText, originalText, translatedText, extraloadEnabled, ...)}}" as="line">
Add as many params as You need to recompute on. Then that compute function should return a valid source anytime at least one of the paras changes.
Also keep in mind, that if any of these params will become undefined that compute function might be ignored completely. Work around for this is making this opposite way - one property which is modified from manny observers, something like this:
properties: {
items_to_use: {
type: Array,
value: []
},
translatedText: {
type: Object,
observer: 'updateItemsToUse'
},
originalText: {
type: Object,
observer: 'updateItemsToUse'
}
},
updateItemsToUse: function (data) {
let updatedArray = this.someMixFixFunction(this.item_to_use, data);
this.set('items_to_use', updatedArray);
},
someMixFixFunction: function (old_array, data_to_apply) {
// do some merging or what ever You need here, for example
let updatedArray = old_array.concat(data_to_apply);
return updatedArray;
}
I am trying to use the Polymer templatizer to create a single instance of a template, append it into a div and get data binding to work between the host and this instance but am having difficulty getting this to work.
The most simple example I have tried:
HTML
<dom-module id="test-app">
<paper-input label="host" value="{{test}}"></paper-input>
<template id="template">
<paper-input label="instance" value="{{test}}"></paper-input>
</template>
<div id="placehere"></div>
</dom-module>
JS
Polymer({
is: "test-app",
behaviors: [Polymer.Templatizer],
properties: {
test: {
type: String,
value: 'hello',
notify: true,
},
},
ready: function() {
this.templatize(this.$.template);
var clone = this.stamp({test: this.test});
Polymer.dom(this.$.placehere).appendChild(clone.root);
},
});
The idea above is to create the instance of the template, place it into "placehere" and have the two input text boxes keep in sync.
When the page loads, the instance is created successfully and the value in both textboxes is "hello" but changing either input box does nothing.
The documentation on the polymer page seems a bit lightweight:
https://www.polymer-project.org/1.0/docs/api/Polymer.Templatizer
but it mentions the use of _forwardParentProp and _forwardParentPath. How exactly am I supposed to implement them in my situation?
As you have already figured out, you need to implement some of the Templatizer's methods. Particularly the _forwardParentProp and _forwardParentPath methods.
But before I begin, I must also point out one additional error in your custom elements definition. In your dom-module element, you have the element's contents defined without the template. It is essential to wrap everything within a template element. The fixed version of your custom element would be like this:
<dom-module id="test-app">
<template>
<paper-input label="host" value="{{test}}"></paper-input>
<template id="template">
<paper-input label="instance" value="{{test}}"></paper-input>
</template>
<div id="placehere"></div>
</template>
</dom-module>
As for the implementation of the Templatizer methods, you first need to store the stamped instance. After that, both methods that need implementations are more or less simple one-liners.
Here is the full JavaScript part of the custom element:
Polymer({
is: "test-app",
behaviors: [Polymer.Templatizer],
properties: {
test: {
type: String,
value: 'hello',
notify: true,
},
},
ready: function() {
this.templatize(this.$.template);
var clone = this.stamp({test: this.test});
this.stamped = clone.root.querySelector('*'); // This line is new
Polymer.dom(this.$.placehere).appendChild(clone.root);
},
// This method is new
_forwardParentProp: function(prop, value) {
if (this.stamped) {
this.stamped._templateInstance[prop] = value;
}
},
// This method is new
_forwardParentPath: function(path, value) {
if (this.stamped) {
this.stamped._templateInstance.notifyPath(path, value, true);
}
},
});
Here is a working JSBin demo: http://jsbin.com/saketemehi/1/edit?html,js,output
I'm trying to make a simple form that will accept user's input for different types of currency.
Here's a (broken) fiddle that hopefully gets across what I want to do:
https://jsfiddle.net/4erk8yLj/7/
I'd like my component to bind data to my root vue instance, but I'm not sure if my v-model string is allowable. Check it out:
Vue.component('conversion-row', {
props: ['currency', 'values'],
template: '<div>{{currency}}:</div><div><input v-model="values[currency]></div><',
});
var vm = new Vue({
el: "#app",
data: {
currencies: ['USD', 'BTC'],
values: {
'BTC': '',
'USD': ''
}
}
});
template:
<div id="app">
<li>
<conversion-row is li v-for="currency in currencies" v-bind:currency="currency">
</conversion-row>
</li>
</div>
What's a good way to fix this?
Couple of things you might need to correct:
First, the data property must be a function rather than an object. This allows every instance to get data recomputed every time it is being created, see:
var vm = new Vue({
el: "#app",
data() {
return {
currencies: ['USD', 'BTC'],
values: {
'BTC': 'BTC Value',
'USD': 'USD Value',
},
};
}
});
Second, <conversion-row> doesn't have values property bound. Here's what you can do:
<div id="app">
<li v-for="currency in currencies">
<conversion-row :currency="currency" :values="values"></conversion-row>
</li>
</div>
Last, the component should always aim for one root element (wrapper) and then you can nest as many children inside as you want. What's more, instead of using v-model, you can bind value which is the proper way to pass a value to an input (one-way data binding), check the following:
Vue.component('conversion-row', {
props: ['currency', 'values'],
template: '<div>{{currency}}:<input type="text" :value="values[currency]"></div>'
});
There's more improvements you could possibly make here like re-thinking if you need to pass values as well as currency to the conversion-row but I'm pretty sure you'll figure it out later on.
All that above will make your code run and execute properly, here's the working example (fork of yours):
https://jsfiddle.net/maciejsmolinski/mp8m0ben/1/
Does this help?
Not sure what you're aiming for in terms of using v-model, but here's an example of working v-model (based on your example):
Vue.component('conversion-row', {
props: ['currency', 'values'],
template: '<div>{{currency}}:<input type="text" v-model="values[currency]"></div>'
});
And the corresponding template:
<div id="app">
<p><strong>USD Value:</strong> {{ values.USD }}</p>
<p><strong>BTC Value:</strong> {{ values.BTC }}</p>
<br>
<li v-for="currency in currencies">
<conversion-row :currency="currency" :values="values"></conversion-row>
</li>
</div>
You can find it under the following URL:
https://jsfiddle.net/maciejsmolinski/0xng8v86/2/