Web component, how to get an element by Id? - polymer

I want to grab an element by its ID. My template looks like this:
<template>
<h2>Welcome</h2>
<div id="toGrab"></div>
</template>
I tried using jQuery but the below snippet doesn't work:
$('#toGrab').dosomething();
Any idea?

If you code is inside the Polymer element, just do
var div = this.$.toGrab;

In Polymer 3:
ready() {
super.ready();
const x = this.shadowRoot.getElementById('divID');
}

also...
var div = this.$$("#toGrab");

Related

Access dom-repeat element's CSS class on tap

My dom repeat displays a list of icons which I can bookmark or unbookmark ,which generating dom-repeat I call a function to find if this icon is bookmarked or not,that will return CSS class
.book-marked {
color: red;
}
.not-book-marked {
color: green;
}
<template is="dom-repeat" items="{{membersList}}">
<iron-icon icon="bookmark" class$="[[_computeBookMark(item.userId)]]" on-tap="_toogleBookMark"></iron-icon>
</template>
Once I get all my list of icon now if user click that icon I need to toogle css class.so I wrote on-tap function
_toogleBookMark:function(e) {
var userData = e.model.item; //gets entire data object of that element
var index = e.model.index; //gets index of that element
},
I can't use ID since its dom-repeat ,Is there any other ways so that I can change CSS of that dom-repeat element in _toogleBookMark() function on clicking? or is it possible to change CSS with index??or using "e" reference in _toogleBookMark(e) function !!
Not sure if I understood correctly - you want to access the element you've tapped?
Just use the event.target property then. It will return the element on which the event happened, in this case, the icon you have tapped.
_toogleBookMark = function(e) {
e.target.classList.toggle("not-book-marked");
}
Check this example.
Mind you:
1) When using Shady DOM, assuming our element is a custom element, target can be a component from the element's template, not the element itself. To prevent that, use Polymer.dom(e).localTarget (read more here).
2) When using a custom element with light DOM children, the above may not be enough, your (local)target will be a light DOM child, not the element you wished for. In that case, use Element.closest(selector) to (optionally) go up the DOM to the element you want. Read more about the method here.
As you just want to swap your class on tap, do it like this:
Add an own attribute, like data-id="1" and the id attribute, but be sure they have the same value:
<template is="dom-repeat" items="{{membersList}}">
<iron-icon icon="bookmark" class$="[[_computeBookMark(item.userId)]]" on-tap="_toogleBookMark" data-id="{{item.userId}}" id="{{item.userId}}"></iron-icon>
</template>
Now, inside your _toggleBookMark function, you can access the current tapped element and swap CSS classes by:
_toogleBookMark:function(e) {
// this gives you your userId from the data-id attribute
var userId = e.path[0].dataId;
// we can access the element now with
var element = this.$$('#' + e.path[0].dataId);
if (element.classList.contains('book-marked')) {
element.classList.remove('book-marked');
element.classList.add('not-book-marked');
} else {
element.classList.add('book-marked');
element.classList.remove('not-book-marked');
}
},

Use Reveal.js with Polymer

