How can I bind a polymer behavior to a child element? - polymer

I am reading about Behaviors in Polymer.
I copy/pasted the example for the highlight-behavior.html:
<script>
HighlightBehavior = {
properties: {
isHighlighted: {
type: Boolean,
value: false,
notify: true,
observer: '_highlightChanged'
}
},
listeners: {
click: '_toggleHighlight'
},
created: function() {
console.log('Highlighting for ', this, 'enabled!');
},
_toggleHighlight: function() {
this.isHighlighted = !this.isHighlighted;
},
_highlightChanged: function(value) {
this.toggleClass('highlighted', value);
}
};
Then, in my element i have the following (just the important parts):
<link rel="import" href="highlight-behavior.html">
<dom-module id="highlighting-test">
<template>
<style>
:host {
display: block;
background-color: red;
}
:host.highlighted {
background-color: green;
}
.highlighted {
background-color: green;
}
</style>
<h1>Click anywhere here to toggle highlighting!</h1>
</template>
<script>
Polymer({
is: 'highlighting-test',
behaviors: [HighlightBehavior]
});
</script>
</dom-module>
Now the problem is that the toggling of the highlighted class works when clicking inside the host element but it is not highlighting just the h1 element. It is adding the highlighted class to the host element.
This is how it is rendered in the browser:
<highlighting-test class="highlighted">
<h1 class="style-scope highlighting-test">Click anywhere here to toggle highlighting!</h1>
</highlighting-test>
When clicking I indeed see that it toggles the highlighted class on the host element highlighting-test and the background changes.
How can I make sure that the highlighting behavior is applied to just the h1 tag?

Use this.toggleClass(className, bool, this.$.id_of_element)

Change:
_highlightChanged: function(value) {
this.toggleClass('highlighted', value);
}
to:
_highlightChanged: function(value) {
this.$.hId.toggleClass('highlighted', value);
}
And in HTML add an ID to H1:
<h1 id="hId">Click anywhere here to toggle highlighting!</h1>

Related

How to target HTML tags inside carousel

I have a quick question about BootstrapVue carousel:
How can I target the HTML tag inside the b-carousel-slide component to change things like font size and so on?
I know that you can change the HTML tag by using the text-tag prop (see my code example).
Would be great if this one's easy to solve or at least someone points me to the respective section in the documentation.
<template>
<div>
<b-carousel
id="carousel"
:interval="3000"
controls
fade
indicators
background="#ababab"
img-width="1024"
img-height="480"
>
<b-carousel-slide
v-for="item in services"
:key="item.title"
:caption="item.title"
:text="item.description"
text-tag="p"
:img-src="require(`../assets/images/${item.image}`)"
></b-carousel-slide>
</b-carousel>
</div>
</template>
<script>
export default {
name: 'Carousel',
data() {
return {
services: [
{
title: 'Title',
description:
'Text',
image: 'picture.jpg',
},
{
title: 'Title',
description: `Text`,
image: 'picture.jpg',
},
{
title: 'Title',
description: `Text`,
image: 'picture.jpg',
},
{
title: 'Title',
description: `Text`,
image: 'picture.jpg',
},
],
};
},
};
</script>
<style scoped>
p {
color: 'red';
}
</style>
The issue you're facing is trying to target a sub-component inside a scoped style tag.
To do this, you will need to use deep selectors.
Either of these should work.
<style scoped>
/deep/ p {
color: 'red';
}
>>> p {
color: 'red';
}
::v-deep p {
color: 'red';
}
</style>

Polymer 2.0 : listeners not working

