Polymer 2.0: pass this to another element. Making a comments section - javascript

So basically what I'm struggling with is how to pass the reference of the parent element to its child element i.e the custom remove element?
can anyone please help me out!
*******************this is the el-insert element********************
// this element recieves data from another element(username and comment)
<link rel="import" href="./remove.html">
<dom-module id="el-insert">
<template >
<div id="userComment"><span>{{username}}</span>: {{saveComment}}</div>
<input id="edit" type="text" value={{saveComment::input}}>
<hr>
<reply-comment></reply-comment>
<button on-click="edit">Edit</button>
<remove-comment ></remove-comment>
</template>
<script>
class elInsert extends Polymer.Element {
static get is() { return 'el-insert'; }
static get properties(){
return {
saveComment:{
type:String,
notify:true
},
username:{
type:String,
notify:true
}
}//return ends
}
edit (){
$(this.$.userComment).toggleClass('hide');
var display = $(this.$.edit).css('display');
if(display == 'none'){
$(this.$.edit).css('display','block');
}else{
$(this.$.edit).css('display','none');
}
}
}
window.customElements.define(elInsert.is, elInsert);
</script>
</dom-module>
***************the remove button element******************
<link rel="import" href="../bower_components/polymer/polymer.html">
<dom-module id="remove-comment">
<template>
<button on-click="remove">el-Remove</button>
</template>
<script>
class removeComment extends Polymer.Element {
static get is() { return 'remove-comment'; }
static get properties(){
return{
}//return ends
}
remove(){
var element = this.parentNode.host;
$(element).remove();
}
}
window.customElements.define(removeComment.is,removeComment);
</script>
</dom-module>
This code works perfectly for me.
But how will I do the same thing using fire or dispatch as mentioned in the answer by Nicolas.
How to pass event details from parent element to child element?
Also, I want to make this element reusable so that I can simply drop it into another place like in a reply to delete that also.
(Also, I'm new very new to polymer so if there is anything else that I can improve upon in this code then please let me know)
I hope now you guys can help me out.

You do not want to do that - in general you should just pass properties to the child element and catch events from the child in the parent elements.
properties go down and events go up -
In you case you do not have to pass anything to the child element. The child element should just fire an event and the parent should respond to it when it catches it.
Something like that:
<dom-module id="child-element">
<template>
<div on-tap="_deleteComment"></div>
</template>
<script>
class ChildElement extends Polymer.Element {
...
_deleteComment() : {
// fire event to be caught by parent
const deleteEvent = new CustomEvent('deleteComment', {detail: {
whatever: {
you: 'want',
},
}});
this.dispatchEvent(deleteEvent);
}
}
...
</script>
</dom-module>
<dom-module id="parent-element">
<template>
<child-element on-delete-comment="_deleteComment"></child-element>
</template>
<script>
class ParentElement extends Polymer.Element {
...
_deleteComment(evt) : {
// handle evt
}
}
...
</script>
</dom-module>
#daKmoR is right - more context and some code would help -

Related

Lit-Element: which event to use for DOM updates?

The documentation over at github.com/Polymer/lit-element describes the lifecycl, if a property of some lit-element is changed. However, I can not seem to find any documentation about a lifecycle if the DOM content of the element is changed.
So assume I have some nested DOM structure and my outermost element should display something based on the DOM content. For sake of simplicity the example below will just display the number of child-elements of the given type.
Now at some point my application inserts a new nested element (click the test button below). At this point I would like to update the shown count.
From my tests it seems that render() is not called again in that case, neither is updated().
Which event do I need to listen or which function do I need to implement for to recognize such a change?
My only current workaround is to use requestUpdate() manually after the DOM update, but I think such changes should be handled by lit-element itself.
document.querySelector( 'button' )
.addEventListener( 'click', () => {
const el = document.querySelector( 'my-element' );
el.insertAdjacentHTML( 'beforeend', '<my-nested-element>new addition</my-nested-element>' );
})
my-element, my-nested-element {
display: block;
}
<script src="https://unpkg.com/#webcomponents/webcomponentsjs#latest/webcomponents-loader.js"></script>
<!-- Works only on browsers that support Javascript modules like Chrome, Safari, Firefox 60, Edge 17 -->
<script type="module">
import {LitElement, html} from 'https://unpkg.com/#polymer/lit-element/lit-element.js?module';
class MyElement extends LitElement {
constructor(){
super();
this.number = this.querySelectorAll( 'my-nested-element' ).length;
}
render() {
return html`<p>number of my-nested-element: ${this.number}</p>
<slot></slot>`;
}
}
customElements.define('my-element', MyElement);
class MyNestedElement extends LitElement {
render() {
return html`<slot></slot>`;
}
}
customElements.define('my-nested-element', MyNestedElement);
</script>
<my-element>
<my-nested-element>first</my-nested-element>
<my-nested-element>second</my-nested-element>
</my-element>
<button>test</button>
In order to detect a new element inserted from the Light DOM through a <slot> element, you can listen to slotchange events on the <slot> element, or on the Shadow DOM root itself.
See the running example below:
document.querySelector('button').onclick = () =>
document.querySelector('my-element').insertAdjacentHTML('beforeend', '<my-nested-element>new addition</my-nested-element>');
my-element,
my-nested-element {
display: block;
}
<script type="module">
import {LitElement, html} from 'https://unpkg.com/#polymer/lit-element/lit-element.js?module';
class MyElement extends LitElement {
firstUpdated() {
var shadow = this.shadowRoot
var nb = shadow.querySelector( 'span#nb' )
shadow.addEventListener( 'slotchange', () =>
nb.textContent = this.querySelectorAll( 'my-nested-element').length
)
}
render() {
return html`<p>number of my-nested-element: <span id="nb"></span></p>
<slot></slot>`;
}
}
customElements.define('my-element', MyElement);
</script>
<my-element>
<my-nested-element>first</my-nested-element>
<my-nested-element>second</my-nested-element>
</my-element>
<button>test</button>

Passing an object to element attribute in Polymer 2

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

How to access functions defined in js file inside the template of Polymer element?

I have created a function in global.function.js file as
function getData(flag) {
if (flag === 1) {
return "one";
}
else {
return "not one";
}
}
which then is imported using custom-js-import.html element:
<script src="global.function.js"></script>
When I tried to access the above function in custom-element.html, I am able to access it in the script part but not in the template part.
Is there any way I can access the function inside the HTML element?
<!-- custom-element.html -->
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<link rel="import" href="custom-js-import.html">
<dom-module id="custom-element">
<template>
<div>
Hello
</div>
<div id="data"></div>
<div>{{getData(1)}}</div><!-- Unable to access this from here -->
<div>{{getLocalData()}}</div>
</template>
<script>
// Define the class for a new element called custom-element
class CustomElement extends Polymer.Element {
static get is() { return "custom-element"; }
constructor() {
super();
}
ready(){
super.ready();
this.$.data.textContent = "I'm a custom-element.";
console.log(getData(1));//can be easily accessed from here
}
getLocalData(){
return "local";
}
}
// Register the new element with the browser
customElements.define(CustomElement.is, CustomElement);
</script>
</dom-module>
Sample Code
Is there any way I can access the function inside the HTML element?
Not really. In order to use data in a template you need to bind it to a property (Polymer calls this data binding).
Polymer's data binding system is designed for binding values to a template. Those values are typically just literals (e.g. strings and numbers) or plain ole javascript objects e.g. {a: 'someval', b: 5}. Polymer's data binding system is not designed to bind functions to a template and you can't just use javascript inside of a template. (If you're really into that idea, check out React as a replacement to polymer).
The polymer way to do what you're trying to do is to use a computed property. Instead of calling a function inside the template, create a computed property that reacts to changes of other variables. When the state of a property changes, the computed property will change too. This state can be thought of as the argument of your function.
I think it's better just to see the code working yeah (tested in chrome)?
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<link rel="import" href="custom-js-import.html">
<dom-module id="custom-element">
<template>
<div>
Hello
</div>
<label>
<input type="number" value="{{flag::input}}">
</label>
<h1>from flag: [[flag]]</h1>
<div id="data"></div>
<div>{{boundComputedData}}</div><!-- Unable to access this from here -->
<div>{{getLocalData()}}</div>
</template>
<script>
// Define the class for a new element called custom-element
class CustomElement extends Polymer.Element {
static get is() {
return "custom-element";
}
constructor() {
super();
}
getData(flag) {
const flagAsNumber = parseInt(flag);
if (flagAsNumber === 1) {
return "one";
} else {
return "not one";
}
}
ready() {
super.ready();
this.$.data.textContent = "I'm a custom-element.";
console.log(this.getData(1)); //can be easily accessed from here
}
getLocalData() {
return "local";
}
static get properties() {
return {
flag: {
type: Number,
value: 0
},
boundComputedData: {
type: String,
computed: 'getData(flag)'
}
};
}
}
// Register the new element with the browser
customElements.define(CustomElement.is, CustomElement);
</script>
</dom-module>
<custom-element></custom-element>
So What I'm doing here is:
creating a computed property boundComputedData and setting the computed property to 'getData(flag)' which will make it use the class function getData.
Now whenever the state the property flag changes, the computed property will update.
Hope it helps!
Thanks to Rico Kahler for suggesting me to use a mixin. Using mixin solved my problem. You can view the full working sample here.
All the global functions can be defined in the mixin.
<!--custom-mixin.html-->
<script>
const CustomMixin = superclass => class extends superclass {
static get properties() {
return {};
}
connectedCallback() {
super.connectedCallback();
}
getData(flag) {
if (flag === 1) {
return "one(From Mixin)";
} else {
return "not one(From Mixin)";
}
}
};
</script>
And remember to import the mixin file and add that mixin to your element.
<!-- custom-element.html -->
<link rel="import" href="https://polygit.org/components/polymer/polymer-element.html">
<link rel="import" href="custom-mixin.html">
<dom-module id="custom-element">
<template>
<div>
Hello
</div>
<div id="data"></div>
<div>{{getData(1)}}</div>
<!-- Unable to access this from here -->
<div>{{getLocalData()}}</div>
</template>
<script>
// Define the class for a new element called custom-element
class CustomElement extends CustomMixin(Polymer.Element) {
static get is() {
return "custom-element";
}
constructor() {
super();
}
ready() {
super.ready();
this.$.data.textContent = "I'm a custom-element.";
console.log(getData(1)); //can be easily accessed from here
}
getLocalData() {
return "local";
}
}
// Register the new element with the browser
customElements.define(CustomElement.is, CustomElement);
</script>
</dom-module>

Polymer binding tap to an object method

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);

Polymer: Instance polymer element from another element

I'm wondering how could I instance a Polymer element when I click on another element. Is there a way to instance window-base from dock-icon? (code down). I though I could use the constructor every element has but I can't figure out how this works. How could I pass a variable to that constructor.
Code of the two elements involved:
<polymer-element name="dock-icon" attributes="name" on-click="{{click}}">
<template>
<link rel="stylesheet" href="dock-icon.css">
</template>
<script>
Polymer('dock-icon', {
name: "",
click: function (event, detail, sender) {
alert(this.name);
//instance <window-base> and pass name parameter
}
});
</script>
Polymer element that has to be instanced
<polymer-element name="window-base" attributes="name height width left top">
<template>
<link rel="stylesheet" href="window-base.css">
<div id="box">
<header id="header"><h2>{{name}}</h2></header>
</div>
</template>
<script>
Polymer('window-base', {
name: "name",
//more stuff here
});
</script>
Thanks
This should be straightforward as:
var el = document.createElement('window-base');
el.name = 'some name';

Categories