I have a project with Polymer + Reveal.js
I have a view with polymer that gets all the Slides/Sections.
<template is="dom-repeat" items="[[my.slides]]" as="slide">
<section>
<h1>slide.title</h1>
<h2>slide.content</h2>
</section>
</template>
When I try to start Reveal.js, I have the issue related to:
(index):21136 Uncaught TypeError: Cannot read property
'querySelectorAll' of undefined
I think is because Reveal.js cannot select a Webcomponent generated by Polymer, because Reveal.js needs to have all slides content wrote on the html file by separate.
Then my question is: How to use Polymer Webcomponents with Reveal,js?
Alan: Yes, you are right.
Now I am creating DOM elements directly with JS avoiding Polymer shadowDOM elements.
Then I created a function called createSlides where - based in a JSON response - I appending slides (sections) within slides div.
Fist I create a Polymer template similar to:
<template>
<div class="reveal">
<div id="slides" class="slides">
<section>
This section will be removed
</section>
</div>
</div>
</template>
Next I removed the unused slide and appended some slides. Finally start the Reveal presentation
ready()
{
this.removeInitialSlide();
this.addSomeSlides();
this.startRevealPresentation();
}
clearInitialSlides()
{
var slidesComp = this.$.slides;
while (slidesComp.hasChildNodes()) {
slidesComp.removeChild(slidesComp.lastChild);
}
}
addSomeSlides()
{
var slide1 = document.createElement("section");
var image = document.createElement("img");
image.src = "some/path/to/image.jpg";
slide1.appendChild(image);
this.$.slides.appendChild(slide1);
var slide2 = document.createElement("section");
slide2.innerHTML = "<h1>Test content</h1>"
this.$.slides.appendChild(slide2);
}
Working fine for now.
I think you most likely can't use reveal.js in a web component created with Polymer right now and here's why.
If you look at reveal.js's code it looks for dom elements with the reveal and slides classes on the main document like this:
dom.wrapper = document.querySelector( '.reveal' );
dom.slides = document.querySelector( '.reveal .slides' );
The problem with that is that Polymer elements have their own local dom which is a different dom tree which can't be accessed using methods like document.querySelector which means reveal.js can't access to them.

Angular conditional container element

