Thursday, June 18, 2015

Data Binding vs. Event Handling

Refactoring some of my projects from Polymer 0.5 (or earlier) to Polymer 1.0 I found myself using data binding and computed properties for situations where I previously had event handlers doing the "hard work". Since I think this is a rather nice and clean pattern  I thought I'd give some examples for this.

Let's take a look at a simple sample of a login. Using the google-signin element you could wait for the google-signin-success event to trigger and then retrieve/display information about the authenticated user and toggle the UI accordingly. Of course then you also have to handle the reverse case if a user signs out by listening for the google-signed-out event.

But the google-signin element also offers an is-authorized attribute / isAuthorized property that you can bind to and observe. Toggling the UI based on this property is as simple as adding hidden$="[[!isAuthorized]]" and hidden$="[[isAuthorized]]" to elements you want to show/hide. No extra JS necessary for this, as opposed to before where you had to set isAuthorized in the event handlers.
To retrieve user information once authorization has been granted you could add an observer to isAuthorized, but I think the much nicer solution is to make user a computed property that depends on isAuthorized. Whenever the value of isAuthorized changes this will re-evaluate the function and set the user property accordingly.

Let's take this sample a bit further. In many cases you will have to retrieve some more information from your server or elsewhere about the authenticated user. So you would need to trigger some request once the user is signed in and handle the response once it is available. In this sample I'm using my discovery-api-elements to fetch information about the user from Google+, but you can do something similar using iron-ajax or any other data-fetching element.

Instead of triggering the request manually, what you can do is binding the auto property (at least for discovery-api-elements or iron-ajax) to the isAuthorized property. Once isAuthorized and with it auto becomes true the request will be triggered automatically and you just have to handle the response.

But this won't remove the data in case the user signs out. To achieve this we make the data that is displayed (activities) a computed property that depends on both the response from the data-fetching element and the isAuthorized property.

Here's what happens now, when a user signs in:
  1. google-signin sets isAuthorized to true.
  2. This sets the auto property on the data element which triggers the request.
  3. Once the request completes plus-activities-list sets the response property accordingly.
  4. This change triggers recomputing activities with the _parseActivities function.
  5. Once there are items in the activities array, they will be displayed by the dom-repeat.
When the user signs out again:
  1. google-signin sets isAuthorized to false.
  2. This triggers recomputing activities which will be set to an empty list.
All of this without having to explicitly care about any event handlers, and provided another data- or sign-in-element offers similar properties you can bind to, you can simply replace those elements.

Friday, June 12, 2015

Polymer Quicktip - Attributes vs. Properties

A recurring problem that people starting with Polymer 1.0 or migrating from earlier version seem to have is the new property name to attribute name mapping.

This issue imho comes mainly from the fact that the element docs generated via iron-component-page only list the JS property names, but in many/most cases you will use the HTML attribute names in your markup that aren't listed anywhere.

Example from the google-signin element:

If you try to include this element in your page like this

<google-signin clientId="MY_CLIENT_ID"></google-signin>

it won't work because the clientId attribute will be mapped to a clientid property that doesn't exist and clientId will stay undefined.

The correct way to use the element would be:

<google-signin client-id="MY_CLIENT_ID"></google-signin>

So if you encounter issues with properties not getting the value you intended make sure your attribute names are correct.

Essentially the attribute name is converted to lower case first, and then dashes are converted to camelcase, SoMeThInG becomes something and SoMeThInG-ElSe becomes somethingElse.

For those interested, here's the part of the Polymer library that takes care of translation between attribute names and property names:

And if you are really curious you can have a look at Polymer.CaseMap._caseMap to see what mappings are being used on your site.

Thursday, June 11, 2015

Polymer Quicktip - debounce

One of the more hidden features of Polymer is the possibility to "debounce" multiple requests into one function invocation.

This is useful if you have a compute- or time-heavy function that depends on several (published) properties and needs to be executed when those properties get a new value, e.g. if you need to create a new ajax call depending on several parameters.

Here a simple sample to demonstrate this behaviour.
First the element without debounce:
Including this element as <without-debounce property1="foo" property2="bar"></without-debounce> will trigger the function twice when the element is first loaded, and even if you change both properties at the same time you still get two function calls.

Here the same element with the debounce functionality added:
Using this element the console.log will only be called once when the element loads and also only once when properties get changed during a definable time (300ms in this case). This causes a small, but mostly ignorable delay before the actual execution of the function.

An element that uses this functionality is the iron-ajax element to prevent executing the actual request until all properties have "finalised".

I'm using the same behavior for the same reason in my discovery-api-elements.

Monday, June 1, 2015

The Photos Dilemma

