polymer-dart
polymer-dart copied to clipboard
Uncaught TypeError: Cannot read property '__dartClass__' of undefined
Having trouble registering a propertyChanged observer in Polymer.dart (dart 1.18.1). The method pageChanged is never called when I run pub serve and surf to http://localhost:......web/index.html#/home. I suspect it is not registered in the first place.
Error:
Uncaught TypeError: Cannot read property '__dartClass__' of undefined
Polymer.Dart.InteropBehavior._propertyChanged
Polymer.Bind._modelApi._propertySetter
Polymer.Base._addFeature._notifyPath
(anonymous function)
Polymer.Base._addFeature._notifyListener
(anonymous function)
Polymer.Base._addFeature.fire
Polymer.Base._addFeature._notifyPathUp
Polymer.Base._addFeature._notifyPath
Polymer.Base._addFeature.set
Polymer.__routeQueryParamsChanged
Polymer.Base.extend._complexObserverEffect
Polymer.Base._addFeature._complexObserverPathEffect
Polymer.Base._addFeature._pathEffector
Polymer.Base._addFeature._notifyPath
Polymer.Base._addFeature.set
Polymer.__tailQueryParamsChanged
Polymer.Base.extend._complexObserverEffect
Polymer.Base._addFeature._complexObserverPathEffect
Polymer.Base._addFeature._pathEffector
Polymer.__setMulti
Polymer.__tryToMatch
Polymer.Base.extend._complexObserverEffect
Polymer.Bind._modelApi._effectEffects
Polymer.Bind._modelApi._propertySetter
Polymer.Bind._modelApi.__setProperty
Polymer.Base._addFeature._applyEffectValue
Polymer.Base.extend._annotationEffect
Polymer.Bind._modelApi._effectEffects
Polymer.Bind._modelApi._propertySetter
setter
(anonymous function)
Polymer.Base._addFeature._flushHandlers
Polymer.Base._addFeature._afterClientsReady
Polymer.Base._addFeature._ready
Polymer.Base._addFeature._tryReady
Polymer.Base._addFeature._initFeatures
Polymer.Base.createdCallback
(anonymous function)
registerDartTypeUpgrader
(anonymous function)
See https://github.com/pjjjv/Akepot/commit/dbf85b3bae3647fbfea78ff6e0a3807fd6e3de1d file app_akepot.dart.
Please can you provide more context? Pubspec.yaml to begin with and snippets of code could help diagnose the problem.
Here's the pubspec.yaml: https://github.com/pjjjv/Akepot/blob/dbf85b3bae3647fbfea78ff6e0a3807fd6e3de1d/pubspec.yaml. Full source code was linked to above.
The error only occurs when I add tail="{{subroute}}" to the app-route element in app-akepot.html.
Some code snippets:
<link rel="import" href="../../packages/polymer/polymer.html">
<dom-module id="app-akepot">
<style>
:host {
display: block;
}
</style>
<link rel="import" type="css" href="app_akepot.css">
<template>
<div>Abba</div>
<login-screen id="loginscreen" layout vertical center-center fit class="{{(signedIn && readyDom) ? '' : 'show'}}" signedin="{{signedIn}}"></login-screen>
<competences-service id="service" user="{{user}}" signedin="{{signedIn}}" readydom="{{readyDom}}"></competences-service>
<app-location route="{{route}}" use-hash-as-path></app-location>
<app-route
route="{{route}}"
pattern="/:page"
data="{{routeData}}"
tail="{{subroute}}"></app-route>
<iron-pages role="main" selected="[[page]]" attr-for-selected="name">
<page-home name="home"></page-home>
<page-edit name="edit"></page-edit>
<page-not-found name="not_found"></page-not-found>
</iron-pages>
<paper-toast id="toast" text="Connection timed out. Showing limited messages.">
<paper-button dismissive tabindex="0" autofocus class="flat-button">Retry</paper-button>
</paper-toast>
</template>
</dom-module>
@HtmlImport('app_akepot.html')
library akepot.lib.app_akepot;
import 'package:polymer/polymer.dart';
import 'package:web_components/web_components.dart';
import 'package:akepot/login_screen.dart';
import 'package:akepot/competences_service.dart';
import 'package:akepot/pages/page_home.dart';
import 'package:akepot/pages/page_edit.dart';
import 'package:akepot/pages/page_not_found.dart';
import 'package:polymer_elements/app_route.dart';
import 'package:polymer_elements/app_location.dart';
import 'package:polymer_elements/iron_pages.dart';
@PolymerRegister('app-akepot')
class AppAkepot extends PolymerElement {
@property bool signedIn;
@property bool readyDom;
@property User user;
@property CompetencesService service;
@Property(observer: 'pageChanged', notify: true) String page;
AppAkepot.created() : super.created();
static const int MIN_SPLASH_TIME = 1000;
static const Duration SPLASH_TIMEOUT = const Duration(milliseconds: MIN_SPLASH_TIME);
void ready () {
service = $$('#service');
}
@Observe('routeData.page')
void routePageChanged(String page) {
if (page == null || page == ""){
page = 'home';
}
this.page = page;
}
@reflectable
void pageChanged(String page, String oldValue) {
// load page import on demand.
Polymer.importHref('pages/page_' + page + '.html');
}
}
From slack. app_akepot.htmlhas in <app-route ...> bindings to properties route, routeData and subroute I don't find them in app_akepot.dart, normally shouldn't they be defined here or?
I believe that solves it. Thanks for your response in slack.
Totally couldn't make that up from the error message though. Can that be fixed?
Great. I haven't seen that warnings for this could be activated, maybe there are some option in js polymer.
Sorry, I checked superficially. Adding subroute to the dart file does not solve it.
The error message is thrown in:
<script>
(function() {
Polymer.Base.originalPolymerCreatedCallback =
Polymer.Base.createdCallback;
Polymer.Base.createdCallback = function() {
if (this.__isPolymerDart__) return;
this.originalPolymerCreatedCallback();
};
Polymer.Dart = {};
// Placeholder for `undefined` return values. This should eventually go
// away, once we have dart:js support for `undefined`.
Polymer.Dart.undefined = {};
// Used to get an empty constructor function which doesn't call into dart.
Polymer.Dart.functionFactory = function() { return function() {}; };
// Generates a function which accesses a particular property on a dart
// class
Polymer.Dart.propertyAccessorFactory = function(property, dartGetter) {
return function() {
if (this.__cache__) return this.__cache__[property];
var value = dartGetter(this.__dartClass__);
if (value === Polymer.Dart.undefined) return undefined;
return value;
};
};
// Generates a function which sets a particular property on a dart class.
Polymer.Dart.propertySetterFactory = function(property, dartSetter) {
return function(value) {
if (this.__cache__) this.__cache__[property] = value;
dartSetter(this.__dartClass__, value);
};
};
// Generates a function which invokes a dart function with this or the
// __dartClass__ proxy if it exists as the first argument, and all other
// arguments as the second argument.
Polymer.Dart.invokeDartFactory = function(dartFunction) {
return function() {
var thisArg = this.__dartClass__ ? this.__dartClass__ : this;
// Must convert `arguments` to an actual array that the js side can
// recognize.
var args = [];
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
var value = dartFunction(thisArg, args);
if (value === Polymer.Dart.undefined) return undefined;
return value;
}
};
// TODO(jakemac): This shouldn't be necessary, but it is today
// https://github.com/dart-lang/sdk/issues/24371
Polymer.Dart.serialize = Polymer.Base.serialize;
Polymer.Dart.deserialize = Polymer.Base.deserialize;
Polymer.Dart.InteropBehavior = {
// Secret hook into property changes. Pretty hacky but its more efficient
// than using a JsProxy object for the element.
_propertyChanged: function(path, newValue, oldValue) {
var parts = this._getPathParts(path);
var prop = parts.splice(parts.length - 1, 1)[0];
// Get the model which is being updated.
var thisArg = this.get(parts);
// If the property to update is an array key, we need to get the index
// for that item instead.
if (prop.charAt(0) == '#') {
// Ouch, O(n) operation :(. Also doesn't support duplicates :(.
prop = thisArg.indexOf(Polymer.Collection.get(thisArg).getItem(prop));
}
// Grab the __dartClass__ if we already have one
thisArg = thisArg.__dartClass__ || thisArg; **<<<<<------error message thrown here**
// Finally, update things on the dart side.
Polymer.Dart.propertyChanged(thisArg, prop, newValue);
},
serialize: function(value, type) {
return Polymer.Dart.serialize(value, type);
}
}
})();
</script>
Here's with the debugger variables. I do have an event "signedIn" elsewhere in my code.
Polymer.Dart.InteropBehavior = {
// Secret hook into property changes. Pretty hacky but its more efficient
// than using a JsProxy object for the element.
_propertyChanged: function(path, newValue, oldValue) { path = "signedIn", newValue = false, oldValue = undefined
1562
var parts = this._getPathParts(path); parts = []
1563
var prop = parts.splice(parts.length - 1, 1)[0]; prop = "signedIn"
1564
1565
// Get the model which is being updated.
1566
var thisArg = this.get(parts); thisArg = competences-service#service.style-scope.app-akepot, parts = []
1567
1568
// If the property to update is an array key, we need to get the index
1569
// for that item instead.
1570
if (prop.charAt(0) == '#') { prop = "signedIn"
1571
// Ouch, O(n) operation :(. Also doesn't support duplicates :(.
1572
prop = thisArg.indexOf(Polymer.Collection.get(thisArg).getItem(prop)); prop = "signedIn", thisArg = competences-service#service.style-scope.app-akepot
1573
}
// Grab the __dartClass__ if we already have one
thisArg = thisArg.__dartClass__ || thisArg;
// Finally, update things on the dart side.
Polymer.Dart.propertyChanged(thisArg, prop, newValue);
},
It gets into the same trouble when path = "route.__queryParams".
@pjjjv The newpolymer branch contains the latest and updated version of the code https://github.com/pjjjv/Akepot/tree/newpolymer ?
Try initializing fields bounded to app-route tag attributrs with {}.
@jonboj correct
@dam0vm3nt Indeed, I debugged the propertyChanged function. After I uncommented the signedIn parts from my code, the only remaining variable that it stumbles over is "subroute". If I remove this, then finally, my pageChanged method is triggered. But the reason the javascript code stumbles over subroute is that it does not find it on AppAkepot. Your solution of setting "subroute ={};" for initialization solves that, and this works too. Thanks @dam0vm3nt.
Now, the standard app-route element seems to be easily high on difficult data bindings involving structured data and leads to observing path changes internally in Polymer.dart.
Is this necessary initialization documented somewhere? Does it apply to most polymer elements?
@pjjjv Just looking in the code at branch newpolymer The routeDatais treated as a structured binded data.
https://github.com/pjjjv/Akepot/blob/newpolymer/lib/app_akepot.dart#L37
routeData is declared as dynamic type
https://github.com/pjjjv/Akepot/blob/newpolymer/lib/app_akepot.dart#L21
Polymer does a lot of instrumentation of the binded properties, since dynamic is like so fare unknown type, I suspect this will not work with a dynamic. I think before doing more bug search on <app-route> it is worth to get some confirmation that bind to dynamic works.
Update
Did a test with binding to dynamic with structured data. Works ok, also an @Observe method is correct triggered.
This is a bug on polymer_interop imho. This bug is triggered by those componente because they notify null values that usually doesn't happen. The code in polymer_interop doesn't check for null and this leads the error. I'm going to send a patch to polymer_interop but don't know if it gets accepted soon because at the moment the maintainer seams to be away
hi, can you try adding the following overrides to you yaml and see if the problem is gone ?
polymer:
version: "^1.0.0-rc.18.exp.6"
hosted:
name: polymer
url: http://pub.drafintech.it:5001
polymer_interop:
version: "^1.0.0-rc.10.exp.8"
hosted:
name: polymer
url: http://pub.drafintech.it:5001
This is a polymer version corresponding to the latest PR I've submitted. It contains also an new strategy for converting to and from JS that should be more performant for lists and maps.
@dam0vm3nt These PRs solve it as far as I can tell. The error is gone. (I had trouble reproducing for a while so the circumstance might be slightly different).
fixed by https://github.com/dart-lang/polymer_interop/pull/47