<dom-module id="polymer-starterkit-app">
<template>
<style>
:host {
display: block;
}
#box{
width: 200px;
height: 100px;
border: 1px solid #000;
}
</style>
<h2>Hello, [[prop1]]!</h2>
<paper-input label="hello">
</paper-input>
<div id="box" on-click="boxTap"></div>
</template>
<script>
/** #polymerElement */
class PolymerStarterkitApp extends Polymer.Element {
static get is() { return 'polymer-starterkit-app'; }
static get properties() {
return {
prop1: {
type: String,
value: 'polymer-starterkit-app'
},
listeners:{
'click':'regular'
},
regular:function(){
console.log('regular')
}
};
}
boxTap(){
console.log('boxTap')
}
}
window.customElements.define(PolymerStarterkitApp.is, PolymerStarterkitApp);
</script>
</dom-module>
As shown in the code above, I have tried to define a simple listener on-tap on my div with the class box but it doesn't seem to work!
I think I'm using the wrong syntax.
Also, why should we use listeners if we can simply use predefined listeners like on-click and on-tap?
I would really appreciate any type of help!
Edit: I helped updating Polymer's documentation. It's now very clear and detailed. https://www.polymer-project.org/2.0/docs/devguide/events#imperative-listeners Just read that and you're good. TL;DR: The listeners object is no more in Polymer 2.0, but there's a new way to do it.
You could simply set them up in ready(). There is no need to use .bind() in this case because this will be your custom element in the callback because it's the event's current target.
ready () {
super.ready()
this.addEventListener('my-event', this._onMyEvent)
}
_onMyEvent (event) { /* ... */ }
If you need to listen for events on something that is not your custom element itself (e.g. window), then do it the way it is shown in the Polymer documentation:
constructor() {
super();
this._boundListener = this._myLocationListener.bind(this);
}
connectedCallback() {
super.connectedCallback();
window.addEventListener('hashchange', this._boundListener);
}
disconnectedCallback() {
super.disconnectedCallback();
window.removeEventListener('hashchange', this._boundListener);
}
Source: https://www.polymer-project.org/2.0/docs/devguide/events#imperative-listeners
You must create the listener manually
connectedCallback() {
super.connectedCallback();
this.addEventListener('click', this.regular.bind(this));
}
disconnectedCallback() {
super.disconnetedCallback();
this.removeEventListener('click', this.regular);
}
regular() {
console.log('hello');
}
However, to add a listener to an element like the div, you need to add Polymer.GestureEventListeners
class PolymerStarterkitApp extends Polymer.GestureEventListeners(Polymer.Element) {
}

Polymer add class when property is true

I have seen this in angular before and wondered if this is possible in polymer as well. Angular - What is the best way to conditionally apply a class?
I have set up a property named 'animated':
animated: {
type: Boolean,
value: false,
},
When animated is true, a div inside my element should have a css class of .animate.
<div class=""></div>
For now I have done that inside of the ready function.
But since I came across that Stackoverflow question I wondered if this is prossible in polymer.
Thanks!
One way to do that is using a function as follow:
<div class$="{{_getClass(animated)}}"></div>
Where class$ with $ symbol indicates to Polymer's that property is generate using data binding. So, your _getClass function will look like this:
_getClass: function(animated){
return animated ? "animate" : "";
}
When animate property changes, the _getClass function will be invoked and this function will return the string that indicates the class you need.
You can also use toggleClass method of Polymer
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
<dom-module id="my-element">
<template>
<style>
.show {
display: block !important;
border: 1px solid black;
}
.hide {
width: 100px;
height: 100px;
display: none;
}
</style>
<div class="hide" id="toggle"></div>
<button on-tap="_toggleDiv">Press to toggle</button>
</template>
</dom-module>
<script>
Polymer({
is: 'my-element',
properties: {
show: {
type: Boolean,
value: false
}
},
_toggleDiv: function() {
this.show = !this.show;
this.toggleClass('show', this.show, this.$.toggle);
}
});
</script>
<my-element></my-element>

Extending paper-fab removes ripple effect

I have tried to extend paper-fab element but after that I lost ripple effect.
My code looks like this:
<polymer-element name="custom-fab" attributes="name count" extends="paper-fab">
<template>
<style>
:host{
margin: 5px;
};
</style>
<shadow></shadow>
</template>
<script>
Polymer({
count: 0,
created: function() {
this.name = {};
}
})
</script>
</polymer-element>
That was because my element was empty, I get the ripple effect again after adding some text to label attribute.
<custom-fab label="test"></custom-fab>

Polymer - styling childnodes of host