I have a large chunk of HTML in an ng-repeat that for certain elements has a container element and for others it does not. I'm currently achieving this with two ng-ifs:
<strike ng-if="elem.flag">
… <!-- several lines of directives handling other branching cases -->
</strike>
<div ng-if="!elem.flag">
… <!-- those same several lines copied-and-pasted -->
</div>
While this works, it means I have to remember copy-and-paste any edits, which is not only inelegant but also prone to bugs. Ideally, I could DRY this up with something like the following (inspired by ng-class syntax):
<ng-element="{'strike':flag, 'div':(!flag)}">
… <!-- lots of code just once! -->
</ng-element>
Is there any way to achieve a similarly non-repetitive solution for this case?
You can make such directive yourself.
You can use ng-include to include the same content into both elements.
Assuming the effect you desire is to have the text within your tag be striked through based on the condition of the elem.flag:
You could simply use the ng-class as follows
angular.module('ngClassExample', [])
.controller('elemController', Controller1);
function Controller1() {
vm = this;
vm.flag = true;
vm.clickItem = clickItem
function clickItem() {
// Toggle the flag
vm.flag = !vm.flag;
};
}
.strikethrough{
text-decoration: line-through
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='ngClassExample' ng-controller="elemController as elem">
<div ng-class="{strikethrough: elem.flag}" ng-click="elem.clickItem()">
element content should be sticked through: {{elem.flag}}
</div>
</div>
You can do it with a directive
module.directive('myFlag', function() {
var tmpl1 = '<strike>...</strike>';
var tmpl2 = '<div>...</div>';
return {
scope: {
myFlag: '='
},
link: function(scope, element) {
element.html(''); // empty element
if (scope.myFlag) {
element.append(tmpl1);
} else {
element.append(tmpl2);
}
}
};
});
And you just use it like:
<div ng-repeat="item in list" my-flag="item.flag"></div>
You could create a directive which will transclude the content based on condition. For tranclusion you could use ng-transclude drirective, in directive template. Also you need to set transclude: true.
HTML
<my-directive ng-attr-element="{{elem.flag ? 'strike': 'div'}}">
<div> Common content</div>
</my-directive>
Directive
app.directive('myDirective', function($parse, $interpolate) {
return {
transclude: true,
replace: false, //will replace the directive element with directive template
template: function(element, attrs) {
//this seems hacky statement
var result = $interpolate(attrs.element)(element.parent().scope);
var html = '<'+ result + ' ng-transclude></'+result+'>';
return html;
}
}
})
Demo Plunkr
You can also use ng-transclude :
Create your directive :
<container-directive strike="flag">
<!-- your html here-->
</container-directive>
Then in your directive do something like :
<strike ng-if="strike">
<ng-transclude></ng-transclude>
</strike>
<div ng-if="!strike">
<ng-transclude></ng-transclude>
</div>

Encapsulation of web components and event binding to shadow DOM elements

I started to learn web components in details before to jump in using Polymer.
I was working on a simple example to create a spin button using two <button> and one <input type="text"> elements.
The template is:
<template id="tplSpinButton">
<style type="text/css">
.spin-button > * {
display: inline;
text-align: center;
margin: 0;
float: left;
}
</style>
<div class="spin-button">
<content select=".up-spin-button"></content>
<content select=".display-spin-button"></content>
<content select=".down-spin-button"></content>
</div>
</template>
And the host element is as:
<article>
<spin-button>
<button class="up-spin-button">+</button>
<input class="display-spin-button" type="text" value="0" size="2"/>
<button class="down-spin-button">-</button>
</spin-button>
</article>
And The JS code
var template = document.querySelector('#tplSpinButton');
var host = document.querySelector('article spin-button');
var articleShadowRoot = host.createShadowRoot();
articleShadowRoot.appendChild(document.importNode(template.content,true));
var counterBox = document.querySelector('.display-spin-button');
var upHandler = document.querySelector('.up-spin-button');
var downHandler = document.querySelector('.down-spin-button');
upHandler.addEventListener('click', function(e){
var count = parseInt(counterBox.value);
counterBox.value = count + 1;
}, false);
downHandler.addEventListener('click', function(e){
var count = parseInt(counterBox.value);
counterBox.value = count - 1;
}, false);
document.registerElement('spin-button', {
prototype: Object.create(HTMLElement.prototype)
});
During experiments I came to know that JS/ code does not work as <style> do in shadow DOM being part of <template>
In the above example I am adding insertion points (<content>) and then attaching event listener to distributed elements.
Is there any way to encapsulate the event listener implementation?
Is there any way to move the controls s and elements to shadow dom and then attaching event listener by any way?
Is there any way to encapsulate the event listener implementation?
You could make it part of your element's prototype and construct it inside of a lifecycle callback.
Is there any way to move the controls s and elements to shadow dom and then attaching event listener by any way?
Use getDistributedNodes to select the elements being projected into your content tags.
Here's a jsbin which illustrates both concepts. Hope that helps!

Create div and add style

I have one big div with id="elements" and I load from JSON file new elements objects and I need that for every element create new div inside elements ( elements div should contain lot off smaller divs, for every element one small div ). How to place this small divs inside this big div one behind another ? How to add this small divs a class style ?
In Dojo (since you have the dojo tag):
var div_elements = dojo.byId("elements");
dojo.forEach(json_data.items, function(item) {
dojo.create("div", { "class":"whatever " + item.classNames }, div_elements);
});
Of course, you can put anything as the class for your div. I just provided an example. In the second argument to dojo.create, you pass in a hash containing all the properties you want that div to have.
Create a new DOM element like so:
var childDiv = document.createElement('div');
Then add to the outer div like so:
var insertedElement = div.insertBefore(childDiv, null);
You would then keep creating childDivs as you iterate over your JSON data, and inserting them into the div Node as above.
I think you need something like this:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js" ></script>
<script type="text/javascript">
$(document).ready(function(){
json_data = 'Hey';
$('#elements').append('<div class="in_elements">' + json_data + '</div>');
});
</script>
<div id="elements">
</div>
Test it
There a simple jQuery functions for that:
var box= $("#elements");
// create elements
for (var i=0; i<items.length; i++) {
var t = $("<div class=\"element\" id=\"item_"+i+"\">"+items[i]['text']+"</div>");
box.append(t);
}
That's what you where looking for?