Create Anchor Element in polymer - html

Is it possible to create an element that will be used as an anchor in polymer. So for example
<template repeat="{{ content in contentitems }}">
<div id="{{ content.id }}">{{content.stuff}}</div>
</template>
Would it be possible to create a hyperlink to the content#id anchor like http://example.com/#someid
Alternatively, we can query that element with querySelector like the below and then scroll it into view if necessary with JavaScript. I'd rather not have to use a JS router however for anchor hyperlinking?
scrollIntoViewFunc(document.querySelector("html /deep/ #someid"))
Here's an actual URL I want to get working: http://megawac.github.io/status-whiskey/#status-408

The Web Component gods (aka Blink engineers) have decided that anchors inside of shadow-roots will not automatically scroll into view like they do in the main document. For this reason, I believe you will have to do something like you showed to handle this in JavaScript.
After brief searching, I couldn't find a reference to this question in the spec, it really needs to be spelled out somewhere.
If you come up with general solution, elementize it and share it back to the community. =P

Let's say you have a simple-element with some child elements with ids as anchors:
<simple-element>
<div id="anchor1"></div>
<div id="anchor2"></div>
</simple-element>
Then you can make a function to scrollIntoView when the hash changes:
window.addEventListener('hashchange', function() {
let targetElement = document.querySelector('simple-element').$[location.hash.substr(1)];
if(targetElement) {
targetElement.scrollIntoView();
}
}, false);

Related

How focus paritcular html component in page with Angular?

I want to scroll down to page. When I clicked anchor , I want to stay on same page but focus on partivular element or page down. How can I achieve this?
Create an element reference in your component like this
#ViewChild('whatever') myElement: ElementRef;
Bind it to the html element like this
<input #whatever />
Then you should be able to call focus to it like this.
function focusWhatever() {
this.myElement.nativeElement.focus();
}
Element References are a powerful tool in Angular for accessing DOM elements directly, particularly for stuff like this. Read more about them here.
Read more about the el.focus() DOM API method here.

How to access more than 2 DOM elements "The AngularJS way"?

