Polymer 1.0: How to nest functions? - polymer

I want to call an observer function '_propChanged' that, in turn, calls two others _foo() and _bar() . How would I do that?
Code
{
is: 'x-el',
properties: {
prop: {
type: String,
notify: true,
observer: '_propChanged'
}
},
_propChanged: function() {
_foo(); // This doesn't work
_bar(); // This doesn't work
},
_foo: function() {
// Do stuff
},
_bar: function() {
// Do stuff
}
}

Simply prefix your function calls with this for the names to resolve properly.
_observePropChanges: function () {
this._foo();
this._bar();
}

Related

I try to use computed to watch navigator.onLine but not work until I refresh the web?

I try to use computed to watch navigator.onLine but not work until I refresh the web?
<template><div>{{isOnline}}</div></template>
...
<scirpt>
...
computed: {
isOnline() {
return navigator.onLine;
},
},
...
</script>
Browsers api are not reactive. So whenever their value changes, Vue doesn't know about it.
To listen to navigator.onLine changes, you have to use a native event fired by the browser (see docs):
data () {
return {
online: false,
}
},
methods: {
onOffline () { this.online = false },
onOnline () { this.online = true },
created() {
window.addEventListener('offline', this.onOffline)
window.addEventListener('online', this.onOnline)
},
destroyed() {
window.removeEventListener('offline', this.onOffline)
window.removeEventListener('online', this.onOnline)
}
}
Note: be careful with SSR, window doesn't exist on server.
Like #Kaocash said browsers api are not reactive, so a watcher won't work
Original answer :
Well, computed property will be updated when data changes on your component,
what you need is a watcher :
<template><div>{{isOnline}}</div></template>
...
<script>
...
data() {
return {
isOnline: true
}
},
watch: {
'navigator.onLine'(val) {
this.isOnline = val
},
},
...
</script>

How do I bind Polymer property in setTimeout?

I have a Polymer property defined as:
properties: {
delay: {
type: Timeranges,
value: '5000'
}
}
And I use this property as a timeout like this:
setTimeout(function() {
request = ajax(request, custParams, inputValue.trim(), input, result, component.subType, component.queryParams);
}, "{{delay}}");
But this is not working. If I specify a literal number as a function argument instead of "{{delay}}", it works fine. How do I bind delay here?
The property type should be Number (not Timeranges).
Polymer's data binding syntax can only be used in HTML (not JavaScript). Your current code passes a literal string to setTimeout() instead of the numeric value of delay.
Assuming setTimeout() is called from your Polymer object definition, you would use this.delay like this:
Polymer({
properties: {
delay: {
type: Number,
value: 5000
}
},
foo: function() {
setTimeout(function() {...}, this.delay);
}
});
If you need setTimeout() to be called whenever delay changes, you would use an observer like this:
Polymer({
properties: {
delay: {
type: Number,
value: 5000,
observer: '_delayChanged'
}
},
_delayChanged: function(newDelay) {
setTimeout(function() {...}, newDelay);
}
// ...
});

Extending Custom Behavior

