-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
it seems that change detection doesn't work in javascript callbacks inside allowInterop #69
Comments
I just saw that change detection works if I call _changeDetectorRef.detectChanges(); import 'dart:async';
import 'dart:html' as html;
import 'dart:js_util';
import 'dart:math';
import 'package:ngdart/angular.dart';
import 'package:rava_frontend/src/shared/directives/value_accessors/custom_form_directives.dart';
// ignore: unused_import
import 'package:rava_frontend/src/shared/js_interop/bootstrap_interop.dart';
import 'package:rava_frontend/src/shared/js_interop/fabric_interop.dart';
import 'package:rava_frontend/src/shared/utils/flatcolor.dart';
class CustomSize {
num width;
num height;
CustomSize({required this.width, required this.height});
}
@Component(
selector: 'cracha-editor-comp',
templateUrl: 'cracha_editor_page.html',
styleUrls: ['cracha_editor_page.css'],
directives: [
routerDirectives,
formDirectives,
//customFormDirectives,
],
)
class CrachaEditorPage
implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
@ViewChild('sidebar')
html.HtmlElement? sidebarElement;
// ignore: unused_field
final ChangeDetectorRef _changeDetectorRef;
@ViewChild('viewport')
html.DivElement? viewport;
@ViewChild('viewportInner')
html.DivElement? viewportInner;
@ViewChild('canvasEl')
html.CanvasElement? canvasEl;
final html.Element nativeElement;
late Canvas canvas;
Rect? stage;
/// tamanho da area de desenho (Prancheta)
// double currentWidth = 637;
// double currentHeight = 1012;
final CustomSize currentSize = CustomSize(width: 637, height: 1012);
CrachaEditorPage(this.nativeElement, this._changeDetectorRef);
StreamSubscription? ssOnResize;
@override
void ngOnInit() {
ssOnResize = html.window.onResize.listen(onResize);
}
@override
void ngAfterContentInit() {}
@override
void ngAfterViewInit() async {
//https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements
//canvasEl.width = nativeElement.
//html.window.getComputedStyleMap();
await Future.delayed(Duration(milliseconds: 20));
// final comStyle = viewportInner!.getComputedStyle();
// print('height: ${comStyle.height}');
// print('width: ${comStyle.width}');
// final rect = viewportInner!.getBoundingClientRect();
// print('height: ${rect.height}');
// print('width: ${rect.width}');
// var width = viewportInner!.offsetWidth;
// var height = viewportInner!.offsetHeight;
setElementCanvasSize();
initFabric();
}
void onResize(e) {
// setCanvasSize();
}
void updateSize() {
final activeObj = canvas.getActiveObject();
// print('updateSize ${activeObj == null}');
// consoleLog(activeObj);
// print(activeObj);
if (activeObj == null) {
stage?.set('width', currentSize.width);
stage?.set('height', currentSize.height);
} else {
activeObj.set('width', currentSize.width);
activeObj.set('height', currentSize.height);
}
canvas.renderAll();
}
void setElementCanvasSize() {
final ele = viewportInner!;
// print('clientHeight: ${ele.clientHeight}');
// print('clientWidth: ${ele.clientWidth}');
canvasEl!.height = ele.clientHeight;
canvasEl!.width = ele.clientWidth;
}
void initFabric() {
canvas = Canvas(canvasEl);
stage = Rect(
jsify({
'left': (canvas.width / 2) -
((currentSize.width * (stage?.scaleX ?? 1)) / 2),
'top': 50,
'width': currentSize.width,
'height': currentSize.height,
'fill': '#fff',
'lockMovementY': true,
'lockMovementX': true,
'selectable': false,
'hoverCursor': 'default',
}),
);
canvas.add(stage);
num zoom = 1;
canvas.on('mouse:wheel', allowInterop((opt) {
final deltaY = opt.e.deltaY;
//var deltaX = opt.e.deltaX;
final mousePoint = canvas.getPointer(opt.e, true);
zoom = canvas.getZoom();
zoom *= pow(0.999, deltaY);
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
//min(max(zoom + (deltaY/150), .5), 6)
//canvas.setZoom(zoom);
canvas.zoomToPoint(Point(mousePoint.x, mousePoint.y), zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
}));
canvas.on('selection:created', allowInterop((obj) {
onSelectObject(obj);
}));
canvas.on('selection:updated', allowInterop((obj) {
onSelectObject(obj);
}));
canvas.on('selection:cleared', allowInterop((event) {
//change detection doesn't work
onUnSelectObject(event);
}));
}
void onUnSelectObject(event) {
//change current width of stage (valid drawing area)
currentSize.width = stage!.getScaledWidth().toDouble();
currentSize.height = stage!.getScaledHeight().toDouble();
_changeDetectorRef.detectChanges();
}
void onSelectObject(event) {
final activeObj = canvas.getActiveObject();
final scaledWidth = activeObj.getScaledWidth();
final scaledHeight = activeObj.getScaledHeight();
currentSize.width = scaledWidth;
currentSize.height = scaledHeight;
_changeDetectorRef.detectChanges();
}
void addObject(String name) {
switch (name) {
case 'rect':
var rect = Rect(
jsify({
'left': (canvas.width / 2) - ((100 * (1)) / 2),
'top': 50,
'width': 100,
'height': 100,
'fill': FlatColor().generateHex2(),
}),
);
canvas.add(rect);
break;
default:
}
}
//https://www.riodasostras.rj.gov.br/cdn/Vendor/limitless/4.0/bs5/template/html/layout_1/full/assets/js/app.js
// Toggle component sidebar
void sidebarComponentToggle(e) {
e.preventDefault();
sidebarElement?.classes.toggle('sidebar-mobile-expanded');
}
@override
void ngOnDestroy() {
ssOnResize?.cancel();
}
}
|
fabric_interop.dart @JS()
library fabric;
import 'dart:html';
import 'package:js/js.dart';
/// canvas = new fabric.Canvas(this.htmlCanvas.nativeElement, {
/// hoverCursor: 'pointer',
/// selection: true,
/// selectionBorderColor: 'blue',
/// isDrawingMode: true
/// });
@JS('fabric.Canvas')
class Canvas {
external Canvas(Element? element, [config]);
external add(dynamic element);
external renderAll();
external on(String event, dynamic func);
external Point getPointer(Event e, [bool ignoreZoom]);
external num getZoom();
external Canvas zoomToPoint(Point point, num value);
external Canvas setZoom(num value);
external dynamic /*Object|Null*/ getActiveObject();
external get width;
external get height;
}
@anonymous
@JS()
abstract class IObjectOptions {
external num get width;
external set width(num v);
external num get height;
external set height(num v);
external num get scaleX;
/// rect.set('fill', 'red');
/// rect.set({ strokeWidth: 5, stroke: 'rgba(100,200,200,0.5)' });
/// rect.set('angle', 15).set('flipY', true);
external set(dynamic propNameOrMap, dynamic val);
external scaleToWidth(dynamic val);
external scaleToHeight(dynamic val);
external num getScaledWidth();
external num getScaledHeight();
}
@JS('fabric.Rect')
class Rect extends IObjectOptions {
external Rect(options);
}
@JS('fabric.Circle')
class Circle extends IObjectOptions {
external Circle(options);
}
@JS('fabric.Triangle')
class Triangle extends IObjectOptions {
external Triangle(options);
}
@JS("fabric.Point")
class Point {
external num x;
external num y;
external factory Point(num x, num y);
}
|
The temporary solution is to use detectChanges() or create a variable member of the component to obtain the custom angular zone and use this zone (Zone.run) when changing a property linked to the template within a function called by javascript // ignore_for_file: deprecated_member_use
import 'dart:async';
import 'dart:html' as html;
import 'dart:js_util';
import 'dart:math';
import 'package:ngdart/angular.dart';
import 'package:rava_frontend/src/shared/directives/value_accessors/custom_form_directives.dart';
// ignore: unused_import
import 'package:rava_frontend/src/shared/js_interop/bootstrap_interop.dart';
import 'package:rava_frontend/src/shared/js_interop/fabric_interop.dart';
import 'package:rava_frontend/src/shared/utils/flatcolor.dart';
class CustomSize {
num width;
num height;
CustomSize({required this.width, required this.height});
}
@Component(
selector: 'cracha-editor-comp',
templateUrl: 'cracha_editor_page.html',
styleUrls: ['cracha_editor_page.css'],
directives: [
routerDirectives,
formDirectives,
//customFormDirectives,
],
)
class CrachaEditorPage
implements OnInit, AfterContentInit, AfterViewInit, OnDestroy {
@ViewChild('sidebar')
html.HtmlElement? sidebarElement;
// ignore: unused_field
final ChangeDetectorRef _changeDetectorRef;
@ViewChild('viewport')
html.DivElement? viewport;
@ViewChild('viewportInner')
html.DivElement? viewportInner;
@ViewChild('canvasEl')
html.CanvasElement? canvasEl;
final html.Element nativeElement;
late Canvas canvas;
Rect? stage;
Zone angularZone = Zone.current;
/// tamanho da area de desenho (Prancheta)
// double currentWidth = 637;
// double currentHeight = 1012;
final CustomSize currentSize = CustomSize(width: 637, height: 1012);
CrachaEditorPage(this.nativeElement, this._changeDetectorRef);
StreamSubscription? ssOnResize;
@override
void ngOnInit() {
ssOnResize = html.window.onResize.listen(onResize);
}
@override
void ngAfterContentInit() {}
@override
void ngAfterViewInit() async {
//https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements
//canvasEl.width = nativeElement.
//html.window.getComputedStyleMap();
await Future.delayed(Duration(milliseconds: 20));
// final comStyle = viewportInner!.getComputedStyle();
// print('height: ${comStyle.height}');
// print('width: ${comStyle.width}');
// final rect = viewportInner!.getBoundingClientRect();
// print('height: ${rect.height}');
// print('width: ${rect.width}');
// var width = viewportInner!.offsetWidth;
// var height = viewportInner!.offsetHeight;
setElementCanvasSize();
initFabric();
}
void onResize(e) {
// setCanvasSize();
}
void updateSize() {
final activeObj = canvas.getActiveObject();
// print('updateSize ${activeObj == null}');
// consoleLog(activeObj);
// print(activeObj);
if (activeObj == null) {
stage?.set('width', currentSize.width);
stage?.set('height', currentSize.height);
} else {
activeObj.scaleToWidth(currentSize.width);
activeObj.scaleToHeight(currentSize.height);
// img.scaleToWidth(canvas.width / 2);
}
canvas.renderAll();
}
void setElementCanvasSize() {
final ele = viewportInner!;
// print('clientHeight: ${ele.clientHeight}');
// print('clientWidth: ${ele.clientWidth}');
canvasEl!.height = ele.clientHeight;
canvasEl!.width = ele.clientWidth;
}
void initFabric() {
canvas = Canvas(canvasEl);
stage = Rect(
jsify({
'left': (canvas.width / 2) -
((currentSize.width * (stage?.scaleX ?? 1)) / 2),
'top': 50,
'width': currentSize.width,
'height': currentSize.height,
'fill': '#fff',
'lockMovementY': true,
'lockMovementX': true,
'selectable': false,
'hoverCursor': 'default',
}),
);
canvas.add(stage);
num zoom = 1;
canvas.on('mouse:wheel', allowInterop((opt) {
final deltaY = opt.e.deltaY;
//var deltaX = opt.e.deltaX;
final mousePoint = canvas.getPointer(opt.e, true);
zoom = canvas.getZoom();
zoom *= pow(0.999, deltaY);
if (zoom > 20) zoom = 20;
if (zoom < 0.01) zoom = 0.01;
//min(max(zoom + (deltaY/150), .5), 6)
//canvas.setZoom(zoom);
canvas.zoomToPoint(Point(mousePoint.x, mousePoint.y), zoom);
opt.e.preventDefault();
opt.e.stopPropagation();
}));
canvas.on('selection:created', allowInterop((obj) {
onSelectObject(obj);
}));
canvas.on('selection:updated', allowInterop((obj) {
onSelectObject(obj);
}));
canvas.on('selection:cleared', allowInterop((event) {
onUnSelectObject(event);
}));
canvas.on('object:modified', allowInterop((e) => onUnSelectObject(e)));
}
void onUnSelectObject(event) {
//change current width of stage (valid drawing area)
currentSize.width = stage!.width;
currentSize.height = stage!.height;
_changeDetectorRef.detectChanges();
}
void onModifiedObject(event) {
print('onModifiedObject $event');
consoleLog(event);
}
void onSelectObject(event) {
// print('onSelectObject Zone.current ${Zone.current} | angularZone: ${angularZone}');
final activeObj = canvas.getActiveObject();
final scaledWidth = activeObj.getScaledWidth();
final scaledHeight = activeObj.getScaledHeight();
//execute code on Zone of Angular
angularZone.run(() {
currentSize.width = scaledWidth;
currentSize.height = scaledHeight;
});
//_changeDetectorRef.detectChanges();
}
void addObject(String name) {
switch (name) {
case 'rect':
final item = Rect(
jsify({
'left': (canvas.width / 2) - ((100 * (1)) / 2),
'top': 50,
'width': 100,
'height': 100,
'fill': FlatColor().generateHex2(),
}),
);
canvas.add(item);
break;
case 'circle':
final item = Circle(
jsify({
'left': (canvas.width / 2) - ((100 * (1)) / 2),
'top': 100,
'radius': 50,
'fill': FlatColor().generateHex2(),
}),
);
canvas.add(item);
break;
default:
}
}
//https://www.riodasostras.rj.gov.br/cdn/Vendor/limitless/4.0/bs5/template/html/layout_1/full/assets/js/app.js
// Toggle component sidebar
void sidebarComponentToggle(e) {
e.preventDefault();
sidebarElement?.classes.toggle('sidebar-mobile-expanded');
}
@override
void ngOnDestroy() {
ssOnResize?.cancel();
}
}
|
@GZGavinZhao @ykmnkmi |
You can't call |
@GZGavinZhao @ykmnkmi
Which ng* package(s) are the source of the bug?
ngdart
Which operating system(s) does this bug appear on?
Windows
Which browser(s) does this bug appear on?
Chrome 117.0.5938.132 64 bits
Is this a regression?
No
Description
I'm using allowInterop to interact with JavaScript code, more specifically I'm creating an AngularDart application that uses Fabric.js to create a Badge Creator (Professional Employee ID Card), so I'm having a problem because it seems that AngularDart doesn't detect changes in variables What do I change inside allowInterop
Please provide the steps to reproduce the bug
Please provide the exception or error you saw
Please provide the dependency environment you discovered this bug in (run
dart pub deps -s compact
)Anything else?
No response
The text was updated successfully, but these errors were encountered: