I have an object variable in an Angular controller with some properties. I want to create a series of polymer elements that takes that variable an the name of the property and shows them in a specific format (depending of type and other attributes). Something like the next example:
<polymer-element name="x-property" attributes="data property">
<template>
{{data.labels[property]}}: {{data[property]}}
</template>
<script>
Polymer('x-property', {
data: {}
});
</script>
</polymer-element>
And then I use it as this:
<x-property data="{{person}}" property="firstName"></x-property>
That works just fine. But now I want to avoid to specify the attribute data in all the elements. Reading Polymer documentation I see that it is possible to have global variables. I followed the example created the app-globals element, as shown in the api guide but when I try to access the property, instead of having the object "person" I got the text "{{person}}"
<polymer-element name="app-globals" attributes="values">
<script>
(function () {
var values = {};
Polymer('app-globals', {
ready: function () {
this.values = values;
for (var i = 0; i < this.attributes.length; ++i) {
var attr = this.attributes[i];
values[attr.nodeName] = attr.value;
}
}
});
})();
</script>
</polymer-element>
<polymer-element name="x-property" attributes="property">
<template>
<app-globals id="globals" values="{{globals}}"></app-globals>
{{globals.data.labels[property]}}: {{globals.data[property]}}
{{globals.data}}
</template>
<script>
Polymer('x-property', {
});
</script>
</polymer-element>
So in my html I have:
<app-globals data="{{person}}"></app-globals>
<x-property property="firstName"></x-property>
And the result I get is just this:
:
{{person}}
Is there anyway I can make this work as it works in the first example?
Because attributes are only strings, this code values[attr.nodeName] = attr.value; cannot capture your object-valued data. Instead, JavaScript converts your object to a string, which is why you see [Object object].
Capturing objects with mustaches ({{ }}) is a special Polymer feature that you enable by publishing the property as an attribute (or listing it in the publish map in the prototype).
If, instead of making app-globals generic, we instead publish values and data directly, then we can make it work like so:
<polymer-element name="app-globals" attributes="values data">
<script>
(function () {
var values = {};
Polymer('app-globals', {
created: function() {
this.values = values;
},
dataChanged: function() {
this.values.data = this.data;
}
});
})();
</script>
</polymer-element>
<polymer-element name="x-property" attributes="property">
<template>
<app-globals values="{{globals}}"></app-globals>
{{globals.data.labels[property]}}: {{globals.data[property]}}
{{globals.data | json}}
</template>
<script>
Polymer('x-property', {
json: function(s) {
return JSON.stringify(s);
}
});
</script>
</polymer-element>
Your original code doesn't have the json filter, but otherwise we are again asking Javascript to put an object in a string context, and it will render [Object object].
http://jsbin.com/vusayo/10/edit
Related
How can i pass a Javascript Variable to a Vue Component?
I have this jQuery function which generates the menu and pushes all the Values inside the array menu:
var menu = [];
$(document).ready(function(){
$('.service-desc-wrap h2,.cta-button').each(function(){
if($(this).hasClass('cta-button')) {
if($(this).attr('title') && $(this).attr('href')) {
var linkTitle = $(this).attr('title');
var href = $(this).attr('href');
data = [
title = linkTitle,
href = href,
]
menu.push(data);
$('#menu, #menuMobile').append('<a class="menuText" href="'+href+'">'+linkTitle+'</a>')
};
} else {
var tag = $.slugify($(this).text());
$(this).attr('id',tag);
var linkTitle = $(this).text();
if($(this).attr('title')) {
var linkTitle = $(this).attr('title');
};
data = [
title = linkTitle,
href = tag,
]
menu.push(data);
$('#menu, #menuMobile').append('<a class="menuText" href="#'+tag+'">'+linkTitle+'</a>')
}
});
});
I want to pass the array to a Vue Component called
<service-menu-component></service-menu-component>
The jQuery Function and the Component are inside a blade.php file, i'm using Laravel as a backend.
Any Vue component has access to the global scope (a.k.a window object), in which $ performs. You don't have to do anything special about it. In simpler words, if a variable has been declared in global scope at the time your Vue component is created - Vue can access it. But Vue won't react to later mutations performed on the contents of that variable. Not out of the box, anyway.
In Vue, that behavior is called reactivity. If that's what you want, you could use Vue.observable():
declare a const, holding a reactive reference (store.menu in this example - name it to whatever makes sense to you)
use a computed in your Vue component, returning the reactive reference
at any point, (before or after Vue instance's creation) modify the reference from anywhere (including outside Vue component/instance) and the Vue instance will get the change
Proof of concept:
Vue.config.productionTip = false;
Vue.config.devtools = false;
// you don't need the above config, it just suppresses some warnings on SO
// declare store, with whatever contents you want:
const store = Vue.observable({menu: []});
// optionally push something to menu:
// works before Vue instance was created
store.menu.push({foo: 'bar'});
$(function() {
// optionally push something to menu
// also works after Vue instance was created - i.e: 3s after $(document).ready()
setTimeout(function() {
store.menu.push({boo: 'far'});
}, 3000)
})
new Vue({
el: '#app',
computed: {
menu() {
// this injects the store's menu (the reactive property) in your component
return store.menu
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.js"></script>
<div id="app">
<pre v-text="menu"></pre>
</div>
The computed doesn't have to be on the root Vue element (it can be inside your <service-menu-component> component). The above is just a basic implementation, to demo the principle.
Use props:
<service-menu-component :data=YOUR_ARRAY></service-menu-component>
In the component:
props: ['data'] // the data being passed.
Yes, It's possible you need to import this jQuery snippet file to your parent component, and then pass it down to your service-menu-component.
Here's how the code should look like:
Your Parent.vue
<template>
<service-menu-component :data=menu></service-menu-component>
</template>
<script src="<pathToYourJqueryFile>"></script>
And then in your service-menu-component:
<template>
<div>
{{ menu }}
</div>
</template>
<script>
export default {
name: 'service-menu-component',
props: {
menu: {
type: Array
}
}
}
</script>
What made it work and seemed simple after trying different things is moving the jQuery function inside the component mounted hook like this:
mounted() {
var menu = [];
$(document).ready(function(){
$('.service-desc-wrap h2,.cta-button').each(function(){
if($(this).hasClass('cta-button')) {
if($(this).attr('title') && $(this).attr('href')) {
var linkTitle = $(this).attr('title');
var href = $(this).attr('href');
data = {
'title': linkTitle,
'href': href,
}
menu.push(data);
$('#menu, #menuMobile').append('<a class="menuText ml-2" href="'+href+'">'+linkTitle+'</a>')
};
} else {
var tag = $.slugify($(this).text());
$(this).attr('id',tag);
var linkTitle = $(this).text();
if($(this).attr('title')) {
var linkTitle = $(this).attr('title');
};
var data = {
'title': linkTitle,
'href': tag,
}
menu.push(data);
$('#menu, #menuMobile').append('<a class="menuText ml-2" href="#'+tag+'">'+linkTitle+'</a>')
}
});
console.log(menu);
});
this.menu = menu;
},
It worked like a charm.
#Şivam Kuvar verdiği örnekteki gibi :data='menu' yazan yeri :data="json_encode($controllerdata)" ile değiştir ve verilerini kullanmaya hazırsın. veri yapına göre #{{ data.data[0].title }} olabilir örneğin veya #{{ data.title }} bu gelen veriye bağlı dostum.
Just like #Şivam Kuvar's example, replace :data='menu' with :data="json_encode($controllerdata)" and you're ready to use your data. According to the data structure, it can be #{{ data.data[0].title }} for example or #{{ data.title }} it depends on the incoming data, my friend.
I am playing with Polymer 2.0, and I don't understand how to pass an object as an element attribute.
Here's my code:
<dom-module id="notes-app">
<template>
<style>
:host {
display: block;
}
</style>
<button on-click="loadNotes">Get the notes</button>
<template is="dom-repeat" items="[[notes]]" as="note">
<note recipe='JSON.stringify(note)'></note>
</template>
</template>
<script>
class NotesApp extends Polymer.Element {
static get is() { return 'notes-app'; }
static get properties() {
return {
notes: {
type: Array,
value: []
}
};
}
loadNotes() {
this.notes = [
{"desc":"desc1", "author":"auth1", "type":"type1"},
{"desc":"desc2", "author":"auth2", "type":"type2"},
{"desc":"desc3", "author":"auth3", "type":"type3"}
];
}
}
window.customElements.define(NotesApp.is, NotesApp);
</script>
</dom-module>
simple-note is the element who has a property of type Object:
<dom-module id="note">
<template>
<style>
:host {
display: block;
}
</style>
<div>
<fieldset>
<label>description</label>{{note.desc}}<br>
<label>author</label>{{note.author}}<br>
<label>type</label>{{note.type}}
</fieldset>
</div>
</template>
<script>
class SimpleNote extends Polymer.Element {
static get is() { return 'simple-note' }
static get properties() {
return {
note: {
type: Object,
value: {},
notify: true
}
};
}
}
customElements.define(SimpleNote.is, SimpleNote);
</script>
</dom-module>
As you can see I want note-app to display all the objects in its notes property by passing an object representing a note to every simple-note elements (don't known if it is the right way to make elements interact each other). I want it to happen when I press the notes-app button. How can I pass an object to an element attribute in this case?
Since you're trying to pass the variable as an object, you should use property bindings instead of attribute bindings (which only supports strings).
Polymer data bindings require curly or square brackets ({{twoWayBinding}} or [[oneWayBinding]]). For example, to set the foo property of the <x-child> element to the value of note, the template would look something like this:
<template is="dom-repeat" items="[[notes]]" as="note">
<x-child foo="[[note]]">
</template>
Given that SimpleNote.is equals "simple-note", I assume your usage of <note> and <dom-module id="note"> were only typos in your question. They should be set to simple-note, as the element name must start with a lowercase ASCII letter and must contain a dash.
It looks like you're binding a recipe property, but <simple-note> declares a note property (and no recipe) and binds to note sub-properties in its template. I assume recipe is another typo.
working demo
I am working on a project that makes use of Firebase Authentication and <firebase-query> from the polymerfire elements. I use data binding in many places throughout my application and never had this problem.
I bind the user object, which was created when a user authenticated, in many places to receive the name of my users. The following code shows a custom element. In there, I am trying to bind besides the user object the Firebase snapshot to a property that is of type Object.
When I console.log() the vidobj property, it displays the whole object. However, I am unable to bind it to my text. Although, the same works for the user object property.
I believe this has something to do with the lifecycle in Polymer. Should the property not update automatically even though the value might be created later?
The following screenshot displays the two console.log's
with the content of the vidobj:
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="my-singlevideo">
<template>
<style>
:host {
display: block;
}
</style>
<firebase-auth user="{{user}}"></firebase-auth>
<iron-localstorage
id="localstorage"
name="my-app-storage"
value="{{localUserDetails}}">
</iron-localstorage>
<h1>Name: [[user.displayName]]</h1>
<h1>Video Title: [[vidobj.title]]</h1>
</template>
<script>
Polymer({
is: 'my-singlevideo',
properties: {
user: {
type: Object,
},
localUserDetails: {
type: Object,
},
vidobj: {
type: Object,
},
},
ready: function() {
this.$.localstorage.reload();
var videoId = this.localUserDetails.lastClickedVid;
firebase.database().ref('/videos/' + videoId).once('value', function(snapshot) {
this.vidobj = snapshot.val();
console.log(this.vidobj);
console.log(this.vidobj.title);
});
},
});
</script>
</dom-module>
Your Firebase callback's context is not bound to your Polymer object, so you're actually setting vidobj on the outer context (usually the Window object).
To fix this, use Function#bind like this:
ready: function() {
// ...
firebase.database().ref('/videos/' + videoId).once('value',
function(snapshot) {
this.vidobj = snapshot.val();
console.log(this.vidobj);
console.log(this.vidobj.title);
}.bind(this)
);
}
I'm trying to bind a method to an on-tap attribute of a paper-button. After much testing, I've found that I can only bind a (for lack of a better word) top-level function, and not a method of an object in the template.
For example, I have a template, to which I have bound a number of objects, one of which is a user object. Object user has a bunch of methods and variables, like 'isNew' or 'reputation'. The user object also has a method 'addReputation'
I can use the object variables like this :
<template if = '{{user.new}}'><h1>{{user.name}}</h1></template>
And I can bind button taps like this:
<paper-button on-tap='{{addReputation}}'>Add Rep</paper-button>
But not like this:
<paper-button on-tap='{{user.addReputation}}'>Add Rep</paper-button>
Does anyone know why this may be?
if you set the method to a handler on your element's prototype it works. That way you can still keep things dynamic:
<script src="http://www.polymer-project.org/webcomponents.min.js"></script>
<script src="http://www.polymer-project.org/polymer.js"></script>
<polymer-element name="my-element" on-tap="{{tapHandler}}">
<template>
<style>
:host {
display: block;
}
</style>
click me
<content></content>
</template>
<script>
Polymer({
created: function() {
this.user = {
method: function() {
alert('hi');
}
};
this.tapHandler = this.user.method;
}
});
</script>
</polymer-element>
<my-element></my-element>
i'm sharing my plunk to resolve above problem. plunk link
In the template
<button on-tap="{{fncall}}" data-fnname="b">b call</button>
In the script
x.fncall = function(e) {
var target = e.target;
var fnName = target.getAttribute("data-fnname");
return x.datamodel[fnName]();
}
Polymer(x);
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
});