I was somewhat bummed to discover that Polymer's
<polymer-ajax> does not support JSON HTTP request bodies—at least in the Dart version. But after a bit of digging, it seems that there is a <polymer-xhr> that seems to support arbitrary body data (in both Dart and JavaScript). So tonight, I explore this element in the hopes that I can use it to get the higher-level <polymer-ajax> to also support arbitrary HTTP payload content.So I import the
<polymer-xhr> tag definition: <!-- Load component(s) -->
<link rel="import" href="/packages/change_history/change-sink.html">
<link rel="import" href="/packages/change_history/store-changes.html">
<link rel="import" href="/packages/polymer_elements/polymer_localstorage/polymer_localstorage.html">
<link rel="import" href="/packages/polymer_elements/polymer_ajax/polymer_ajax.html">
<link rel="import" href="/packages/polymer_elements/polymer_ajax/polymer_xhr.html">Then, in the body of the page, I add <polymer-xhr> as a child to the <store-changes> element that I am trying to get working over Ajax: <store-changes>
<!-- <polymer-localstorage name="store-changes" value="{{value}}"></polymer-localstorage> -->
<!-- <polymer-ajax url="http://localhost:31337/widgets/change-history" -->
<!-- handleAs="json"> -->
<!-- </polymer-ajax> -->
<polymer-xhr></polymer-xhr>
<!-- changes will originate from elements here ... -->
</store-changes>With that, I am ready to make my changes to the class that is backing <store-changes>. Instead of setting <polymer-ajax> parameters and then invoking go(), I just need to invoke request() on <polymer-xhr> with the necessary options. And, since this is a low-level call, there are a lot of options:@CustomTag('store-changes')
class StoreChangesElement extends PolymerElement {
StoreChangesElement.created(): super.created();
// ...
get store => children.
firstWhere((el) => el.localName == 'polymer-xhr');
storeChange(e) {
// Convert the change event into an "update" record, then...
store.
request({
'url': 'http://localhost:31337/widgets',
'method': 'POST',
'callback': (__res, __xhr){},
'headers': {},
'body': JSON.encode(update)
});
// ...
}
}And that turns out to just work™. When I make changes to the the contained <div contenteditable> element, change events propagate to the custom Polymer, <store-changes> which sends JSON encoded HTTP requests to the server:{"id":"change-history","current":"\n <h1>
Change #1</h1>
\n ","history":["\n <h1>
Change #1</h1>
\n "]}And the server is happy to respond with:HTTP/1.1 201 Created server: Dart/1.0 (dart:io) access-control-allow-origin: * access-control-allow-headers: Content-Type, X-Requested-With access-control-allow-methods: GET,DELETE,PUT content-type: application/json; charset=utf-8 transfer-encoding: chunked content-encoding: gzipYay!
So the question then becomes, can I get
<polymer-ajax> to support arbitrary HTTP bodies? To answer that question, I fork the polymer elements (Dart), and clone it locally. Then I point my application to this local copy of my fork by making the necessary change in pubspec.yaml:name: change_history
dependencies:
polymer: any
polymer_elements:
path: /home/chris/repos/polymer_elementsA quick pub getIn my new copy of polymer_elements, I add an xhrArgs public attribute to <polymer-ajax> (mimicking the property of the same name in the JavaScript version of the element):@CustomTag('polymer-ajax')
class PolymerAjax extends PolymerElement {
// ...
@published
Map xhrArgs;
// ...
go() {
var args = xhrArgs != null ? xhrArgs : {};
// ...
}
}If present, this Map will serve as the default values for the arguments sent to the underlying <polymer-xhr>. I already know that, if there is body key in that xhrArgs then I can POST whatever I want via <polymer-xhr>, so I supply just that in my <store-changes> element:@CustomTag('store-changes')
class StoreChangesElement extends PolymerElement {
StoreChangesElement.created(): super.created();
// ...
get store => children.
firstWhere((el) => el.localName == 'polymer-ajax');
storeChange(e) {
// Convert the change event into an "update" record, then...
store
..method = 'POST'
..xhrArgs = {'body': JSON.encode(update)}
..go();
// ..
}
}After switching back to the higher level <polymer-ajax> in my web page:<store-changes>
<!-- <polymer-localstorage name="store-changes" value="{{value}}"></polymer-localstorage> -->
<polymer-ajax url="http://localhost:31337/widgets/change-history"
handleAs="json">
</polymer-ajax>
<!-- <polymer-xhr></polymer-xhr> -->
<!-- changes will originate from elements here ... -->
</store-changes>It works! When I make changes in the wrapped <div contenteditable>, my <store-changes> Polymer sees the change and saves a history record over XHR. Only this time, it does it with <polymer-ajax> and mercifully fewer arguments. There are a few other benefits to be reaped in
<polymer-ajax>. I will likely try to reap them tomorrow.Day #955
No comments:
Post a Comment