Dart2's ResizeObserver being broken by Dart minifier - dart

i have the following snippet of Dart2 code (Dart 2.7) which works 100% when running in dev mode, but fails when running it in production mode
ResizeObserver rowObserver;
final ResizeObserverCallback rowObserverCallback = (List<ResizeObserverEntry> entries, ResizeObserver observer){
print("inside row observer callback");
for (ResizeObserverEntry entry in entries) {
print(" ... entry is ${entry.runtimeType} and entry.target is ${entry.target.runtimeType}");
//resizeRow(entry.target as TableRowElement);
}
};
rowObserver = ResizeObserver(rowObserverCallback);
Changing content in the table row now correctly outputs
inside row observer callback
... entry is ResizeObserverEntry and entry.target is TableRowElement
When building the Dart code and running it, this same snippet generates the following error:
Uncaught TypeError: Instance of 'minified:c1': type 'minified:c1' is
not a subtype of type 'List'
Running is done with
pub global run webdev serve web:9999
While building is done with
pub global run webdev build --output=web:build --release
i've tried changing --release to --no-release, but not sure how to run it in that mode.
Changing to dynamic, i'm getting the exact same issue with minification:
final ResizeObserverCallback rowObserverCallback = (dynamic blah1, dynamic blah2){
Changing to Object, at least i'm getting into the resize callback:
final ResizeObserverCallback rowObserverCallback = (Object blah1, Object blah2){
print("inside row observer callback");
print(blah1.runtimeType);
print(blah2.runtimeType);
print(blah1 is List<ResizeObserverEntry>);
which outputs:
inside row observer callback
minified:c1
minified:bQ
false
Force casting it seems to throw a casting error:
final ResizeObserverCallback rowObserverCallback = (Object blah1, Object blah2){
print("inside row observer callback");
print(blah1.runtimeType);
print(blah2.runtimeType);
print(blah1 is List<ResizeObserverEntry>);
for (ResizeObserverEntry entry in blah1 as List<ResizeObserverEntry>) {
print(" ... entry is ${entry.runtimeType} and entry.target is ${entry.target.runtimeType}");
}
};
Uncaught CastError: Instance of 'minified:c1': type 'minified:c1' is
not a subtype of type 'List'
Force casting it to a single instance of ResizeObserverEntry fails as well
final ResizeObserverCallback rowObserverCallback = (Object blah1, Object blah2){
print("inside row observer callback");
print(blah1.runtimeType);
print(blah2.runtimeType);
print(blah1 is List<ResizeObserverEntry>);
for (ResizeObserverEntry entry in [blah1 as ResizeObserverEntry]) {
print(" ... entry is ${entry.runtimeType} and entry.target is ${entry.target.runtimeType}");
//resizeRow(entry.target as TableRowElement);
}
};
Uncaught CastError: Instance of 'minified:c1': type 'minified:c1' is
not a subtype of type 'minified:bp'
pubspec.yaml in case it's relevant:
name: blah
version: 0.0.2
description: Blah Blah
environment:
sdk: ">=2.6.0 <3.0.0"
dependencies:
angular: ^5.3.1
angular_forms: ^2.1.2
http: any
js: ^0.6.0
logging: "^0.11.3+1"
html: any
json_serializable: any
blahvo:
path: ../blah-vo
dev_dependencies:
angular_test: 2.3.0
build_runner: ^1.5.2
build_test: ^0.10.2
build_web_compilers: ^2.0.0
test: ^1.0.0
So how do i actually make ResizeObserver work in Dart2?
It seems ResizeObserver is very broken in Dart2, at least in production / release mode, it works fine in dev mode.
EDIT1:
Some snippets from the generated JS where c1 is referenced:
J.c1.prototype={
k:function(a,b){H.p(b,H.j(a,0))
if(!!a.fixed$length)H.a7(P.E("add"))
a.push(b)},
u.c1=new ResizeObserver(H.aU(new L.j6(),2))},
this.a.c1.unobserve(a)},
r.c1.observe(a)},
if(a.constructor==Array)return J.c1.prototype
c1:function c1(a){this.$ti=a},
_.f3=_.c1=_.bg=_.c0=_.f2=null},
Trying to shoot in the dark, but not hitting anything
final ResizeObserverCallback rowObserverCallback = (Object blah1, Object blah2){
print("inside row observer callback");
print(blah1.runtimeType);
print(blah2.runtimeType);
print(blah1 is List<ResizeObserverEntry>);
print(blah1 is ResizeObserverEntry);
print(blah1 is TableRowElement);
print(blah1 is List<TableRowElement>);
print(blah1 is JsArray);
print(blah1 is NumToArrayPipe);
print(blah1 == null);
print(blah1 is Function);
print(blah1 is ResizeObserver);
all of them prints false
Taking a chance and replacing blah1 with blah2
final ResizeObserverCallback rowObserverCallback = (Object blah1, Object blah2){
print("inside row observer callback");
print(blah1.runtimeType);
print(blah2.runtimeType);
print(blah2 is List<ResizeObserverEntry>);
print(blah2 is ResizeObserverEntry);
print(blah2 is TableRowElement);
print(blah2 is List<TableRowElement>);
print(blah2 is JsArray);
print(blah2 is NumToArrayPipe);
print(blah2 == null);
print(blah2 is Function);
print(blah2 is ResizeObserver);
and the last one prints true as expected
EDIT2:
using JS Interopt, i'm doing console.log to see what the type of blah1 actually is and it turns out it is a DartObject containing an Array ...
context["console"].callMethod("log", [blah1]);
What should i cast blah1 to in order to get access to the list of ResizeObserverEntry ?
EDIT3
I made a demo that can reproduce the error:
bug_resize_observer.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="div1" style="background-color: red">
<textarea style="padding: 10px; margin: 10px;">
TESTING
</textarea>
</div>
<div class="div2" style="background-color: blue">
.
</div>
<script defer src="bug_resize_observer.dart.js"></script>
</body>
</html>
and bug_resize_observer.dart.js:
import "dart:html";
void main() {
final DivElement div1 = querySelector("div.div1");
final DivElement div2 = querySelector("div.div2");
final ResizeObserver divResizeObserver = ResizeObserver((List<ResizeObserverEntry> blah1, ResizeObserver blah2) {
print("inside divResizeObserverCallback");
for (ResizeObserverEntry entry in blah1) {
div2.style.height = "${entry.target.clientHeight}px";
}
});
divResizeObserver.observe(div1);
}
In DEV mode, the blue div mimicks the height of the red div when you resize the text area:
In release mode, you only get errors:
EDIT4:
Logged a bug here:
https://github.com/dart-lang/sdk/issues/40961

Related

Cannot read property 'Elm' of undefined while using Elm 0.19 in Electron

I’m experimenting Elm 0.19 with Electron building a simple Elm app (based on the Counter example) in Electron. When I run electron-forge start, I get an error that says Cannot read property 'Elm' of undefined highlighting scope[‘Elm’] part of the elm.js file.
function _Platform_export(exports) {
scope[‘Elm’] ? _Platform_mergeExportsDebug(‘Elm’, scope[‘Elm’], exports) : scope[‘Elm’] = exports;
}
The interesting thing is that the exact same files (Main.elm, index.html) open up just fine (showing the counter as expected), if I run elm-live Main.elm --open -- --output=elm.js instead.
So it appears that this passed to elm.js in Electron is undefined, which then caused scope to be undefined.
The Chrome Dev Tool shows the scope variable passed to elm.js is undefined in the case of the Electron app. In the case of elm-live, that value was the Window object.
elm.js
(function(scope){
'use strict';
--- omitted ----
var author$project$Main$main = elm$browser$Browser$sandbox({ init: author$project$Main$init, update: author$project$Main$update, view: author$project$Main$view });
_Platform_export({ 'Main': { 'init': author$project$Main$main(elm$json$Json$Decode$succeed(_Utils_Tuple0))(0) } });
})(undefined);
elm.js? [sm]
(function(scope){
'use strict';
--- omitted ----
var author$project$Main$main = elm$browser$Browser$sandbox(
{init: author$project$Main$init, update: author$project$Main$update, view: author$project$Main$view});
_Platform_export({'Main':{'init':author$project$Main$main(
elm$json$Json$Decode$succeed(_Utils_Tuple0))(0)}});}(this));
Error message
Uncaught TypeError: Cannot read property 'Elm' of undefined
at _Platform_export (elm.js:1949)
at elm.js:4004
at elm.js:4005
index.html:44 Uncaught ReferenceError: Elm is not defined
at index.html:44
Index.html
<!DOCTYPE HTML>
<html>
<head>
</head>
<body>
<div id="elm"></div>
</body>
<script src="./elm.js"></script>
<script>
var app = Elm.Main.init({
node: document.getElementById('elm')
});
</script>
</html>
Main.elm
import Browser
import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)
main =
Browser.sandbox { init = init, update = update, view = view }
-- MODEL
type alias Model = Int
init : Model
init =
0
-- UPDATE
type Msg = Increment | Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
Increment ->
model + 1
Decrement ->
model - 1
-- VIEW
view : Model -> Html Msg
view model =
div []
[ button [ onClick Decrement ] [ text "-" ]
, div [] [ text (String.fromInt model) ]
, button [ onClick Increment ] [ text "+" ]
]
In the Elm #electron channel on Slack, I saw a discussion about this issue and here is a recap for anyone interested.
The issue is that 'this' is not defined in the electron renderer
This causes scope within Elm runtime to be undefined.
Possible work-around is
uglify the compiled elm code using the instructions at https://elm-lang.org/0.19.0/optimize
or change (this) to (this||window) at the bottom of the compiled elm.js file (Opened an issue here https://github.com/elm/core/issues/998)

Apollo GraphQL: Child Component Re-Runs Parent Query?

I've got a parent component with an Apollo query attached:
const withData = graphql(MY_QUERY, {
options({ userID }) {
return {
variables: { _id: userID}
};
},
props({ data: { loading, getOneUser } }) {
return { loading, getOneUser };
},
});
export default compose(
withData,
withApollo
)(NavigatorsList);
export { getOneUser_QUERY };
I've got a child component called userPhoto embedded in the render function:
return (
<div>
<userPhoto />
[.....]
</div>
)
Without the child component, the withData GraphQL function runs twice, once for loading == true, and one more time with the data returned.
With the child component included, the withData GraphQL function runs three times. The third time getOneUser is undefined and my component throws an error.
How can I correct this?
Thanks in advance to all for any info.
Fixed. There was a syntax error in the child component that wasn't throwing an error, but was causing the query to run twice + assorted other anomalies.

Polymer Dart Testing with 1.0

I try to test a polymer-component written in dart using the "test" package, following the instructions on https://pub.dartlang.org/packages/test.
In pubspec.yaml, I set my transformers like this:
transformers:
- web_components:
entry_points:
- web/foo.html
- test/my_component_test.html
- reflectable:
entry_points:
- web/foo.dart
- test/pub_serve:
$include:
- test/**_test{.*,}.dart
my_component_test.html looks like this:
<!doctype html>
<html>
<head>
<title>Custom HTML Test</title>
<link rel="x-dart-test" href="my_component_test.dart">
<script src="packages/test/dart.js"></script>
</head>
<body><my-component id="abc"></my-component></body>
</html>
my_component_test.dart like this:
#TestOn("browser")
import "dart:html";
import "package:test/test.dart";
import 'package:polymer/polymer.dart';
import '../web/my_component.dart';
void main() {
MyComponent componentUnderTest;
setUpAll(() {
return initPolymer();
});
setUp(() {
componentUnderTest = document.body.querySelector("#abc");
});
test("my-component should be initialized correctly", () {
Element heading = componentUnderTest.shadowRoot.querySelector('h1');
expect(heading.text, equals('This is my component'));
});
}
If I try to run the tests using pub serve and pub run test:test --pub-serve=8081 -p firefox in two separate terminals, there are two different errors:
[Error from WebComponents on polymer_dart_example|test/my_component_test.html]:
Unable to import `polymer_dart_example/web/my_component.dart` from polymer_dart_example|test/my_component_test.bootstrap.initialize.dart.
[Error from WebComponents on polymer_dart_example|test/my_component_test.html]:
Can't import `polymer_dart_example|web/my_component.dart` from `polymer_dart_example|test/my_component_test.bootstrap.initialize.dart`
Build error:
Transform WebComponents on polymer_dart_example|test/my_component_test.html threw error: Could not format because the source could not be parsed:
line 16, column 1 of <unknown>: Directives must appear before any declarations
import 'package:polymer/src/common/polymer_register.dart' as i13;
while compiling and
NullError: receiver is undefined
package:web_components/src/init.dart 31:1 J.$asx
package:polymer/init.dart 28:22 <fn>
package:stack_trace init.<fn>.<fn>
package:polymer/init.dart 31:3 dart<._setUpPropertyChanged
package:path/src/style/windows.dart 95:71 dart<.initPolymer.<fn>
===== asynchronous gap ===========================
while testing (caused by initPolymer()).
I suspect there is something wrong with my configuration, but I can't figure out what the problem is. The component itself is working fine.
test/my_component_test.dart needs to be added to the reflectable transformer.
I'm also pretty sure await initPolymer(); needs to be outside the setUpAll() method, at least there is no reason to wrap it with setUpAll().
void main() async {
MyComponent componentUnderTest;
await return initPolymer();
...
test(...);
group(...);
}
Related issue https://github.com/dart-lang/polymer-dart/issues/649

Sorting an array and saving to Mongo doesn't seem to trigger reactivity on my page

I have a template that I am loading from a route like so:
this.route('formEdit', {
path: '/admin/form/:_id',
data: function() { return Forms.findOne({_id: this.params._id}); },
onBeforeAction: function() { AccountUtils.authenticationRequired(this, ['ADMIN']); }
});
In which I have a template defined like:
<template name="formEdit">
<div id="formContainer">
...
{{#each header_fields}}
<div class="sortable">
{{> headerFieldViewRow }}
</div>
{{/each}}
</div>
</template>
And:
<template name="headerFieldViewRow">
{{#with header_field}}
...
{{/with}}
</template>
I then make the container around all the header fields sortable using jQuery UI Sortable:
Template.formEdit.rendered = function() {
$('.sortable').sortable({
axis: "y",
stop: function(event, ui) {
var form = Blaze.getData($('#formContainer')[0]);
var newFormHeaders = [];
$('#headerFieldsTable div.headerField').each(function(idx, headerFieldDiv) {
var header = Blaze.getData(headerFieldDiv);
header.sequence = idx;
Meteor.call('saveHeaderField', header);
newFormHeaders.push({header_field_id: header._id});
});
form.header_fields = newFormHeaders;
Meteor.call('saveForm', form);
}
});
}
Basically, when sorting stops, loop through all the headers, getting the data for each and updating the sequence number, then re-build the array in Forms and save them back. In the server code I have printouts for the two save calls, and the do properly print out the correct order of both the headers and the form.
The problem I am running into is that, after sorting, the visual display of the form and it's headers "snaps" back to the pre-sorted state, even though the data in the DB is correct. If I simply reload the form, either by hitting enter in the Address bar or by simply re-loading it from the menu, everything is displayed correctly. It's as if the reactive piece isn't working.
I have noted that I am getting an error when I update the client code in my server log that reads:
=> Client modified -- refreshing
I20141010-18:25:47.017(-4)? Failed to receive keepalive! Exiting.
=> Exited with code: 1
I don't think this is related as I was getting that error prior to adding this sorting code.
Update: Adding code for saveForm and saveHeader
saveForm:
// Saves the Form to the DB
saveForm: function(params) {
// Structure the data to send
var formEntry = {
title: params.title,
client_id: params.client_id,
header_fields: params.header_fields,
form_fields: params.form_fields,
created_by: Meteor.userId(),
created_on: new Date()
};
if (params._id == null) {
console.log("Saving new Form entry: %j", formEntry);
formEntry._id = Forms.insert(formEntry);
} else {
formEntry._id = params._id;
console.log("Updating Form entry: %j", formEntry);
Forms.update({_id: formEntry._id}, formEntry);
}
return formEntry;
}
saveHeader:
// Saves the HeaderField to the DB
saveHeaderField: function(params) {
// Structure the data to send
var headerFieldEntry = {
label: params.label,
field_type: params.field_type,
field_options: params.field_options,
form_id: params.form_id,
required: params.required,
allows_pictures: params.allows_pictures,
record_geo: params.record_geo
};
if (params._id == null) {
console.log("Saving new HeaderField entry: %j", headerFieldEntry);
headerFieldEntry._id = HeaderFields.insert(headerFieldEntry);
} else {
headerFieldEntry._id = params._id;
console.log("Updating HeaderField entry: %j", headerFieldEntry);
HeaderFields.update({_id: headerFieldEntry._id}, headerFieldEntry);
}
return headerFieldEntry;
}
I think the issue here is that Meteor.call will run on the server - you either need to use a callback or invalidate your template, if you want to return a value Meteor.call. From the docs:
On the client, if you do not pass a callback and you are not inside a stub, call will return undefined, and you will have no way to get the return value of the method. That is because the client doesn't have fibers, so there is not actually any way it can block on the remote execution of a method.
There is more info in this answer and this answer and the Meteor.call docs.
Hope that helps!

Listening to the Stream created from List in Dart

I've modified little bit example from tutorial https://www.dartlang.org/docs/tutorials/streams/ by adding item after subscription:
import 'dart:async';
main() {
var data = new List<int>();
var stream = new Stream.fromIterable(data); // create the stream
// subscribe to the streams events
stream.listen((value) { //
print("Received: $value"); // onData handler
}); //
data.add(1);
}
And after running this program I've got:
Uncaught Error: Concurrent modification during iteration: _GrowableList len:1.
Stack Trace:
#0 ListIterator.moveNext (dart:_collection-dev/iterable.dart:315)
#1 _IterablePendingEvents.handleNext (dart:async/stream_impl.dart:532)
#2 _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:661)
#3 _asyncRunCallback (dart:async/schedule_microtask.dart:18)
#4 _createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:11)
#5 _Timer._createTimerHandler._handleTimeout (timer_impl.dart:151)
#6 _Timer._createTimerHandler.<anonymous closure> (timer_impl.dart:166)
#7 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:93)
Putting data.add(1) before adding listener works as expected.
I've checked documentation about Stream and didn't found what I am doing wrong. I was expecting that listener will be fired in best case and just not fired in worst case, but not exception.
Is it expected behavior? If yes, please describe why.
The exception comes from you trying to modify the list while it is iterated over. This is unspecified behaviour in Dart (*), and the used implementation simply chose to throw an exception. While it is obfuscated by the asynchronous stuff happening in Stream.fromIterable, it basically is the same as if you tried to do this:
var data = [1,2,3];
for(var d in data) {
print(d);
data.add(d+10);
}
If you wrapped your data.add in another async call, for example with Timer.run(() => data.add(2)), it would "work". By that, I mean it wouldn't throw an exception.
Received: 2 still would not be printed. The stream will only send the elements that where already in the list at the time new Stream.fromIterable was called. After that, the stream is closed (onDone will be called), and modifications to the original list will not be sent to your listener.
(*) Source: iterator.dart in SDK 1.1.3 -- "If the object iterated over is changed during the iteration, the behavior is unspecified." Why the text on api.dartlang.org is different is beyond me.
EDIT
To answer the question in the comment: One way would be to use a StreamController.
// or new StreamController<int>.broadcast(), if you want to listen to the stream more than once
StreamController s = new StreamController<int>();
// produce periodic errors
new Timer.periodic(new Duration(seconds: 5), (Timer t) {
s.isClosed ? t.cancel() : s.addError("I AM ERROR");
});
// add some elements before subscribing
s.add(6);
s.add(9);
// this will close the stream eventually
new Timer(new Duration(seconds: 20), () => s.close());
// start listening to the stream
s.stream.listen((v) => print(v),
onError: (err) => print("An error occured: $err"),
onDone: () => print("The stream was closed"));
// add another element before the next event loop iteration
Timer.run(() => s.add(4711));
// periodically add an element
new Timer.periodic(new Duration(seconds: 3), (Timer t) {
s.isClosed ? t.cancel() : s.add(0);
});
// one more (will be sent before 4711)
s.add(4);
The List can't be modified while it is iterated over.
You need an iterable that doesn't have this limitation (e.g. custom implementation) for your example.

Resources