Saturday, October 5, 2013

Dynamically Creating Polymer.dart Elements and Attributes


I have multiple <ice-code-editor> working the right way. Last night's fix was not so much a Polymer.dart solution as it was a Dart / js-interop / my-crazy-usage solution. But I do not know if the solution is complete.

I know that, if the first <ice-code-editor> is evaluated before some of the required JavaScript has loaded, then everything will work. The underlying ICE Code Editor code already accounted for this case and it has always worked in the Polymer.dart version. After last night, I also have a second <ice-code-editor> working — even if the JavaScript pulled in by the first element has not finished loading. What I am unsure of is what happens when a third element loads after the JavaScript has been loaded and evaluated.

To figure out if this is working, I am going to dynamically create and add a <ice-code-editor> element to my test page and query that the underlying component is updated correctly:
      test("can embed code much later", (){
        var later = new Element.html(
          '<ice-code-editor src="embed_baz.html" line_number="0"></ice-code-editor>'
        );
        document.body.append(later);
        expect(
          queryAll('ice-code-editor').last.shadowRoot.query('h1').text,
          contains('embed_baz.html')
        );
      });
Adding my custom element like this does not quite work, however. Dart's HTML sanitization strips the tag, causing the test to fail:
Removing disallowed element <ICE-CODE-EDITOR>
ERROR: [polymer] multiple elements can embed code much later
  Test failed: Caught Bad state: No elements
  dart:collection/iterable.dart 266:26 
I try to define the usual NullTreeSanitzer:
class NullTreeSanitizer implements NodeTreeSanitizer {
  void sanitizeTree(node) {}
}
And use that in my test:
      test("can embed code much later", (){
        var later = new Element.html(
          '<ice-code-editor src="embed_baz.html" line_number="0"></ice-code-editor>',
          treeSanitizer: new NullTreeSanitizer()
        );
        document.body.append(later);

        expect(
          queryAll('ice-code-editor').last.shadowRoot.query('h1').text,
          contains('embed_baz.html')
        );
      });
With that, I no longer get the error, but neither is my test passing:
ERROR: [polymer] multiple elements can embed code much later
  Test failed: Caught The null object does not have a method 'query'.
  
  NoSuchMethodError : method not found: 'query'
  Receiver: null
  Arguments: ["h1"]
  dart:core-patch/object_patch.dart 20:25                                                                                                   Object.noSuchMethod
  ../test.dart 70:60               
Even if I wrap that in a Timer delay, I still get the same failure. Eventually, I discover the polymer / custom_element createElement() and createElementFromHtml() methods. These methods ought to serve as a means of creating a custom element that has already been registered. But even these don't work. Well, not without some effort.

I am able to get this working by creating the element with createElement()—without any attributes. I then have to access the web component via the xtag property:
  // This gets: Removing disallowed element  ...
  // var later = createElementFromHtml('<ice-code-editor src="embed_baz.html" line_number="0"></ice-code-editor>');

  // Don't see attributes change ...
  // var later = createElement('ice-code-editor')
  //   ..attributes['src'] = 'embed_baz.html'
  //   ..attributes['line_number'] = '0';

  var later = createElement('ice-code-editor');
  var iceElement = later.xtag;
  iceElement.src = 'embed_baz';

  document.body.append(later);
Though to get the src loaded, I now have to implement the attributeChanged() method in my Polymer class:
class IceCodeEditorElement extends PolymerElement with ObservableMixin {
  @published String src;
  @published int line_number = 0;
  ICE.Editor editor;

  void created() { /* ... */ }

  void attributeChanged(String name, String oldValue) {
    super.attributeChanged(name, oldValue);
    loadContent();
  }

  loadContent() { /* ... */ }
}
With that, I have my test passing:
PASS: [polymer] can embed code
PASS: [polymer] can set line number 
PASS: [polymer] creates a shadow preview 
PASS: [polymer] creates an editor
PASS: [polymer] multiple elements can embed code 
PASS: [polymer] multiple elements can embed code much later 

All 6 tests passed. 
That was a little harder than I expected—at least dynamically updating the attributes on a created element. Which makes me suspect that I am doing something wrong. Ah well, for tonight I will accept the working solution. The right solution is solid grist for tomorrow.


Day #895

No comments:

Post a Comment