first, i searched for similar questions but haven't found a solution for my problem, which is basically simple, i guess. :)
I built a simple image-slider for clearing up the whole concepts of web components for myself with a real world example.
My custom component is made out of 5 components and a headline.
stage-slider
stage-element
h1
stage-button
stage-teaserdock
stage-teaser
The component slides fine. Now i wanted to add teaser navigation at the bottom. So first i tried adding a single teaser item.
Ok.. what i want to do is access an element inside of the stage-slider:
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../stage-element/stage-element.html">
<link rel="import" href="../stage-button/stage-button.html">
<polymer-element name="stage-slider" attributes="items slideInterval">
<template>
<style>
:host {
width: 960px;
height: 485px;
position: absolute;
top: 50%;
left: 50%;
margin: -242px 0px 0px -480px;
overflow: hidden;
display: block;
}
:content .teaser
{
left: 30px;
}
</style>
<div id="wrapper">
<template id="slider" repeat="{{item in items}}">
<stage-element headline="{{item.headline}}"
image="{{item.image}}"
buttonLabel="{{item.buttonLabel}}"
buttonTargetWindow="{{item.buttonTargetWindow}}"
buttonTargetURL="{{item.buttonTargetURL}}">
</stage-element>
</template>
<content class="teaser" select="stage-teaser"></content>
</div>
</template>
<script src="./libs/TweenLite/easing/EasePack.min.js"></script>
<script src="./libs/TweenLite/plugins/CSSPlugin.min.js"></script>
<script src="./libs/TweenLite/TweenLite.min.js"></script>
</polymer-element>
<script>
Polymer('stage-slider',
{
slideInterval: 7000,
items: [],
index: 0,
ready: function ()
{
console.log('-----------------');
console.log('stage slider ready!');
},
attached: function ()
{
console.log('-----------------');
console.log('stage slider attached!');
this.$.wrapper.style.width = (960 * (this.items.length)).toString() + "px";
//
if (this.items.length > 1 && this.slideInterval != 0)
{
var that = this;
setInterval(function ()
{
that.startSliding(that);
}, this.slideInterval
);
}
},
startSliding: function (shadowDom)
{
console.log('More children than 1 -> SLIDE EM!');
TweenLite.to(shadowDom.$.wrapper, 1.5, {
marginLeft: -960,
ease: Expo.easeInOut,
onStart: function ()
{
console.log('tween started'); //, this = ', this);
},
onComplete: function ()
{
// console.log('tween complete');
// console.log(shadowDom.$.wrapper.getElementsByTagName('stage-slide')[0]);
shadowDom.$.wrapper.style.marginLeft = 0;
shadowDom.$.wrapper.appendChild(shadowDom.$.wrapper.getElementsByTagName('stage-element')[0]);
}});
}
});
</script>
This is how my markup looks like:
<stage-slider slideInterval="0"
items='[
{
"headline" : "Test headline",
"image" : "img/slide0.jpg",
"buttonLabel" : "Test buttonlabel",
"buttonTargetURL" : "http://www.google.com"
}
]'>
<stage-teaser class="teaser"
image="img/teaser0.jpg"
headline="Test teasertext"
targetURL="http://google.com">
</stage-teaser>
</stage-slider>
So there is a stage-teaser element nested inside my stage-slider element.
I thought i have to distribute it to the content tag inside my template element. Which is why there is a content tag like this:
<content class="teaser" select="stage-teaser"></content>
It displays the teaser item correctly.
But now i want to define its css from within the slider component. This is where i am totally stuck..
I can access the element itself with :host, thats good.
But how do i access the content element, which renders the teaser?
i tried the following:
:host(stage-teaser),
:host(.teaser),
:host(#teaser),
:content .teaser,
:host(:content .teaser),
as you can see.. i am kinda stuck. :-/
any idea would be cool!
thanks,
Rob
I suspect that the issue you're seeing is just a typo. Instead of :content you want ::content. Here's a jsbin showing a simple example: http://jsbin.com/mijifiru/1/edit and for more info on styling web components with the shadow DOM, check out this article: http://www.html5rocks.com/en/tutorials/webcomponents/shadowdom-201/
If that doesn't solve the issue it would be helpful if you reduced your code down to a Minimal, Complete, and Verifiable example, and for bonus points do so in an online editor like jsbin.
<polymer-element name='my-container' noscript>
<template>
<style>
::content .innerContent {
background-color: red;
}
</style>
Shadow Dom
<content></content>
</template>
</polymer-element>
<my-container>
<div class='innerContent'>Contained matching Light DOM</div>
<div>Contained unmatched Light DOM</div>
</my-container>