While the new Google Photos has some pretty interesting features for users (and several problems as well which I won't discuss here) the situation for developers wanting to do anything with photos gets increasingly depressing. Let's have a look at a little bit of history of how things evolved, where we are today, and what I would wish for the future.

In the beginning there was Picasa

Picasa Web Albums which is still available today comes with a fully-fledged API  with read & write access to fully manipulate and organize photos. Admittedly the old GData APIs aren't the nicest to work with compared to modern APIs, especially for client-side applications in JS, but the API still does its job today.

Probably the most useful API calls for read-access, since the documentation can be a bit confusing:

Request a list of albums:{{userid}}

Info about one album:{{userid}}/albumid/{{albumid}}

List photos in an album:{{userid}}/albumid/{{albumid}}

Along came Buzz

Here's a blog post for those who still remember the good old times:

Google Buzz didn't really change much about how Picasa Web Albums and the associated API worked, it mostly seemed like Buzz was using the API itself to achieve all the features.

One feature that was introduced was the concept of "Photos from Posts" that automatically created special albums in Picasa for each post with photos you shared to Buzz. Those albums could be recognized in the Picasa Web Albums API with the <gphoto:albumType>Buzz</gphoto:albumType> tag they had assigned in the album description.

Funny enough photos shared directly in posts on Google+ today still generate "Buzz"-albums.

On the plus side...

With Google+ we got a new UI for managing photos that in many ways still is more cumbersome to use than the old Picasa Web Albums UI. But the album and photo IDs matched so it was easy to use the Picasa Web Albums API for programmatic managing of your Google+ photos

With Google+ the new concept of sharing albums to circles was introduced. Those albums would show up with <gphoto:access>private</gphoto:access> and you could (and still can) retrieve the information about what people and circles albums were shared with by requesting the acl of an album:{{userid}}/albumid/{{albumid}}/acl

This would show information like this:

  <gAcl:scope type='group' value='...'/>
  <gphoto:nickname>Photo Share Test</gphoto:nickname>

  <gAcl:scope type='user' value='...'/>
  <gphoto:nickname>Scarygami Test</gphoto:nickname>

Google+ also introduced Instant Upload (or Auto Backup as it is called) creating a new automatic album with the <gphoto:albumType>InstantUpload</gphoto:albumType> tag. As with "buzz" the "InstantUpload" name stayed in the API even after the name was changed in the front end

At the Drive-In

Things started to get a little bit weird with Google Drive integration.

It began with the feature to show (some but not all) photos stored on Google Drive in the Google+ Photos UI, with each Drive Folder that contained photos getting their own album.

Those albums wouldn't show up when requesting the list of albums from the Picasa API, but you could request some information and the photos inside if you copied the album ID from the corresponding Google+ URL ({{albumId}})

Things got even more confusing when Google(+) Photos were added to Google Drive. This allowed you to add a folder to your Drive which would include all the photos you uploaded and shared on Google+ sorted by year and month. You can then go ahead and re-arrange/edit the photos as you want, but... the sync is one-way and one-time only, meaning that changes done on Google Drive won't be reflected back to Google Photos and you only get the originally uploaded photo in Google Drive without any changes that you might make in Google Photos at a later point.

You can access those photos using the Drive API using the files.get and files.list methods, and you also have write access using insert/update/patch methods, and the Drive API being one of the newer discovery-based APIs is much nicer to work with than the antiquated Picasa API. But it won't help you in managing your Google+ Photos since the data isn't synced, and there is no indication whatsoever in the file meta-information that the files originally came from Google+. The photos in Google Drive also have completely different IDs than the ones you could use in the Picasa API, they are completely decoupled.

New and shiny?

And so we reach the present with the new Google Photos UI to replace the Google+ Photos UI.

Since there are several essential features missing, like the possibility to add geotags, I've been thinking about creating some extensions/scripts to do some of the things via the Picasa API. The problem is that Google Photos invented completely new IDs for photos and album that don't match the corresponding IDs in the Picasa API, even though the photos and albums still show up there.

The Picasa IDs show up nowhere in the page source so they could be parsed, and the Google Photos IDs don't show up anywhere in the Picasa API which makes finding a matching photo to work with in the Picasa API nearly impossible. You could parse some meta information (like date/filename) from the Google Photos page and try to find a match in the Picasa API but that is (a) bound to break regularly as the Google Photos page gets updated and (b) potentially requires a lot of API requests until you get where you want. But that seems to be the only possibility at the moment to get some programmatic access to your photos, or you could completely forget about Google Photos and continue using Picasa Web Albums and the API to manage your photos, only using Google Photos for uploading/backing up/editing/sharing photos.

Talking about sharing: with Google Photos the main way of sharing albums is to create a "secret link" that can be shared and viewed by anyone who has the link. That also means that all albums created with Google Photos now will always show up with <gphoto:access>private</gphoto:access>.

Sharing to Google+ still allows you to share to circles/people without creating the shareable link, and those access permissions are still visible in the Picasa API.

The Picasa API gets a little bit confused though when sharing publicly to Google+. Those albums show up as private in the API, and are shown as "Limited, anyone with the link" in the Picasa Web Albums UI. To make things a little bit more confusing those publicly shared private albums show up in the API even when not authenticated as the owner of the album:
Example of a public private album in the API

A New Hope

It's been almost 4 years now since a blog post about a potential Google+ Photos API was leaked.
While being read-only (as most of the Google+ API) this seemed like a promising start to replace the antiquated Picasa Web Albums Data API. But nothing ever happened there anymore and with Google Photos now getting decoupled from Google+ the Plus API doesn't seem to be right place to add such an API.

As discussed above the Google Drive API probably won't be a good home for new photos features either since there is no sync happening after the initial upload, even though it would be possible to represent most metadata related to albums/sharing/editing using custom file properties.

So it seems that we still have to wait for a separate photos API and try to use the Picasa Web Albums UI now as long as it is still working. The minimal functionality I would wish for is a way to map Google Photos IDs to Picasa IDs...

For lack of a better place you might want to star this feature request and maybe add a comment about what you would want to do with a Photos API and what features you are expecting to see in such an API.

Alternatively/additionally you can also use the feedback option in the new Google Photos site/app to tell Google you care about such an API.