I'm starting to learn angularJS better, and I've noticed that AngularJS tries to make strong emphasis on separating the view from the controller and encapsulation. One example of this is people telling me DOM manipulation should go in directives. I kinda got the hang of it now, and how using link functions that inject the current element allow for great behavior functionality, but this doesn't explain a problem I always encounter.
Example:
I have a sidebar I want to open by clicking a button. There is no way to do this in button's directive link function without using a hard-coded javascript/jquery selector to grab the sidebar, something I've seen very frowned upon in angularJS (hard-coding dom selectors) since it breaks separation of concerns. I guess one way of getting around this is making each element I wish to manipulate an attribute directive and on it's link function, saving a reference it's element property into a dom-factory so that whenever a directive needs to access an element other than itself, it can call the dom-factory which returns the element, even if it knows nothing where it came from. But is this the "Angular way"?
I say this because in my current project I'm using hard-coded selectors which are already a pain to mantain because I'm constantly changing my css. There must be a better way to access multiple DOM elements. Any ideas?
There are a number of ways to approach this.
One approach, is to create a create a sidebar directive that responds to "well-defined" broadcasted messages to open/close the sidebar.
.directive("sidebar", function(){
return {
templateUrl: "sidebar.template.html",
link: function(scope, element){
scope.$root.$on("openSidebar", function(){
// whatever you do to actually show the sidebar DOM content
// e.x. element.show();
});
}
}
});
Then, a button could invoke a function in some controller to open a sidebar:
$scope.openSidebar = function(){
$scope.$root.$emit("openSidebar");
}
Another approach is to use a $sidebar service - this is somewhat similar to how $modal works in angularui-bootstrap, but could be more simplified.
Well, if you have a directive on a button and the element you need is outside the directive, you could pass the class of the element you need to toggle as an attribute
<button my-directive data-toggle-class="sidebar">open</button>
Then in your directive
App.directive('myDirective', function() {
return {
restrict: 'A',
link: function(scope, element, attrs) {
angular.element('.' + attrs.toggleClass).toggleClass('active');
}
};
}
You won't always have the link element argument match up with what you need to manipulate unfortunately. There are many "angular ways" to solve this though.
You could even do something like:
<div ng-init="isOpen = false" class="sidebar" ng-class="{'active': isOpen}" ng-click="isOpen = !isOpen">
...
</div>
The best way for directive to communicate with each other is through events. It also keeps with the separation of concerns. Your button could $broadcast on the $rootScope so that all scopes hear it. You would emit and event such as sidebar.open. Then the sidebar directive would listen for that event and act upon it.

google map wrong size in polymer element

I am using the google-map element and I read about the .resize() trick to fix the size of a google-map element.
However I could not get it to work. Moreless, it doesn't make sense to me.
In my application:
only the first page which displays maps renders properly. Another page which render a map will not render properly.
if the first page rendered doesn't contain a map, if I go to another page with a map, the map renders properly.
That is why I do not understand how this can be related to the core-animated pages.
Live demo:
http://nicolasrannou.github.io/webapp-core/components/webapp-core/demo.html#welcome/home
All the "Contact" pages contain maps.
The maps are created after pulling data from a google doc, using a template:
<template repeat="{{row, rowIndex in rows}}">
<!-- location -->
<template if="{{ row.gsx$type.$t === 'location'}}">
<google-map latitude="{{+ row.gsx$latitude.$t}}" longitude="{{+ row.gsx$longitude.$t}}" showCenterMarker zoom="15">
</google-map>
</template>
</template>
Those "google-map" element are pretty far in the shadow dom and encapsulated in templates.
I tried to access then in the core-animated-prepare event without success:
transitionPrepare : function(){
window.console.log(document.querySelectorAll('google-map'));
}
Is there a good way to access an element inside a template, inside a shadow dom?
Thanks
On accessing inside a shadow dom, here is one citation from the docs:
...If the element is in another shadow tree deep within another element, you can't traverse into it easily. You can use .shadowRoot if you really need to poke around:
element.shadowRoot.querySelector('x-other-element')
.shadowRoot.querySelector('#something');
On maps that do not resize properly, I'll first look at the timing: .resize() should be done AFTER the animation is completed.

How to dynamically change CSS in polymer?

I have a polymer element which is a stack of images that need to expand and reveal each of the images upon hovering on the stack. It's supposed to look like this if left untouched:
And upon hovering, the stack would expand vertically.
The code for the element is (do-profile-pic is another element that puts each image down):
<polymer-element name="do-profile-pic-stack" attributes="images">
<template>
<style>...</style>
<div class="stack-container"
on-mouseover='{{onHovered}}'
on-mouseout='{{onUnhovered}}'>
<template repeat="{{picture, i in images}}">
<div class="stack-img-container"
/* the stack is angled so bit of every image is visible */
style="top: {{5 * i}}; z-index: {{10 - i}}">
<do-profile-pic imgurl="{{picture.url}}" showtime="false">
</do-profile-pic>
</div>
</template>
</div>
</template>
<script>
Polymer('do-profile-pic-stack', {
images: [],
...
});
</script>
</polymer-element>
Now i've looked around, and there seem to be two ways to do this. One is to use the polymer selectors. The other is to use user event methods. I'm not using any of these methods to achieve the default angled layout (comment above). To expand the stack vertically, i'll have to play with positioning and layout, but i cannot seem to think of a good way to implement the this.
The polymer selectors appear complicated.
Programmatically, it looks like playing with HTMLElement will be required. I can do that, but angular-js handles this much better. So is there a better way to go about this that i might be mssing? Thanks.
If you want to change appearance on mouse-over I think you can go with a pure CSS solution with two sets of styles one normal and one for mouse-over.
stack-img-container {
// normal style here
}
stack-img-container:hover {
// mouse-over style
// overrides and extends normal style
}
and put it in your style tag (instead of the dots ...).
Use Element.classList to change class dynamically.
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/platform.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/polymer/0.3.3/polymer.js"></script>
<my-app></my-app>
<polymer-element name='my-app'>
<template>
<style>
.hovered {
background-color:red;
}
</style>
<span on-mouseenter='{{onHover}}'
on-mouseleave='{{onUnhover}}'>
Mouse over me, please.
</span>
</template>
<script>
Polymer('my-app', {
onHover: function(e) {
e.srcElement.classList.add('hovered');
},
onUnhover: function(e) {
e.srcElement.classList.remove('hovered');
}
})
</script>
</polymer-element>
Notes:
Event.srcElement is not standard will and not work for every user.
The standard way to reference an element within an Polymer element is the Polymer.Base $$(slctr):
this.$$('span').classList.add('hovered');
It works in my project, but not in this snippet.
The code above was copied and edited from Peter Burns' code snippet from How can I handle a hover the Polymer way without external libraries?
Curly braces around onHover in the event handler shouldn't be necessary, however, they don't work otherwise in this snippet. Not sure if it's because of the version of Polymer used. I tried to link to the latest but it failed.
Here is a demo of changing class dynamically with plain javascript.

Is it possible to inject HTML into a polymer component via an attribute?

I'm using one of the core polymer components that basically has:
<polymer-element attributes="label">
<div>{{label}}</div>
as part of the source. I'd like to inject some HTML into this so that it ultimately renders as:
<div>Item <small>Description</small></div>
Is there any way to do this without copying the entire component (which is basically impossible considering the dependency chain)?
Polymer doesn't allow setting HTML inside {{}} expressions because it's a known XSS outlet. However, there are ways around it (1, 2).
I'm not sure there's a great way around this issue but I found something that works. You want to extend the element but also need to modify its shadow dom because of the .innerHTML limitation. Taking paper-button as an example, it has an internal {{label}}. You could extend the element, drill into its shadow dom, and set .innerHTML of the container where {{label}} is set. React to label changing (labelChanged) and call this.super():
<polymer-element name="x-el" extends="paper-button">
<template>
<shadow></shadow>
</template>
<script>
Polymer('x-el', {
labelChanged: function() {
// When label changes, find where it's set in paper-button
// and set the container's .innerHTML.
this.$.content.querySelector('span').innerHTML = this.label;
// call paper-button's labelChanged().
this.super();
}
});
</script>
</polymer-element>
Demo: http://jsbin.com/ripufoqu/1/edit
Problem is that it's brittle and requires you to know the internals of the element you're extending.