I use Polymer Starter Kit 1.0.3.
My application have different types of pages. Some of them should render data received from request to backend API.
But when the application starts, all requests from all pages immediately start.
The question is how to do the right job with the backend?
I had the same problem as you, the solution I found was to use methods in my elements to call a function that it request information to the backend when I really need it. Sorry for my English.
There is iron-ajax,this is the mainly element polymer work with backend.
Here is an example in my project
<iron-ajax auto
verbose="true"
url="[[apiCategories]]"
handle-as="json"
on-response="_onCategoriesLoadComplete"></iron-ajax>
<iron-ajax auto
id="ajaxWeapons"
verbose="true"
url="[[apiWeapons]]"
params="[[apiWeaponsParams]]"
debounce-duration="300"
handle-as="json"
last-response="{{page}}"></iron-ajax>
<iron-ajax auto
verbose="true"
url="{{apiCountries}}"
handle-as="json"
last-response="{{countries}}"></iron-ajax>
This is very useful, when param change or id change, the whole page's data will reload, the simplest way to work with backend.
Maybe you don't want to auto request, example in my code
<iron-ajax id="ajaxUserRegister"
verbose="true"
url="{{apiUsers}}"
method="PUT"
on-response="_onUserRegisterComplete"
content-type="application/json"
handle-as="json"></iron-ajax>
<iron-ajax id="ajaxUserLogin"
verbose="true"
url="{{apiUsers}}"
method="POST"
on-response="_onUserLoginComplete"
content-type="application/json"
handle-as="json"></iron-ajax>
<iron-ajax id="ajaxUserGet"
verbose="true"
url="{{apiUsers}}"
method="GET"
headers="[[user.headers]]"
on-response="_onUserGetComplete"
content-type="application/json"
handle-as="json"></iron-ajax>
You can also use backend source when you needed
com._userLogin = function () {
var {inputAccount:account,inputPassword:pwd} = this;
if (!(account && account.length >= 4)) {
this.showToast('Wrong account');
return
}
if (!(pwd && pwd.length >= 4)) {
this.showToast('Wrong password');
return
}
this.$.ajaxUserLogin.body = {name: account, password: pwd};
this.$.ajaxUserLogin.generateRequest();
};
com._onUserLoginComplete = function (e, ajax) {
if (ajax.response.error) {
this.showToast('Login faield:' + ajax.response.error);
return
}
this.user = ajax.response;
};
In conclusion,define you backend resource as a iron-ajax, this is how polymer work with backend.
Related
component html file part where request is being send
<paper-button class="indigo" style="margin-top:1em; width:15%"
on-tap="sendLoginRequest">Login</paper-button>
<iron-ajax id="loginAjaxRequest" handle-as="json" on-
response="loginResponse" on-error="showToast"></iron-ajax>
sendloginRequest function part where request is fired
sendLoginRequest() {
//some code
this.$.loginAjaxRequest.url = "http://localhost:8080/api/login"
this.$.loginAjaxRequest.body = JSON.stringify(loginJson)
this.$.loginAjaxRequest.method = "POST"
this.$.loginAjaxRequest.generateRequest()
}
how to write test cases for it
I am new to polymer and I am trying to read JSON data in a custom-element and display it in other element.
This is my JSON data:
jsonData.json
[
{
"name":"Ladies+Chrome+T-Shirt",
"title":"Ladies Chrome T-Shirt"
},
{
"name":"Ladies+Google+New+York+T-Shirt",
"title":"Ladies Google New York T-Shirt"
}
]
This is my shop-app.html file where I try to read data from JSON file (I am not sure if this is correct or not as I am not able to test it):
<dom-module id="shop-category-data">
<script>
(function(){
class ShopCategoryData extends Polymer.Element {
static get is() { return 'shop-category-data'; }
static get properties() { return {
data: {
type: Object,
computed: '_computeData()',
notify: true
}
}}
_computeData() {
this._getResource( {
url: 'data/jsonData.json',
onLoad(e){
this.set('data.items', JSON.parse(e.target.responseText));
}
})
}
_getResource(rq) {
let xhr = new XMLHttpRequest();
xhr.addEventListener('load', rq.onLoad.bind(this));
xhr.open('GET', rq.url);xhr.send();
}
}
customElements.define(ShopCategoryData.is, ShopCategoryData);
})();
</script>
</dom-module>
This is the element where I want to display the data I read from the JSON file:
<dom-module id="shop-app">
<template>
<app-location route="{{route}}"></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}">
</app-route>
<shop-category-data data="{{data}}"></shop-category-data>
<template>
<div> Employee list: </div>
<template is="dom-repeat" items="{{data}}">
<div>First name: <span>{{item.name}}</span></div>
<div>Last name: <span>{{item.title}}</span></div>
</template>
</template>
</template>
<script>
class ShopApp extends Polymer.Element {
static get is() { return 'shop-app'; }
}
customElements.define(ShopApp.is, ShopApp);
</script>
</dom-module>
The line <shop-category-data data="{{data}}"></shop-category-data> should give me the data, which I then try to display using dom-repeat. But nothing is being displayed. So, I think there is some mistake in my reading the JSON data.
Edit:
The JSON is read correctly, it is just not getting reflected back in my:
<shop-category-data data="{{data}}"></shop-category-data>
Computed properties is not returning a value. If you want to define data as a computed property you must return a value from the computed property function _computeData(). But in your case you are using asynchronous XMLHttpRequest. So, if you return a value after calling this._getResource... you need to make it synchronous (which no one recommends).
Plnkr for synchronous method: http://plnkr.co/edit/jdSRMR?p=preview
Another way is calling the method inside ready(). This is asynchronous.
Plnkr: http://plnkr.co/edit/pj4dgl?p=preview
It's not getting reflected back because the json is assigned to data.items, rather than to data itself.
this.set('data', JSON.parse(e.target.responseText));
It's recommended to use <iron-ajax>, and scrap <shop-category-data>. e.g. replace the following line
<shop-category-data data="{{data}}"></shop-category-data>
with
<iron-ajax auto url="data/jsonData.json" handle-as="json"
last-response="{{data}}"></iron-ajax>
I'm using the latest Polymer (1.2.0), and I'm having trouble with databinding from iron-localstorage to the iron-ajax headers field. I'm not seeing the Authorization header set when I inspect the request. I've verified the request works when I just create a valid headers object with no databinding.
Am I doing something wrong, or is it not designed to be used like this?
<iron-localstorage name="userToken" value="{{localtoken}}" use-raw></iron-localstorage>
<iron-ajax url="api/twitter/v1/private/gettweets" last-response="{{data}}" auto
headers= '{"Authorization":"Bearer [[localtoken]]}"'
handle-as="json">
</iron-ajax>
<iron-list items="[[data.futuretweets]]" as="item">
<template>
<div>
datetime: <span>[[item.datetime]]</span>
text: <span>[[item.text]]</span>
</div>
</template>
</iron-list>
I think you have a typo error in your compound binding, here is a corrected version:
<iron-ajax url="api/twitter/v1/private/gettweets" last-response="{{data}}" auto
headers= '{"Authorization":"Bearer [[localtoken]]"}'
handle-as="json">
</iron-ajax>
[EDIT] since it is not working, try with a computed function like this:
<iron-ajax url="api/twitter/v1/private/gettweets" last-response="{{data}}" auto
headers='_computeHeaders(localtoken)'
handle-as="json">
</iron-ajax>
where
_computeHeaders(localtoken) {
return {"Authorization": "Bearer " + localtoken};
}
Shouldn't:
_computeHeaders(localtoken) {
return '{"Authorization":"Bearer ' + localtoken + '"}';
}
Instead be:
_computeHeaders: function(localtoken){
return '{"Authorization":"Bearer ' + localtoken + '"}';
}
I've found a little workaround.
I'm using loopback with authentication service enabled and I'm saving the authentication token with iron-localstorage. The problem I've found is that the "auto" of iron-ajax let start the request when any ( almost ) of the iron-ajax parameter change. When the request start the localstorage value hasn't been populated yet
<iron-localstorage name="appdata" value="{{user}}"></iron-localstorage>
and
<iron-ajax auto="{{user.id}}" headers$='{"Authorization" :"{{user.id}}"}' url="/api/data" handle-as="json" last-response="{{data}}"></iron-ajax>
the workaround is inside auto="{{user.id}}". While user isn't loaded the user.id is false. When loaded it become something that match as true. That cause also a change inside the iron-ajax header attribute and cause the "auto" request send to get fired.
I've a custom element which, among other things, has a core-input and a paper button in it.
When the element is created, the input is disabled, and I want to enable it when I tap the button.
I've tried several ways and can't access the input's attribute.
<paper-input-decorator label="Nombre de usuario" floatingLabel>
<input id="usernameinput" value="{{UserName}}" is="core-input" disabled />
</paper-input-decorator>
<paper-button raised id="edprobutton" on-tap="{{edbutTapped}}">EDITAR</paper-button>
What should I write in
edbutTapped: function () {
},
EDIT
So, I've learned that the problem was that my username input element was inside a repeat template, and that's bad for what I was trying to do. Now I'm trying to bind a single json object to my element, with no luck so far.
What I have right now:
In my Index page:
<profile-page id="profpage" isProfile="true" entity="{{profEntity}}"></profile-page>
<script>
$(document).ready(function () {
var maintemplate = document.querySelector('#fulltemplate');
$.getJSON('api/userProfile.json', function (data) {
var jsonString = JSON.stringify(data);
alert(jsonString);
maintemplate.profEntity = jsonString;
});
}
</script>
In my element's page:
<polymer-element name="profile-page" attributes="isprofile entity">
<template>
<style>
[...]
</style>
<div flex vertical layout>
<core-label class="namepro">{{entity.Name}}</core-label>
<core-label class="subpro">{{entity.CompanyPosition}}</core-label>
<core-label class="subpro">{{entity.OrgUnitName}}</core-label>
</div>
</template>
</polymer-element>
And my JSON looks like this:
{"Name": "Sara Alvarez","CompanyPosition": "Desarrollo","OrgUnitName": "N-Adviser"}
I'm asuming I need to "update" my element somehow after changing its entity attribute?
Try the following
<script>
Polymer({
edbutTapped: function () {
this.$.usernameinput.disabled = false;
}
});
</script>
The this.$ allows you to access controls defined in an elements and the usernameinput is the id you assigned to the input.
This can go below the closing tag of the element you are defining.
'disabled' is conditional-attribute.
So this will be the correct use of it:
<input id="usernameinput" value="{{UserName}}" is="core-input" disabled?="{{isDisabled}}" />
In the prototype:
//first disable the field, can be done in ready callback:
ready: function () {
this.isDisabled = 'true';
}
//set idDisabled to 'false' i.e. enable the input
edbutTapped: function () {
this.isDisabled = 'false';
},
OK this is going to be a long answer (hence why I am not entering this as an edit of my original answer). I've just done something which is functionally the same.
The first thing is this code;
$.getJSON('api/userProfile.json', function (data) {
var jsonString = JSON.stringify(data);
alert(jsonString);
maintemplate.profEntity = jsonString;
});
Polymer has a control called core-ajax - this as it's name suggests makes an ajax call. The other really nice thing is that it can be made to execute when the URL changes. This is the code from the project I've got.
<core-ajax id="ajax"
auto=true
method="POST"
url="/RoutingMapHandler.php?Command=retrieve&Id=all"
response="{{response}}"
handleas="json"
on-core-error="{{handleError}}"
on-core-response="{{handleResponse}}">
</core-ajax>
The auto is the bit which tells it to fire when the URL changes. The description of auto from the polymer documentation is as follows;
With auto set to true, the element performs a request whenever its
url, params or body properties are changed.
you don't need the on-core-response but the on-core-error might be more useful. For my code response contains the JSON returned.
So for your code - it would be something like this
<core-ajax id="ajax"
auto=true
method="POST"
url="/api/userProfile.json"
response="{{jsonString}}"
handleas="json"
on-core-error="{{handleError}}" >
</core-ajax>
Now we have the data coming into your project we need to handle this. This is done by making use of Polymer's data-binding.
Lets detour to the element you are creating. Cannot see anything wrong with the following line.
<polymer-element name="profile-page" attributes="isprofile entity">
We have an element called 'profile-page' with two properties 'isprofile' and 'entity'.
Only because my Javascript leaves a bit to be desired I would pass each property as a seperate entity making that line
<polymer-element name="profile-page" attributes="isprofile name companyposition OrgUnitName">
Then at the bottom of your element define a script tag
<script>
Polymer({
name: "",
companyposition: "",
OrgUnitName: ""
});
</script>
Now back to the calling (profile-page). The following code (from my project) has the following;
<template repeat="{{m in response.data}}">
<map-list-element mapname="{{m.mapName}}" recordid="{{m.Id}}" on-show-settings="{{showSettings}}">
</map-list-element>
</template>
Here we repeat the following each element. In your case you only have one entry and it is stored in jsonString so your template is something like this
<template repeat="{{u in jsonString}}">
<profile-page name="{{u.name}} companyposition="{{u.companyposition}}" OrgUnitName="{{u.OrgUnitName}}">
</profile-page>
</template>
Now we get to the issue you have. Return to your profie-page element. Nothing wrong with the line
on-tap="{{edbutTapped}}"
This calls a function called edbutTapped. Taking the code I gave you earlier
<script>
Polymer({
edbutTapped: function () {
this.$.usernameinput.disabled = false;
}
});
</script>
The only thing to change here is add the following code
created: function() {
this.$.usernameinput.disabled = true;
},
This is inserted after the Polymer({ line. I cannot see in your revised code where the usernameinput is defined but I am assuming you have not posted it and it is defined in the element.
And you should be working, but remember to keep your case consistent and to be honest I've not been - certain parts of Polymer are case sensitive - that catches me out all the time :)
I'm stuck a bit conceptually. I've created a builder component for managing the input of data via an AJAX post. On return, I'll have a JSON object that I can render to the client. Optimally, I'd like to instantiate a new render component, pass the JSON object to it, and then destroy the builder component (door number two is a simple page reload, but that seems like a very 1990s hammer for a 21st century nail).
Representative (simplified) builder component:
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="/bower_components/core-ajax/core-ajax.html">
<polymer-element name="post-builder" attributes="accesstoken">
<template>
<core-ajax id="poster" url="api_call" handleAs="json"></core-ajax>
<textarea class="form-control" rows="4" placeholder="Enter text here." value="{{ body }}"></textarea>
<div class="postControls">
<div class="sendLink">
Post
</div>
</div>
</template>
<script>
created: function(){
this.body = '';
},
ready: function(){
},
postAndReplaceTile: function(){
data = {body : this.body, publish : true};
var ajax = this.$.poster;
ajax.removeEventListener('core-response');
ajax.method = 'POST';
ajax.contentType = 'application/json';
ajax.params = { access_token: this.accesstoken };
ajax.body = JSON.stringify(data);
ajax.addEventListener('core-response', function(){
if(this.response.hasOwnProperty('post')){
if(this.response.post.hasOwnProperty('id')){
// valid JSON object of the new post
}
}
});
ajax.go();
}
});
</script>
</polymer-element>
At the stage of the valid JSON object, I'm looking for a moral equivalent of jQuery's replaceWith()...recognizing that the JSON object is in the component that's being replaced so I need to sequence these events carefully. Is there a way to cleanly reach up to the parent DOM and do these types of transformations?
You could use parentNode.host (see here) to access the container element and use DOM methods to replace the element but that's somehow an anti-pattern and breaks encapsulation (see here and here).
It's proably better to use events and let the container element take care of swaping the elements.