Data binding in nested custom polymer elements (recursive data-binding) - javascript

I try to bind custom sub elements to values of local storage by using polymer's template repeat functionality like this:
<polymer-element name="aw-outerElement">
<template>
<template repeat="{{group in grouplist}}">
<aw-innerElement groupId="{{group.groupId}}" name="{{group.name}}" val="{{group.val}}"></aw-innerElement>
</template>
</template>
<script>
Polymer('aw-outerElement', {
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
this.grouplist = [
{ groupId: 100, name: 'GroupName1', val: this.prj.ke.groupVal100},
{ groupId: 200, name: 'GroupName2', val: this.prj.ke.groupVal200}
];
}
</script>
In the code above I try to pass the data binding this.prj.ke.groupVal100 and this.prj.ke.groupVal200
to my inner element aw-innerElement through the attribute val. The aw-innerElement is a custom paper-input element where the value attribute should be set to e.g. this.prj.ke.groupVal100. It seems that only the stored initial value 0 will be set and NOT the data-binding string this.prj.ke.groupVal100 inside the value attribute. Is there a way to make a data-binding with template repeat inside inner elements?
My inner elements looks like this:
<polymer-element name="aw-innerElement" attributes="groupId name val">
<template>
<paper-input type="number" floatingLabel label="{{groupId}} {{name}}" value="{{val}}" error="{{i18nnrerror}}"></paper-input>
</template>
<script>
Polymer('aw-innerElement', {
publish: {
groupId: 0,
name: '',
val: 0
},
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
...
}
</script>
As you can see above the value="{{val}}" of my innerElement should be set to this.prj.ke.groupVal100 and this.prj.ke.groupVal200.
Thanks in advance!

I know I'm digging up an old question, but for future searchers this might come in handy.
Polymer does not allow a variable as your key, so you need to pull it through a function like so:
...
<template is="dom-repeat" items="{{users}}">
<li>{{showValue(item)}}</li>
</template>
...
<script>
Polymer('aw-outerElement', {
// Standard Polymer code here
showValue: function(item){
return item[myVar];
}
});
</script>
You can then manipulate as much as you want in Javascript and return the output for that one item in items.

Passing in a value seems to work fine for me in this example: http://jsbin.com/kalih/4/edit
<polymer-element name="x-bar">
<template>
<paper-input id="input"
label="{{name}}"
value="{{val}}">
</paper-input>
<button on-tap="{{logVal}}">Log val</button>
</template>
<script>
Polymer('x-bar', {
publish: {
val: 0
},
logVal: function() {
console.log(this.$.input.value);
}
});
</script>
</polymer-element>
<polymer-element name="x-foo">
<template>
<template repeat="{{item in items}}">
<x-bar name="{{item.name}}" val="{{item.val}}"></x-bar>
</template>
</template>
<script>
Polymer('x-foo', {
ready: function() {
this.items = [
{
name: 'baz',
val: 28
},
{
name: 'qux',
val: 42
}
];
}
});
</script>
</polymer-element>
<x-foo></x-foo>
Btw, your aw-innerElement doesn't need to have an attributes attribute because you're using the publish object (they serve the same purpose, but publish lets you set default values, which you've done). Also, we recommend that you don't camel-case your element names, the HTML parser is actually going to lowercase them all anyway.

I'm new with polymer so maybe my answer is not correct. But I done something like this that you're trying to do.
here is a sample of my code
I guess that your problem is that you didn't use bind on your template
<template bind="{{grouplist}}">
<template repeat="{{group in grouplist}}">
</template>
</template>
<script>
Polymer('aw-outerElement', {
ready : function () {
// Binding the project to the data-fields
this.prj = au.app.prj;
this.grouplist = [
{ groupId: 100, name: 'GroupName1', val: this.prj.ke.groupVal100},
{ groupId: 200, name: 'GroupName2', val: this.prj.ke.groupVal200}
];
}
</script>

Related

how to v-model on different array vue 3

Hi I was trying to use a v-model an input to a value in object in an array of object in Vue 3. The complexity lies in the fact the object is first processed by a function. And that it need to be processed every time when a change is made to an input. Here is my code (and a sandbox link) :
<template>
<div id="app">
<div v-for="param in process(parameters)" :key="param">
Name : {{param.name}} Value : <input v-model="param.value">
</div>
{{parameters}}
</div>
</template>
<script>
export default {
name: "App",
data(){
return{
parameters :[
{'name':'Richard Stallman','value':'cool dude'},
{'name':'Linus Torvalds','value':'very cool dude'},
{'name':'Dennis Ritchie','value':'very very cool dude'}
]
}
},
methods:{
process(parameters){
const results = parameters.map( param =>{
return {name:param.name+' extra text',
value:param.value+' extra text',
}
})
return results
}
}
};
</script>
I just want the original parameters to change when something is types in the inputs. Maybe #change could be of use. But I didn't find a fix with #change. Does anyone know a solution to my problem? Thanks in advance.
Use computed property to get reactive state of the data.
Working Demo :
new Vue({
el: '#app',
data: {
parameters :[
{'name':'Richard Stallman','value':'cool dude'},
{'name':'Linus Torvalds','value':'very cool dude'},
{'name':'Dennis Ritchie','value':'very very cool dude'}
]
},
computed: {
process() {
const results = this.parameters.map((param) => {
return {
name: param.name + ' extra text',
value: param.value + ' extra text'
}
});
return results;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="param in process" :key="param">
Name : {{param.name}}
Value : <input v-model="param.value">
</div><br>
<strong>Orinigal Data :</strong> {{parameters}}
</div>
I am not entirely sure I understood whether the person should be able to see/edit the text you added within you processing method.
Anyway, I think this sample of code should solve you problem :
<template>
<div id="app">
<div v-for="param in parameters" :key="param.name">
Name : {{ param.name }} Value : <input v-model="param.value" />
</div>
{{ process }}
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
parameters: [
{ name: "Richard Stallman", value: "cool dude" },
{ name: "Linus Torvalds", value: "very cool dude" },
{ name: "Dennis Ritchie", value: "very very cool dude" },
],
};
},
computed: {
process: function() {
const results = this.parameters.map((param) => {
return {
name: param.name + " extra text",
value: param.value + " extra text",
};
});
return results;
},
},
};
</script>
So, we're iterating through the parameters array directly, adding an input on the value just like you did.
When you type in the input, you update the parameter linked to it, in live.
I just switched the method you made into a computed method.
This way, every time parameters is updated, "process" is also updated because it's depending on it directly.
I also removed passing the "parameters" argument, it's in the component data, you can just access it directly.
This way, using "process" just like any variable, you'll always have the updated parameters + what you added to em.

Pass variable from within script tags to Vue instance

In my Drupal 7 site's html I have this
<script>$L = $L.wait(function() {
(function($) {
Drupal.behaviors.related_products = {
attach: function (context, settings) {
artiklar = Drupal.settings.related_products.artiklar;
console.log(artiklar);
}
};
})(jQuery);
});</script>
In the variable artiklar above I have some data that I have passed from the server side using Drupal behaviors. Now, on the client side I need to access the variable artiklar in a Vue component, like so:
Vue.component('artikel-lista', {
template:`
<ul>
<artikel v-for="artikel in artiklar">{{ artikel.title }} Pris: {{artikel.price}} <a :href="artikel.link" class="button tiny" target="_blank">Läs mer</a></artikel>
</ul>
`,
data(){
return {
artiklar: "",
};
},
mounted: function(){
this.artiklar = artiklar // how can I access the variable "artiklar" here
},
});
The data in the variable consists of an array of items, that I need in my Vue component. But how can I pass the variable from within the script tags to the Vue instance, that lives in a separate file, inserted just before the ending body tag. Anyone?
If you have data in the globally visible Drupal.settings.related_products.artiklar object then you can refer to it practically the same way in Vue.js. or if you must use this function, assign data to global scope window.*.
new Vue({
template: `<div>{{foo}} / {{bar}}</div>`,
data() {
return {
foo: Drupal.settings.related_products.artiklar,
bar: window.artiklarData
};
}
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">Vue App</div>
<script>
// simulate global variable
var Drupal = {
settings: {
related_products: {
artiklar: ['fus', 'ro', 'dah']
}
}
};
(function() {
window.artiklarData = Drupal.settings.related_products.artiklar;
})();
</script>
If you assign the value to Drupal.settings.related_products.artiklar after creating the Vue object, you can try to use the solutions described in the documentation, e.g.
const vm = new Vue({
template: `<div>{{foobar}}</div>`,
data() {
return {
foobar: 'Initial value'
};
}
}).$mount("#app");
setTimeout(() => {
// simulate global variable
var Drupal = {
settings: {
related_products: {
artiklar: 'Changed value'
}
}
};
(function() {
vm.foobar = Drupal.settings.related_products.artiklar;
})();
}, 2000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">Vue App</div>
Maybe you could use RxJS but I don't have enough knowledge to tell if it's true and give an example.
Just in case anyone else is struggling with the same thing, I post this answer to my own question (I accidentally posted the question with the wrong account). In the end it turns out that the answer from Gander was correct and that I could access the variable directly in the Vue component, w/o first stashing it an a global variable. The viewed result was kind of weird though and after some trialling I found out that I had to parse the result with JSON.parse(). This is the working code now:
Vue.component('artikel-lista', {
template:`
<ul>
<artikel v-for="artikel in artiklar">{{ artikel.title }} Pris: {{artikel.price}} <a :href="artikel.link" class="button tiny" target="_blank">Läs mer</a></artikel>
</ul>
`,
data(){
return{
artiklar:""
}
},
mounted:function(){
this.artiklar = JSON.parse(Drupal.settings.related_products.artiklar);
console.log(this.artiklar);
}
});

Component Vuejs2 declare $data obj to share data between components

I am trying to make a Vue2 component to all the select of my app so would be easier later to change it if necessary!
I've based my research on the example given by the docs and I am breaking my head to figure out why should I speficy all the object on the data attr to make it work!
The following code is working properly, but if we change:
data: { record: { category_id: null } } by data: { record: {} } it stop to work!
Must be said the $data.record is loaded by ajax... would I always specify the whole object even knowing that after the ajax request I am going to replace all with something like this.record = response.data?
If somebody need there is FIDDLE [ https://jsfiddle.net/gustavobissolli/4xrfy54e/1/ ]
EDIT: SORRY GUYS JUST FIXED FIDDLE LINK
Vue.component('select2', {
props: ['options', 'value'],
template: '#select2-template',
data() {
return {
model: ''
}
},
mounted: function() {
this.model = this.value
},
watch: {
value: function(value) {
this.model = value
},
model: function(value) {
this.$emit('input', value)
},
}
})
var vm = new Vue({
el: '#el',
template: '#demo-template',
data: {
record: {
category_id: null
},
options: [{
id: 1,
text: 'Hello'
}, {
id: 2,
text: 'World'
}]
}
})
<div id="el"></div>
<!-- using string template here to work around HTML <option> placement restriction -->
<script type="text/x-template" id="demo-template">
<div>
<pre>{{ $data | json }}</pre>
<select2 :options="options" v-model="record.category_id" value="record.category_id"></select2>
</div>
</script>
<script type="text/x-template" id="select2-template">
<select v-model="model">
<option disabled>Select...</option>
<option v-for="opt in options" :value="opt.id">{{ opt.text }}</option>
</select>
</script>
So you are trying to edit a value which didn't arrive yet? :-)
The thing is: at the moment v-model="record.category_id" is "executed", you have nothing there, ie, there is no "category_id" at the "record" object. So, it binds to nothing. This is why the select won't work if you omit the "category_id" at data initialization.
But your assumption that when data arrives from server (ajax call) the component will not work, is wrong.
I have updated your fiddle: https://jsfiddle.net/4xrfy54e/4/
First, use the dropdown before clicking the button: since it is binded to nothing, it will not update anything. This is correct.
Now, click the button. The button is simulating that data arrived from the server, and is assigned to this.record of the vm.
Play with the dropdown again: since record.category_id exists now, the binding is working fine.
Please, read the "Reactivity in Depth" documentation page, and you will stop breaking your head :-)

Databind iron-ajax JSON response entry to nested Polymer custom elements

I've got a Polymer app running an iron-ajax call to a service that returns a json string:
{
"name": "John"
}
The Polymer code on the main page is:
<link rel="import" href="/elements/my-form.html">
<dom-module id="my-app">
<template>
...
<iron-ajax auto url="/grabData" handle-as="json" last-response="{{data}}"></iron-ajax>
<iron-label>
From iron-ajax = {{data.name}}
</iron-label>
<my-form></my-form>
...
"my-form" is:
<link rel="import" href="/my-name.html">
<dom-module id="my-form">
<template>
<my-name></my-name>
</template>
<script>
Polymer({
is: 'my-form'
});
</script>
</dom-module>
And "my-name" is:
<dom-module id="my-name">
<template>
<iron-label>
From my-name = {{data.name}}
</iron-label>
</template>
<script>
Polymer({
is: 'my-name'
});
</script>
</dom-module>
When run, "From iron-ajax = John", and "From my-name =".
While I could use a single ajax call within my-name, I'd don't want to do this for every custom element. I'd rather acquire my data in one call and have custom elements access the returned data.
How do I acquire 'name' within my-name without passing the value through the my-form element?
--- Update ---
For a little more clarity on desired outcomes, here's a little more info.
Ultimately I'd like to acquire a very large JSON string with multiple entries:
{
"name": "John",
"address": [{
"street" : "14 Baker Street",
"city" : "somewhereville"
}],
"phone": "123-1234"
...
}
And have custom elements handle each entry:
<dom-module id="my-form">
<template>
<my-name></my-name>
<my-address></my-address>
<my-phone></my-phone>
...
</template>
Below is the example snippet on how to pass data between elements. Instead of ajax call i've passed data from element's attribute. Some key points to be noted
In case you want to pass whole data to child elements you can replace name property in child element with data property of parent, names doesn't have to be same, eg if child has a property user with type Object(first letter caps) then your binding will be user={{data}}
If you want you can replace {{}} with [[]], unless you want to propagate change from child to parent in which case you will have to add another parameter to name property namely notify and set its value as true, something like
name:{
type:String,
notify:true
}
In your case(with ajax call) listing name and data inside properties is optional, but i'll recommend it as it makes it easy to add new features to a property(like notify) and also makes it easy to track all the properties in a large element.
For more details on properties and data-binding checkout these links. properties, data-binding
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="my-app">
<template>
Name from App: {{data.name}}
<br>
<my-form name={{data.name}}></my-form>
</template>
</dom-module>
<script>
Polymer({
is: 'my-app',
properties: {
data: {
type: Object
}
}
});
</script>
<dom-module id="my-form">
<template>
Name from Form: {{name}}
<br>
<my-name name={{name}}></my-name>
</template>
</dom-module>
<script>
Polymer({
is: 'my-form',
properties: {
name: {
type: String
}
}
});
</script>
<dom-module id="my-name">
<template>
Name from Name: {{name}}
</template>
</dom-module>
<script>
Polymer({
is: 'my-name',
properties: {
name: {
type: String
}
}
});
</script>
<my-app data='{"name":"User1"}'></my-app>

Mutiple checkboxes, bound to the same Array in Vue JS

I'm starting out with vuejs and a vue grid at https://jsfiddle.net/kc11/7fqgavvq/
I want to display the checked row objects in the:
<pre> {{ selected| json}} </pre>
area of code,
as in the documentation at http://vuejs.org/guide/forms.html#Checkbox in the "Mutiple checkboxes, bound to the same Array:" example
As you can see when I check 1 checkbox, all are selected. Why is this happening? How can I fix this?
You made a few wrong assumptions in your code (mainly in respect to the scope).
You have your selected array in your main instance, instead of the demo-grid component, where you have your checkboxes:
var demo = new Vue({
el: '#demo',
data: {
searchQuery: '',
gridColumns: ['name', 'power'],
gridData: [
{name: 'Chuck Norris', power: Infinity},
{name: 'Bruce Lee', power: 9000},
{name: 'Jackie Chan', power: 7000},
{name: 'Jet Li', power: 8000}
],
selected: [] // <- This one
}
})
And there is no selectAll method defined on your demo-grid component either, even though you reference it in your template:
<input #click="selectAll" type="checkbox" v-model="selected" id="{{ entry.name }}" value="{{ entry.name }}"></td>
If you thus pass your selected property into your demo-grid, (and define it in the props), you should be fine:
<demo-grid
v-bind:data="gridData"
v-bind:columns="gridColumns"
v-bind:filter-key="searchQuery"
v-bind:selected="selected"> <!-- here -->
</demo-grid>
And define a selectAll method:
methods: {
...
selectAll: function () {
...
}
Here you can see a working example:
https://jsfiddle.net/7fqgavvq/3/
You should add the selected key to the component's data, not to the main instance of vue.
https://jsfiddle.net/7fqgavvq/4/

Categories