In polymer 1.0, I created custom behavior pageBehavior for two custom elements. On one of the elements, I would like to extend the behavior. After reading the docs, it seems that I would need to create another behavior and place it in array. I don't want to create another behavior because only this element will be using the extra code.
With the element and the extended behavior needed, how can I add hidePrintButton and to the properties and overwrite function fullDisplayeMode?
custom element:
<script>
Polymer({
is: "resume-page",
properties: {
hidePrintButton: {
type: Boolean,
reflectToAttribute: true,
value: true
}
},
behaviors: [pageBehavior],
fullDisplayMode: function() {
this.show = true;
this.hidePrintButton = false;
this._toggleStyles();
this.nextElementSibling.show = true;
}
});
</script>
the page behavior:
<script>
pageBehavior = {
properties: {
hideRipple: {
type: Boolean,
value: false
},
fullDisplay: {
type: Boolean,
value: false
},
show: {
type: Boolean,
reflectToAttribute: true,
value: true
}
},
_mediaQuery: function(section) {
if (window.matchMedia( "(min-width: 1200px)" )) {
section.style.width = "90%";
} else {
section.style.width ="90%";
}
},
_toggleWidth: function(section, fullDisplay) {
if (fullDisplay) {
section.style.width = "100%";
} else {
this._mediaQuery(section);
}
},
_toggleHover: function(section, fullDisplay) {
if (fullDisplay) {
section.classList.remove('enabled-hover');
} else {
section.classList.add('enabled-hover');
}
},
_toggleRipple: function(fullDisplay) {
//This is necessary because if page ripple
//is hidden to quick the animation doesn't finish
if (fullDisplay) {
setTimeout(function() {
this.hideRipple = true;
}.bind(this), 700);
} else {
this.hideRipple = false;
}
},
_toggleStyles: function(fullDisplay) {
var section = this.firstElementChild;
this._toggleRipple(fullDisplay);
this._toggleWidth(section, fullDisplay);
this._toggleHover(section, fullDisplay);
},
fullDisplayMode: function() {
this._toggleStyles(true);
this.show = true;
this.nextElementSibling.show = true;
},
homeMode: function() {
this._toggleStyles(false);
this.show = true;
this.nextElementSibling.show = false;
},
disappearMode: function() {
this.show = false;
this.nextElementSibling.show = false;
}
}
</script>
A behavior method cannot be extended. It can only be overwritten. However you could still abstract the shared logic in the behavior and have some empty methods on the behavior for customization purposes.
E.g
//In your behavior
fullDisplayMode: function() {
this.show = true;
this._toggleStyles();
this.nextElementSibling.show = true;
this.prepareFullDisplayMode();
},
prepareFullDisplayMode:function(){
//Empty inside behavior
//Elements could opt to implement it with additional logic
}
Using this pattern, one of your custom elements could add additional logic by implementing the 'prepareFullDisplayMode' while the other would not need to.
I don't know since when we can do this, but we CAN extend behaviors:
https://www.polymer-project.org/1.0/docs/devguide/behaviors#extending
I'm going to use as an example the Polymer.AppLocalizeBehavior from app-localize-behavior to set the default language.
1) Namespace your behavior, so they don't collide with others:
var MyNamespace = MyNamespace|| {};
2) Write the behavior's implementation:
MyNamespace.LocalizeImpl = {
ready() {
},
attached: function() {
this.loadResources(this.resolveUrl('../../../locales.json'));
},
properties: {
language : {
value : "en"
}
},
};
3) Add the implementation to the new behavior in an array.
MyNamespace.Localize = [Polymer.AppLocalizeBehavior, MyNamespaceLocalize.Impl]
All together:
var MyNamespace = MyNamespace || {};
MyNamespace.Localize = {
ready() {
},
attached: function() {
this.loadResources(this.resolveUrl('../../../locales.json'));
},
properties: {
language : {
value : "en"
}
},
};
MyNamespace.LocalizeBehavior = [Polymer.AppLocalizeBehavior, MyNamespace.Localize]
Then, in your element, include it like this:
<link rel="import" href="../../../../bower_components/polymer/polymer.html">
<link rel="import" href="../path-to-custom-behavior/mynamespace-localize-behavior/mynamespace-localize-behavior.html">
<dom-module id="my-element">
<style is="custom-style"></style>
<template is="dom-bind">
<template is="dom-if" if="{{query}}">
<h1> {{localize("EmailActivationSuccessful")}}</h1>
</template>
<template is="dom-if" if="{{!query}}">
<h1> {{localize("EmailCodeExpired")}}</h1>
</template>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'my-element',
behaviors: [MyNamespace.LocalizeBehavior],
});
})();
</script>
Now, as you can see I've only included the MyNamespace.LocalizeBehavior and started using all the methods and functions from "Polymer.AppLocalizeBehavior"
This is a great way for setting the default language and only handling the language logic in a single element.
Explanation and notes:
All the properties, methods, functions that match the previous
behavior are overwritten. In this case, I overwrote the
"language" property from "Polymer.AppLocalizeBehavior".
Remember include the .html file where the old behavior is located only where you are extending the behavior. Afterwards you only include your custom behavior wherever and whenever you want.
In point #3, the array works like this: the first element is the behavior to extend/overwrite, and the second one is your implementation, or the extended behavior.

Two observers Polymer

I have Two observers and in each observer I change the value of the property of the other observer. In this case I dont want that the other observer will execute.
How Can I change that The observer will execute only in change of the property from outside?
Thanks
Your best option is to use a local variable to stop the update.
Polymer({
is: 'my-element',
properties: {
myProperty: {
type: String,
observer: '_myObserverA'
}
},
observers: [
'_myObserverB(myProperty)'
],
_myObserverA(newValue) {
if(!this._localUpdate) {
//do stuff here
} else {
this._localUpdate = false;
}
},
_myObserverB(newValue) {
this._localUpdate = true;
//do stuff here
}
})
You must use an observer like that:
Polymer({
is: 'x-custom',
properties: {
preload: Boolean,
src: String,
size: String
},
observers: [
'updateImage(preload, src, size)'
],
updateImage: function(preload, src, size) {
// ... do work using dependent values
}
});
More info in: https://www.polymer-project.org/1.0/docs/devguide/properties.html#multi-property-observers

Polymer two way data binding

I have defined a polymer element
Polymer({
is: 'disco-ccontrol',
properties: {
midiValue: {
type: Number,
value: 0,
observer: '_valueChanged',
notify: true
},
channel: {
type: Number,
value: 0
},
channelNumber: {
type: Number,
value: 0
},
ref: {
type: Object,
computed: '_computeRef(channel, channelNumber)'
}
},
_computeRef: function(channel, channelNumber) {
var ref = new Firebase("https://incandescent-inferno-8405.firebaseio.com/user/"+channel+"/"+channelNumber);
ref.on("value", function(data) {
this.midiValue = data.val().value;
});
return ref;
},
_valueChanged: function() {
var message = { value: this.midiValue, channel: this.channel, channelNumber: this.channelNumber };
if (this.ref) {
this.ref.set(message);
}
}
});
I use this element in another element (parent element)
<disco-ccontrol midi-value="{{value}}" channel="{{channel}}" cn="{{channelNumber}}"></disco-ccontrol>
When I adapt the value property in the parent it propagates to the child. When I change the value property in the child (i.e in disco-ccontrol) it doesn't propagate up. What am I doing wrong to establish a two way binding?
In this function
ref.on("value", function(data) {
this.midiValue = data.val().value;
});
the this keyword is is not bound to the Polymer element. Thus your are not setting the midiValue on the correct object. You can bind this to the Polymer element using bind.
ref.on("value", function(data) {
this.midiValue = data.val().value;
}.bind(this);