I have a bunch of paper-toggle-buttons that I am wanting to have the label display its current state.
I can get this to work simply enough on one button, but I am wanting to have them point to one function so that my code is cleaner.
I currently have this code, however both will toggle at the same time???
It's definitely the JS that is wrong.
Polymer({
is: 'example-element',
properties: {
labelText: {
type: String,
value: 'false'
}
},
checkToggle: function(e) {
var toggleButton = e.currentTarget.getAttribute('input');
this.$$('#' + toggleButton).checked ? this.labelText = "True" : this.labelText = "False";
},
});
<base href="https://polygit.org/polymer+polymer+v1.9.0/components/" />
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="paper-toggle-button/paper-toggle-button.html" />
<example-element></example-element>
<dom-module id="example-element">
<template>
<paper-toggle-button input="paper-toggle1" id="paper-toggle1" on-click="checkToggle">{{labelText}}</paper-toggle-button>
<paper-toggle-button input="paper-toggle2" id="paper-toggle2" on-click="checkToggle">{{labelText}}</paper-toggle-button>
</template>
</dom-module>
No point using a custom property for this, you may as well change the dom directly. This is just one way of doing it.
Polymer({
is: 'example-element',
properties: {
labelText: {
type: String,
value: 'false'
}
},
checkToggle: function(e) {
e.currentTarget.querySelector(".label").innerHTML = e.currentTarget.checked == true;
},
});
<base href="https://polygit.org/polymer+polymer+v1.9.0/components/" />
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="paper-toggle-button/paper-toggle-button.html" />
<example-element></example-element>
<dom-module id="example-element">
<template>
<paper-toggle-button input="paper-toggle1" id="paper-toggle1" on-click="checkToggle"><span class="label">false</span></paper-toggle-button>
<paper-toggle-button input="paper-toggle2" id="paper-toggle2" on-click="checkToggle"><span class="label">false</span></paper-toggle-button>
</template>
</dom-module>
Try changing only the innerText of the toggle which was clicked:
checkToggle : function(e) {
var toggleButton = this.$$('#' + e.currentTarget.getAttribute('input'));
toggleButton.checked ? toggleButton.innerText = "True" : toggleButton.innerText = "False";
}
Use the strength of properties.
<paper-toggle-button checked="{{paperToggle1}}">[[paperToggle1]]</paper-toggle-button>
<paper-toggle-button checked="{{paperToggle2}}">[[paperToggle2]]</paper-toggle-button>
You could do more advanced stuff as well, like saving each toggle button in an Object (paperToggle.first) nor adding a method that takes in the id and use that as a key in said Object.
Related
I am learning react.js
I created an example where there is a textarea,
If I enter comma seperated names in that textarea, I show a listing those
names below the textarea.
the working example is as follows,
click here
The code is as follows
<!DOCTYPE HTML>
<html>
<head>
<title>React Template</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<style type="text/css">
.mywidth{
width : 50%;
}
</style>
</head>
<body>
<!-- Body Content -->
<div id="mainContent"></div>
<!-- Body Content -->
<!-- The core React library -->
<script src="/node_modules/react/dist/react.min.js"></script>
<!-- The ReactDOM Library -->
<script src="/node_modules/react-dom/dist/react-dom.min.js"></script>
<script type="text/javaScript">
(function(){
'use strict'
// custom javaScript content
var DataFeedComponent = React.createClass({
propTypes : {
"defaultValue" : React.PropTypes.string.isRequired
},
getDefaultProps : function(){
return {
"content" : ""
}
},
getInitialState : function(){
return {
"content" : this.props.defaultValue
}
},
doRenderingUI : function(evt){
this.setState({
content : evt.target.value
});
},
render : function(){
var TextAreaElement = React.createElement("textarea",{
"className" : "mywidth",
"value" : this.state.content,
"onChange" : this.doRenderingUI
});
var listElementArray = [];
var someContent = this.state.content.split(",");
for(var x = 0; x < someContent.length; x++){
if(someContent[x] !== undefined && someContent[x] !== null && someContent[x] !== ""){
listElementArray.push(React.createElement("li",null,someContent[x]));
}
} // end of for
var orderedListingElement = React.createElement("ol",null,listElementArray);
var DivElement = React.createElement("div",null,TextAreaElement,orderedListingElement);
return DivElement;
}
});
var elementObject = React.createElement(DataFeedComponent,{ "defaultValue" : "Victor,Nick" });
ReactDOM.render(elementObject,document.getElementById("mainContent"));
})();
</script>
</body>
</html>
So my problem is
on event 'onChange' my example works fine,
render : function(){
var TextAreaElement = React.createElement("textarea",{
"className" : "mywidth",
"value" : this.state.content,
"onChange" : this.doRenderingUI
});
i.e. on entering the text in textarea I am able to see the listing below the textarea.
BUT
if I change to onBlur, it doesn't work. why ?
You have your component's value set as a derivative of your this.state.content. This is what is called a controlled component, in order to change what appears in the field, you must change the React state every time something happens (i.e. when a button is pressed).
onBlur is not activated when the input changes, it's activated when you deselect the element. Hence your app never has a chance to actually change the input.
Your component is a controlled component, since you have set value to your Textarea.
Everytime you type, onChange gets called, which in turn updates the state and that inturn updates the value in textarea. When you change to onBlur, your state is never set, and so when you type the value is not changing.
Simplest solution would be to change value to defaultValue, since i guess you want to pass initial value to your Textarea and that would look something like below
var TextAreaElement = React.createElement("textarea",{
"className" : "mywidth",
"defaultValue" : this.state.content,
"onBlur" : this.doRenderingUI
});
But if you want the component to be still controlled, then you need to pass both onChange and onBlur and handle it appropriately.
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 -
I'm starting to learn Polymer 1.0 and I couldn't figure out how to programatically search for insertion points. I realize I could wrap a <div> around the <content> tag and check if that <div> has children or not, but that requires rendering a <div> for every element, which seems wasteful. Is there a way, with JavaScript, to check if any insertion points have been loaded? Ideally, I'd have a function thereAreInsertionPoints which would determine whether or not the <p> tag would render. My Polymer code looks like this:
<template>
<h1>{{title}}</h1>
<p>{{body}}</p>
<content id="content"></content>
<p if="{{thereAreInsertionPoints()}}">There are insertion points!</p>
</template>
<script>
Polymer({
is: "post-content",
properties: {
title: String,
body: String
},
thereAreInsertionPoints: function(){
//determine whether or not we have insertion points
}
});
</script>
There are various Polymer APIs for working with the DOM including Content APIs.
Content APIs:
Polymer.dom(contentElement).getDistributedNodes()
Polymer.dom(node).getDestinationInsertionPoints()
These APIs can be used in various ways to check for distributed nodes and insertion points. I have created a working implementation that shows the post-content element with additional methods to check for distributed nodes and destination insertion points.
<script src="http://www.polymer-project.org/1.0/samples/components/webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import"
href="http://www.polymer-project.org/1.0/samples/components/polymer/polymer.html">
<dom-module id="post-content">
<template>
<h1>{{title}}</h1>
<p>{{body}}</p>
<content></content>
<template is="dom-if" if="{{destinationInsertionPointsExist()}}">
<p>Destination insertion point(s) exist.</p>
</template>
<template is="dom-if" if="{{distributedNodesExist()}}">
<p>Distributed node(s) exist.</p>
</template>
</template>
<script>
Polymer({
is: "post-content",
properties: {
title: String,
body: String
},
destinationInsertionPointsExist: function () {
var distributedNodes = Polymer.dom(this).childNodes;
var countDestinationInsertionPoints = 0;
distributedNodes.forEach(function (distributedNode) {
var distributedNodeHasDestinationInsertionPoints = Polymer.dom(distributedNode).getDestinationInsertionPoints().length > 0 ? true : false;
if (distributedNodeHasDestinationInsertionPoints) {
countDestinationInsertionPoints++;
}
});
return countDestinationInsertionPoints > 0 ? true : false;
},
distributedNodesExist: function () {
var contentNodes = Polymer.dom(this.root).querySelectorAll("content");
var countDistributedNodes = 0;
contentNodes.forEach(function(contentNode) {
var contentNodehasDistributedNodes = Polymer.dom(contentNode).getDistributedNodes().length > 0 ? true : false;
if (contentNodehasDistributedNodes) {
countDistributedNodes++;
}
});
return countDistributedNodes > 0 ? true : false;
}
});
</script>
</dom-module>
<post-content title="This is the title" body="This is the body">
<p>This is distributed content</p>
</post-content>
A few notes about the code:
I made a lot of the variable names and ternary checks very verbose for clarity in this answer. Changes could be made to simplify the code.
For example:
var distributedNodeHasDestinationInsertionPoints = Polymer.dom(distributedNode).getDestinationInsertionPoints().length > 0 ? true : false;
could become something like
var hasInsertionPoints = Polymer.dom(distributedNode).getDestinationInsertionPoints().length
Use the new (Polymer 1.0) dom-if conditional template.
<p if="{{thereAreInsertionPoints()}}">There are insertion points!</p>
becomes
<template is="dom-if" if="{{destinationInsertionPointsExist()}}">
<p>Destination insertion point(s) exist.</p>
</template>
I would recommend stepping through the destinationInsertionPointsExist and distributedNodesExist methods to insure that you fully understand what is actually being checked. You may need to modify these methods to suit your particular needs and requirements.
For example, even if you have a single space between the post-content element start and end tag both of these methods will return true.
<post-content title="This is the title" body="This is the body"> </post-content>
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'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';