I am having huge difficulty to implement simple dropdown list with Polymer 0.5.
I am also parallel migrating from Polymer .5 to 1.0. But that is separate discussion (
Migrating Polymer project .5 to 1.0 error).
Here is the code I am using to define polymer element inside body:
<polymer-element name="x-trigger" extends="paper-icon-button" relative="" on-tap="{{toggle}}" noink="">
<template>
<shadow></shadow>
<content></content>
</template>
</polymer-element>
I am using the element further down the body like this:
<x-trigger icon="menu" relative="" noink="" role="button" tabindex="0" aria-label="menu">
<paper-dropdown tabindex="-1" class="core-transition" style="outline: none; display: none;">
halign = left
<br>
valign = top
</paper-dropdown>
</x-trigger>
I defined following script section in the head section of the page:
<script>
Polymer('x-trigger', {
toggle: function () {
if (!this.dropdown) {
this.dropdown = this.querySelector('paper-dropdown');
}
this.dropdown && this.dropdown.toggle();
}
});
</script>
The problem is, I do see the icon button in the page but when ever I click on that button, nothing happens.
Further debugging revealed,
If I open the console debugger in chrome and
Place break point on Polymer or inside toggle method in the script section
Do page refresh
Break point gets hit and drop-down works
I don’t know what is causing the issue
Update: While debugging i got the following error in the line:
Polymer('x-trigger', {
/deep/ combinator is deprecated
Does this mean that i have to upgrade to polymer v1 to resolve this issue or is their any workaround for polymer 0.5?
The difference between Polymer 0.5 and 1.0 is really quite large. The /deep/ selector you reference was one of the big issues I faced migrating.
I recently migrated a project from 0.5 to 1.0 and in order to do so I had to change all instances of /deep/ to the new notation.
My advice would be to migrate from 0.5 to 1.0 first, then use the new Polymer documentation to come up with a solution.
In that project I implemented a simple drop-down. Here's my approach:
<dom-module id="profile-edit-page">
<style>
// Styling
</style>
<template>
<div class="container-app">
<div class="container-inner">
<!-- Other form elements -->
<input is="iron-input" id="filterInput" type="text" required placeholder="Automotive assistant" label="Occupation" bind-value="{{order.occupation}}" on-focus="startPickingOccupation" on-keydown="updateFilter" on-blur="stopPickingOccupation" class="block field input-reg mb2"></input>
<div class$="[[pickingOccupationClass(pickingOccupation)]]">
<paper-menu >
<template id="occupationRepeat" is="dom-repeat" items="[[occupations]]" filter="isShown">
<paper-item class="option" on-click="pickOccupation">[[item]]</paper-item>
</template>
</paper-menu>
</div>
<button class$="inputClass" class="btn btn-primary btn-block" on-click="forward" value="{{order.registration}}">Continue</button>
</div>
</div>
</template>
</dom-module>
<script>
Polymer({
properties: {
order: Object,
pickingOccupation: {
type: Boolean,
value: false
},
occupationFilter: {
type: String,
value: ""
},
occupations: {
type: Array,
value: ["Abattoir Worker",
"Accommodation Officer",
"Accountant",
// Etc.
"Zoology Consultant"]
}
},
is: "profile-edit-page",
pickOccupation: function(e) {
this.set('order.occupation', e.model.item);
this.set('pickingOccupation', false);
},
startPickingOccupation: function() {
this.pickingOccupation = true;
},
stopPickingOccupation: function() {
this.async(function() {
this.pickingOccupation = false;
},500);
},
updateFilter: function() {
if(typeof(this.$.occupationRepeat) === "undefined") {
return;
}
this.set('occupationFilter', this.$.filterInput.value.toLowerCase());
this.async(function() {
this.$.occupationRepeat.render();
},50);
},
isShown: function(item) {
if(this.order.occupation == '') {
return false;
}
return (item.toLowerCase().search(this.occupationFilter) !== -1);
},
pickingOccupationClass: function(picking) {
if(this.pickingOccupation) {
return "picking";
} else {
return "hidden";
}
}
});
</script>
Move the script into the actual polymer-element:
<polymer-element name="x-trigger" extends="paper-icon-button" relative="" on-tap="{{toggle}}" noink="">
<template>
<shadow></shadow>
<content></content>
</template>
<script>
Polymer('x-trigger', {
toggle: function () {
if (!this.dropdown) {
this.dropdown = this. querySelector('paper-dropdown');
}
this.dropdown && this.dropdown.toggle();
}
});
</script>
</polymer-element>
Related
I have this vue template, with a number of child components, many of which use overflow-y to show a scrollbar when they get too much content.
<div #wheel="onWheel">
...
</div>
And the corresponding handler is:
onWheel: function (ev) {
//console.log(ev)
if (event.deltaY < 0) this.goto(-1)
else if (event.deltaY > 0) this.goto(+1)
},
I.e. the desired behaviour is for the mouse wheel to move up or down a database record, unless it has something more obvious to do. By that I mean, if it is over a component with a scroll bar, the user will expect the mouse wheel to scroll within that component, and not change the database record!
Is there a way to do this? I tried adding .self:
<div #wheel.self="onWheel">
...
</div>
But that stopped it working completely.
I wondered about trying to intercept the wheel event in each child component, but (apart from that being a maintenance nightmare) I am not sure this is possible as it is a special one (at least in Chrome) that you cannot call preventDefault() on.
I can see from looking at ev that I know what element the mouse is over from ev.target. Could I somehow go from that to finding out if it, or any parent, is showing a scrollbar?
Well, it's not so much that you want to preventDefault as you want the event to not bubble up to the parent if the child component has its own defined wheel event. You can do this using:
event.stopPropagation();
Code Sandbox:
I created a codesandbox demo here: https://codesandbox.io/s/wheel-propagation-demo-cocnx
Be sure to open the console in the demo to see the different events fire.
Code Example:
App.vue
<template>
<div #wheel="onWheel" id="app">
<img width="25%" src="./assets/logo.png">
<Base msg="Hello Vue in CodeSandbox!">this is a test</Base>
</div>
</template>
<script>
import Base from "./components/Base";
export default {
name: "App",
components: {
Base
},
methods: {
onWheel: function(e) {
console.log("wheeling over " + e.currentTarget.id);
}
}
};
</script>
Base.vue
<template>
<div class="hello" style="margin-top: 30px; padding: 30px; background: beige;">
<slot></slot>
<Wheeler>wheel me!</Wheeler>
</div>
</template>
<script>
import Wheeler from "./Wheeler.vue";
export default {
components: {
Wheeler
},
name: "Base",
props: {
msg: String
}
};
</script>
Wheeler.vue
<template>
<div #wheel="otherWheel" class="wheelMe">
<slot></slot>
</div>
</template>
<script>
export default {
methods: {
otherWheel: function(e) {
console.log("wheeling over " + e.currentTarget.className);
e.currentTarget.style["background-color"] =
"rgb(" +
[
Math.floor(Math.random() * 254),
Math.floor(Math.random() * 254),
Math.floor(Math.random() * 254)
].toString() +
")";
e.stopPropagation();
}
}
};
</script>
Note:
If you want to stop the wheel from causing a vertical scroll to occur you would handle that event separately.
I'm trying to create a custom slider to emulate horizontal scroll behavior of a specific element. the slider is implemented as a stand alone component and the selector for the element is passed to the slider component as a property:
<template>
<div class="slidercontainer">
<h1>{{contentWidth}}</h1>
<button class="right-scroll"></button>
<input
type="range"
min="1"
:max="contentWidth"
value="1"
class="slider"
v-on:input="handleScroll"
>
<button class="left-scroll"></button>
</div>
</template>
<script>
export default {
props: ["el"],
data() {
return {
scrollLeft: 0
};
},
methods: {
handleScroll($event) {
this.content.scrollLeft = this.contentWidth - $event.target.value;
}
},
computed: {
content() {
return document.querySelector(this.el);
},
contentWidth() {
return this.content.scrollWidth - this.content.clientWidth;
}
}
};
</script>
the problem though, is that content wont update and always return 0. when first loaded the app is waiting for some data from the server and thats why when mounted the contentWidth equals 0, but i thought returning content as a computed property should take care of that, but nothing happens after the content is injected with some new...well - content :)
any idea how to solve this?
I'm used to jquery and I'm rewriting my code to Vue. Need a little help with this basic onclick event.
Original html:
<div id="main">
<div id="right-nav" onclick="closeRightNav();"></div>
<i onclick="openRightNav();"></i>
</div>
Original javascript:
function openRightNav() {
$("#right-nav").width("100%");
}
function closeRightNav() {
$("#right-nav").width("0");
}
This is where I'm currently at:
New html:
<div id="main">
<div id="right-nav" #click="closeRightNav"></div>
<i #click="openRightNav"></i>
</div>
New javascript using Vue:
var app = new Vue({
el: '#main',
data: {
width: '100%'
},
methods: {
openRightNav() {
$("#right-nav").width("100%"); // not sure how to write this part in Vue?
}
}
});
How do I write this correctly in Vue?
Ok. You can approach this from multiple angles. I like to use v-show or v-if directives like so:
<template>
<div>
<div v-if="rightOpen" id="right-div" style="width:100%">Right Div</div>
<a #click="rightOpen = ! rightOpen">Toggle</a> // this will actually toggle right sidebar open and close
</div>
</template>
<script>
export default {
mounted() {
},
data: function() {
return {
rightOpen: false
}
}
}
</script>
Now you could use v-show instead of v-if difference is v-show element will be rendered on the page but not shown and v-if will not render element.
You could also use v-class like so
...
<div :class="{ 'someClass': !rightOpen,'sidebar-opened': rightOpen}" id="right-div">Right Div</div>
....
In this example someClass will be the one loaded when component is rendered, lets say width: 0 will be in that class. sidebar-opened class should contain width: 100%. Everything else stays the same from previous example.
in openRightNav you change your data and don't manipulate the DOM directly. Just say in your method: this.width = 100.
in your html then you need to bind your width attribute like this:
<div id="right-nav" #click="closeRightNav" v-bind:style="{'width': width + '%'}"></div>
don't forget to set your default value for width to 0.
in closeRightNav you would only write: this.width = 0
Thanks to Yousef Kama here's how I ended up doing it:
New html:
<div id="main">
<div id="right-nav" #click="closeRightNav" v-bind:style="{'width': width + '%'}"></div>
<i #click="openRightNav"></i>
</div>
New Vue javascript:
var app = new Vue({
el: '#main',
data: {
width: 0
},
methods: {
openRightNav() {
this.width = 100;
},
closeRightNav() {
this.width = 0;
}
}
});
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 make custom element within a custom element and have the inner custom element able to change its value from within and the outer able sync its binding on that change. What can I do to get this working?
I've thoroughly scoured the documentation, but it's very lackluster in this department. I believe Node.bind() may be something of interest, but not sure how it would apply in this case.
Here's a simplified test case and plunker demo:
<polymer-element name='test-app'>
<template>
First:
<test-input id='one' value='{{value}}'></test-input>
<br/>
<br/>
Second:
<test-input id='two' value='{{value}}'></test-input>
<br/>
<br/>
Value:
{{value}}
</template>
<script>
Polymer({
value: 5
})
</script>
</polymer-element>
<polymer-element name='test-input'>
<template>
<style>
#val {
font-size: 50px;
}
</style>
<div id='val'>{{value}}</div>
<button on-tap='{{increment}}'>+</button>
<button on-tap='{{decrement}}'>-</button>
</template>
<script>
Polymer({
publish: {
value: 4
},
increment: function() {
this.value = this.value + 1;
},
decrement: function() {
this.value = this.value - 1;
}
})
</script>
</polymer-element>
<test-app></test-app>
http://plnkr.co/edit/KjQ9DusaFg2jp1BTFUde?p=preview
If this was working, the value property of the test-app parent element should be in sync with both of the test-inputs value property.
Notice this warning in the console:
Attributes on test-input were data bound prior to Polymer upgrading the element. This may result in incorrect binding types.
test-app uses test-input before Polymer knows about test-input. All of an element's dependencies must be declared before that element is declared. Move test-input above test-app and it works as expected.
http://plnkr.co/edit/ZaIj60S3lAHT18k5T3sn?p=preview