I would like to get notified any any change of the elements style changes (in my case: any change to the elements height).
I have tried to manually register an observer:
observers: [
'heightUpdated(style.height)',
],
This seems not to work (for any style properties).
I have also tried an other observer:
observers: [
'heightUpdated(clientHeight)',
],
This observer never gets called.
The only way i've found to observer changes to style properties of a Polymer element, is to use a MutationObserver
It can easily be set up to listen for any change to any of the .style. properties such as .style.height, but it has a lot of restrictions.
Here is one I set up earlier
Polymer({
is: "my-element",
properties: {
// Saved to the element, so you can inspect it if you want
mutationObserver: {
value: function() {
// uses the function "mutated" defined later"
var observer = new MutationObserver(this.mutated.bind(this));
observer.observe(this, {
// Only fires on attribute changes
attributes: true,
attributeOldValue: true,
// Only fires when the "style" attribute changes
attributeFilter : ['style'],
});
return observer;
},
},
height: String,
},
observers: [
'heightUpdated(height)'
],
mutated: function(mutations) {
// Change this function if you want to listen for something else instead, this is fired on all "style" changes, and you can see the changes inside the "mutations" object it provides
this.set('height', this.style.height);
},
heightUpdated: function(height) {
console.log('height', height);
}
});
It listens whenever this.style.height is set, but not when you resize the element.
Hopefully, the mutation observer will be updated with a css extension to provide better support for listening to css style changes. Until then I don't think this is the best option I can come up with.
Related
I would like to create element that does something for ctrl+tap. How can I assign listener to it?
I have tried to use iron-a11y-keys-behavior https://elements.polymer-project.org/elements/iron-a11y-keys-behavior
and
keyBindings: {
'ctrl:tap': 'doSomething'
},
but it does not work.
Can I use somehow Polymer features like listeners or behaviors for it, or should I code it on my own using VanillaJS?
The only solution I have found is
listeners: {
'tap': '_elementTapped'
},
keyBindings: {
'ctrl+control:keydown': '_updatePressed',
'control:keyup': '_updatePressed'
},
_ctrlPressed: false,
_updatePressed: function(event) {
this._ctrlPressed = event.detail.ctrlKey;
},
_elementTapped: function(){
if(this._ctrlPressed){
this.doSomething();
}
}
For me it looks, like an overhead, as we have to add 2 more methods, 1 property and 1 listeners, comparing to 'ctrl:tap': 'doSomething'.
I would really appreciate cleaner solutions.
Is there a way to add or remove observer not in the moment of element initing?
I can define observer this way:
observers: ['dataChanged(data.*)']
Can i remove this observer later or can I set this observer different way than that?
You can easily add an observer dynamically, either by:
this._addObserverEffect("property", observerFunction);
or
this._addComplexObserverEffect("dataChanged(data.*)");
Removing is harder and Polymer does not provide a function to do this. Although you could search for it in the _propertyEffects array, I wouldn't recommend it. Maybe just check in your observer function whether it should still be active, and return if not.
you maybe can try this way:
configure your data property in the element with notify: true, so you can add a listener to changes with plain js
var element=document.querySelector("#YOUR-ELE-ID");
element.addEventListener("data-changed", function(e) {
//triggered when data property changes
});
https://www.polymer-project.org/1.0/docs/devguide/properties#notify
and to remove the bound listener you can call removeEventListener
https://developer.mozilla.org/de/docs/Web/API/EventTarget/removeEventListener
Example 1 - plain JS :
document.addEventListener('WebComponentsReady', function(e) {
var element=document.querySelector("#YOUR-ELE-ID");
element.addEventListener("data-changed", function(e) {
//triggered when data property changes
});
});
Example 2 - in custom element:
//...element definition...
ready: function() {
var element=document.querySelector("#YOUR-ELE-ID");
element.addEventListener("data-changed", function(e) {
//triggered when data property changes
});
}
I'm working on a site with a scrollable list of canvases for plotting data, and I need to resize the canvases whenever the width of the div they're in changes.
I have it working in most cases, but if I delete a plot such that the scroll bar goes away, it doesn't work. I tried the following, where plotsScroller is the div with the scroll bar and plotsList is what's inside of it:
$scope.isScrollingPlotsList = function() {
return plotsList.offsetHeight > plotsScroller.offsetHeight;
}
$scope.$watch('isScrollingPlotsList()', $scope.$apply);
This code would work except that no $digest happens after the reflow that removes the scroll bar; $digest is called when I delete a plot but I guess the reflow happens later.
Does anyone know how I can detect the reflow without using $timeout?
You can use Mutation Observers to detect changes in the DOM. When a change occur you will be notified and can traverse to see what changed.
An example of usage (source):
// select the target node
var target = document.querySelector('#some-id');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
// later, you can stop observing
observer.disconnect();
Also see the source link for details.
It should be supported in all major browsers incl. IE11. For [IE9, IE11> there exists a polyfill that can be used.
The specification
I have a working Polymer prototype, a code snippet of which is:
Polymer({
myData: [],
observe:{
myData: 'myDataChange'
},
myDataChange: function(val, newVal){ ... }
...
However, under the the attribute hinting section of the developer API, it states that objects and arrays should be initialised in the created lifecycle callback, not on the prototype. So, I changed the code snippet above to:
Polymer({
created: function(){
this.myData = [];
},
observe:{
myData: 'myDataChange'
},
myDataChange: function(val, newVal){ ... }
...
As soon as I make this change, the change watcher function no long invokes.
The myData property of my element instance is being populated by jQuery in an document ready callback. Moving this code into a 'polymer-ready' callback on the containing page solves this issue.
My concern with this is that my pages are going to be littered with polymer-ready events for the initial data population.
I amended my prototype so that the custom element is added to the DOM after a 5 second timeout, after the polymer-ready event was fired. Injecting the DOM like this doesn't fire the polymer ready event again.
Is this the correct/best approach to initialising properties on a Polymer element? I could manually fire an event from my custom element to say its loaded but this seems a bit crude. Any better ideas?
You shouldn't use any custom element before polymer-ready event (unless you're doing it intentionally), I think the best you could do is to replace every ready callback with polymer-ready.
However if you still want to use ready callback you could call myDataChange inside the element's ready callback:
Polymer({
created: function(){
this.myData = [];
},
observe:{
myData: 'myDataChange'
},
ready: function() {
this.myDataChange([], this.myData);
},
myDataChange: function(val, newVal){ ... }
....
How can I access the published properties in the ready : function of my custom element. I can get it if someone passes it in, but I want to check agains the default value. I.e.:
Polymer('post-card', {
publish: {
inline : false
},
ready:function(){
// returns what's passed in
var passedIn = this.getAttribute('inline'),
// returns null when inline is not present on the element
default = this.getAttribute('inline') ;
}
});
I know the default variable is looking for the attribute "inline" on the elemnt. My question is, how to I access the default value I set under the publish object?
Thanks in advanced :)
95% of the time, you should never need getAttribute when using polymer and published properties. Published properties are accessed as properties on this. In the context of your element, this is the element. To check against a default value, you could do something like this:
(function() {
var INLINE_DEFAULT = false;
Polymer('post-card', {
publish: {
inline: INLINE_DEFAULT
},
ready: function() {
console.log(this.inline === INLINE_DEFAULT);
}
}
}();