Is there a way to define a property when its type isn't known?
properties: {
value: {
type: Generic
}
}
What would be the best way to do this?
My problem comes from having a value that may be a String or Number. I'm aware that I can parseInt(), but I would then need to detect if that's necessary. Also, when a property is meant to be a string, that string could be value = '5', which makes conditionally applying parseInt() tedious.
You would need to define a property of type Object.
properties: {
value: Object
}
Given the possible values of your property, parsing is unavoidable but can be simple. For example, you could use a regular expression with String#replace to remove all non-numeric characters from the input, and convert the result to a Number:
Number(value.replace(/[^\d]+/g, ''))
HTMLImports.whenReady(() => {
Polymer({
is: 'x-foo',
properties: {
value: Object,
numberValue: {
computed: '_computeValue(value)'
}
},
_computeValue: function(value) {
// If the value is a string, remove all non-numeric
// characters and convert the result to a number.
return typeof value === 'string'
? Number(value.replace(/[^\d]+/g, ''))
: value;
}
});
});
<head>
<base href="https://polygit.org/polymer+1.7.1/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link rel="import" href="polymer/polymer.html">
</head>
<body>
<x-foo value="100"></x-foo>
<x-foo value="value = '5'"></x-foo>
<x-foo value="foo 2 bar 4 baz 6 qux"></x-foo>
<dom-module id="x-foo">
<template>
<div>[[value]] ==> [[numberValue]]</div>
</template>
</dom-module>
</body>
regex101 explanation of pattern:
Related
I want to check if the user is logged in on element creation and eventually redirect him if the user is not. The problem is that the domHost.signedIn property is false even though the user is signedIn. If I check the property later(for example when I call a function with button tap) the property is true as it should be.
Here is the code:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/paper-button/paper-button.html">
<dom-module id="settings-view">
<template>
<style>
</style>
TODO: user settings
<paper-button on-tap="debugFunction">button</paper-button>
</template>
<script>
Polymer({
is: 'settings-view',
ready: function () {
console.log(this.domHost.signedIn); // false
console.log(this.domHost.user); // null
},
debugFunction: function () {
console.log(this.domHost.signedIn); // true
console.log(this.domHost.user); // user object
}
});
</script>
</dom-module>
What is the best way to check if the user is signedIn in child element? Would setting the signedIn value to iron-meta element be a better approach?
Thanks, Jan
You're better off declaring properties with observers on them. Observers will execute the function as soon as the property's value is something other than undefined. So your code will look like this:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/paper-button/paper-button.html">
<dom-module id="settings-view">
<template>
<style>
</style>
TODO: user settings
</template>
<script>
Polymer({
is: 'settings-view',
properties: {
signedIn: {
type: Boolean,
observer: '_signedInChanged'
},
user: {
type: Object,
observer: '_userChanged'
},
},
_signedInChanged: function (newSignedInValue) {
console.log(newSignedInValue); // true
console.log(this.signedIn); // true
},
_userChanged: function (newUserValue) {
console.log(newUserValue); // user object
console.log(this.user); // user object
}
});
</script>
</dom-module>
Then when you update those signedIn and user values through JavaScript or data binding, the observers will call the associated functions.
Whats the difference between defining a computed property and using it like {{prop}}
prop: {
type: String,
computed: 'some(prop1)'
}
vs a function binding like
{{some(prop1)}}
The property is, as the name implies, also a property of the node object. It can notify outside listeners or reflect to attribute.
Function binding is only used to that. You can call it from the outside but it should have no effect - assuming that the function has no side-effects which is shouldn't.
The most important difference however, is that compute function will evaluate for each binding usage. Computed property will evaluate only once when a dependency changes. See below what happens in the console whenever you click INCREMENT.
Polymer({
is: 'my-elem',
properties: {
i: {
type: Number,
value: 0
},
c: {
computed: 'compute(i)'
}
},
inc: function() {
console.clear();
this.i += 1;
},
compute: function(i) {
console.log('computing property binding');
return i * 2;
},
f: function(i) {
console.log('computing function binding');
return i * 2;
}
});
<!DOCTYPE html>
<html>
<head>
<base href="https://polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.min.js"></script>
<link href="polymer/polymer.html" rel="import"/>
<link href="paper-button/paper-button.html" rel="import" />
</head>
<body>
<my-elem></my-elem>
<dom-module id="my-elem">
<template>
<div>Computed: [[c]]</div>
<div>Computed: [[c]]</div>
<div>Computed: [[c]]</div>
<div>Computed: [[c]]</div>
<div>Function: [[f(i)]]</div>
<div>Function: [[f(i)]]</div>
<div>Function: [[f(i)]]</div>
<div>Function: [[f(i)]]</div>
<paper-button on-tap="inc">Increment</paper-button>
</template>
</dom-module>
</body>
</html>
I'm trying to reproduce the Social Security Number example provided by the polymer demo here. How can I set a default/initial value for paper-input-container with its ssn-input component inside? The running version is here.
I tried to add the attribute value to both paper-input-container and ssn-input, but it does not display as initial default value.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Polymer Element Test Case</title>
<base href="//polygit.org/components/">
<script src="webcomponentsjs/webcomponents-lite.js"></script>
<link rel="import" href="polymer/polymer.html">
<link rel="import" href="paper-input/paper-input-container.html">
<link rel="import" href="paper-input/paper-input-error.html">
<link rel="import" href="paper-input/demo/ssn-input.html">
</head>
<body>
<paper-input-container always-float-label auto-validate attr-for-value="value">
<label>Social Security Number</label>
<ssn-input class="paper-input-input"></ssn-input>
<paper-input-error>SSN invalid!</paper-input-error>
</paper-input-container>
</body>
</html>
I checked the original implementation of the <ssn-input>, and it seems to me that there is no code to split the provided value in its three substrings and provide it to the three paper-input. Instead when the user types something within the paper-inputs, each string becomes a _ssnX and a computeValue function links them together storing the result in the value property. Here the piece of code from ssn-input.html:
properties: {
value: { notify: true, type: String },
_ssn1: { type: String },
_ssn2: { type: String },
_ssn3: { type: String },
validator: { type: String, value: 'ssn-validator' }
},
observers: [
'_computeValue(_ssn1,_ssn2,_ssn3)'
],
_computeValue: function(ssn1, ssn2, ssn3) {
this.value = ssn1.trim() + '-' + ssn2.trim() + '-' + ssn3.trim();
}
ssn-input is the component that reads the content typed by the user and puts everything in the value property. Hence, the first thing is to initialise the value attribute of this element, as follows:
<paper-input-container always-float-label auto-validate>
<label>Social Security Number</label>
<ssn-input class="paper-input-input" value="102-12-1233"></ssn-input>
<paper-input-error>SSN invalid!</paper-input-error>
</paper-input-container>
The attribute value initialises the relevant property in the ssn-input. ssn-input has three internal input element, which show and take the user input. Therefore, the initial value must be split on the "-" character. The best place to do it is in the value property observer. So, the modified ssn-input element code is the following:
Polymer({
is: 'ssn-input',
behaviors: [
Polymer.IronValidatableBehavior
],
properties: {
value: { notify: true, type: String, observer: '_handleValueChanged' },
_ssn1: { type: String },
_ssn2: { type: String },
_ssn3: { type: String },
validator: { type: String, value: 'ssn-validator' }
},
_handleValueChanged: function(value) {
var arr = value.split("-");
this._ssn1 = arr[0];
this._ssn2 = arr[1];
this._ssn3 = arr[2];
},
observers: [
'_computeValue(_ssn1,_ssn2,_ssn3)'
],
_computeValue: function(ssn1, ssn2, ssn3) {
this.value = ssn1.trim() + '-' + ssn2.trim() + '-' + ssn3.trim();
}
});
Here, the jsbin running example.
I have the following polymer element:
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/paper-input/paper-input.html">
<link rel="import" href="../bower_components/iron-input/iron-input.html">
<dom-module id="number-input">
<template>
<paper-input type="number" label="amount" value="{{amount}}"></paper-input>
</template>
<script>
Polymer({
is: "number-input",
properties: {
amount: {
type: Number,
}
}
});
</script>
</dom-module>
The "amount" property is always a string. Even though I define the property as a "Number". Am I missing something or is this normal behaviour ?
Sander.
You need use an ordinary input field directly and specify is="iron-input" and type="number". The type attribute is what ultimately gets you the number field you are looking for. Other number input attributes such "min" should work as well.
You add a label, validator, and error message, around that as described by the polymer paper-input-container documentation.
Try setting a default value to 0 and use pattern for input validation:
<dom-module id="number-input">
<template>
<paper-input type="number" label="amount" value="{{amount}}" pattern="\d+\.?\d*"></paper-input>
</template>
<script>
Polymer({
is: "number-input",
properties: {
amount: {
type: Number,
value:0
}
}
});
</script>
I'm working on a reddit client using polymer to check out web compoments technologies. I started with the 0.5 version and got back on this project recently. That when I found out that polymer had the 1.0 released so I started over (as it wasn't that advanced anyway).
I have a service that use the iron-ajax to request reddit api and look for the posts. Here is the code :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<dom-module id="reddit-list-service">
<template>
<iron-ajax
url='https://www.reddit.com/new.json'
handle-as='json'
debounce-duration="300"
on-response='handleResponse'
debounce-duration="300"
auto>
</iron-ajax>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'reddit-list-service',
properties: {
modhash: {
type: String,
value: function() {
return '';
}
},
posts: {
type: Array,
value: function () {
return [];
}
},
after: {
type: String,
value: function () {
return '';
}
}
},
// Update object properties from the ajax call response
handleResponse: function (resp) {
this.properties.modash = resp.detail.response.data.modhash;
this.properties.posts = resp.detail.response.data.children;
this.properties.after = resp.detail.response.data.after;
this.post = this.properties.posts; // just to try
console.log(this.properties.posts);
}
});
})();
</script>
My log shows me that I get posts from the API and that's great!
Here's the issue when I want to use this service to make a list out of the posts array I can't figure out how to get them into my list compoment which is below :
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../reddit-list-service/reddit-list-service.html">
<dom-module id="reddit-post-list">
<template>
<reddit-list-service posts="{{posts}}">
</reddit-list-service>
<template is="dom-repeat" id="post-list" posts="{{posts}}">
<p>{{post.author}}</p>
<template>
</template>
</dom-module>
<script>
(function () {
Polymer({
is: 'reddit-post-list',
properties: {
},
});
})();
</script>
I've tried several think I saw in the documentation but I can't figure out what's wrong the author property doesn't show up.
Any clue?
You have a few things that aren't quite right here. In reddit-post-list you are not using the dom-repeat template correctly. See below:
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="reddit-list-service.html">
<dom-module id="reddit-post-list">
<template>
<reddit-list-service posts="{{posts}}"></reddit-list-service>
<template is="dom-repeat" items="[[posts]]">
<p>{{item.data.author}}</p>
</template>
</template>
</dom-module>
<script>
Polymer({
is: "reddit-post-list"
});
</script>
You need an attribute called items which is the array the dom-repeat will iterate over. Inside the iteration you need to refer to item as this is the array item for the current iteration. Here are the docs.
For you reddit-list-service, you need to set the the reflectToAttribute and notify attributes to true on your posts property. This means that any changes to this property are reflected back on the posts attribute on the reddit-list-service element. See here for more information.
<link rel="import" href="bower_components/polymer/polymer.html">
<link rel="import" href="bower_components/iron-ajax/iron-ajax.html">
<dom-module id="reddit-list-service">
<template>
<iron-ajax auto url="https://www.reddit.com/new.json" handle-as="json" on-response="handleResponse"></iron-ajax>
</template>
</dom-module>
<script>
Polymer({
is: "reddit-list-service",
properties: {
modhash: {
type: String,
value: ""
},
posts: {
type: Array,
value: function () {
return [];
},
reflectToAttribute: true, // note these two new attributes
notify: true
},
after: {
type: String,
value: ""
}
},
// Update object properties from the ajax call response
handleResponse: function (resp) {
this.modash = resp.detail.response.data.modhash;
this.posts = resp.detail.response.data.children;
this.after = resp.detail.response.data.after;
}
});
</script>
I have also tidied up the following things:
Removed the immediately called function wrapper from the <script> tags as these are not needed.
When referring to properties in your element you only need to use this.PROPERTYNAME rather than this.properties.PROPERTYNAME.
Having looked at the JSON returned, it appears that the author property is on the another property called data.
When you declare a value for a property in your element, you only need to have a function that returns a value if the property type is an Object or Array.