I asked to fix an issue in software which use dart to develop a chrome application. currently, the chrome application doesn't run in my mac osx and I get the following exception:
Uncaught Unhandled exception:
Class '_InternalLinkedHashMap' has no instance method 'pushState'.
NoSuchMethodError: method not found: 'pushState'
Receiver: _LinkedHashMap len:0
Arguments: [null, "", "/index.html#events"]
#0 Object._noSuchMethod (dart:core-patch/object_patch.dart:42)
#1 Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#2 Router._go (package:route/client.dart:153:22)
#3 Router.gotoPath (package:route/client.dart:139:7)
#4 Router.listen.<anonymous closure> (package:route/client.dart:111:13)
#5 wrap_event_listener.<anonymous closure>.<anonymous closure> (dart:html:1189)VM81:1 (anonymous function)
After I backtrace the issue, I found that listen() function instead of calling handle calls gotoPath(string,string). Can someone explain me that. this cause
[https://github.com/justinfagnani/route/blob/master/lib/client.dart][1]
void gotoPath(String path, String title) {
_logger.finest('gotoPath $path');
var url = _getUrl(path);
if (url != null) {
_go(path, title);
// If useFragment, onHashChange will call handle for us.
if (!_listen || !useFragment) {
_handlers[url](path);
}
}
}
void handle(String path) {
_logger.finest('handle $path');
var url = _getUrl(path);
if (url != null) {
// always give handlers a non-fragment path
var fixedPath = url.reverse(url.parse(path));
_handlers[url](fixedPath);
} else {
_logger.info("Unhandled path: $path");
}
}
this calls from here:
Note that other methods if/else call handle and only the !ignoreclick is call gotopath
void listen({bool ignoreClick: false}) {
_logger.finest('listen ignoreClick=$ignoreClick useFragment=$useFragment');
if (_listen) {
throw new StateError('listen should be called once.');
}
_listen = true;
if (useFragment) {
window.onHashChange.listen((_) {
var path = '${window.location.pathname}${window.location.hash}';
_logger.finest('onHashChange handle($path)');
return handle(path);
});
handle('${window.location.pathname}${window.location.hash}');
} else {
window.onPopState.listen((_) {
var path = '${window.location.pathname}${window.location.hash}';
_logger.finest('onPopState handle($path)');
handle(path);
});
}
if (!ignoreClick) {
window.onClick.listen((e) {
if (e.target is AnchorElement) {
AnchorElement anchor = e.target;
if (anchor.host == window.location.host) {
var fragment = (anchor.hash == '') ? '' : '${anchor.hash}';
gotoPath("${anchor.pathname}$fragment", anchor.title);
e.preventDefault();
}
}
});
}
}
Related
I'm using flutter-sound to record (and then play back) some audio in my flutter app. However, I've run up against an interesting problem: On the iOS emulator it works, but on the actual iOS device I get an obscure error. Both are running on the same version of iOS (15.4).
Here is the code for starting and stopping the recording/playback, it's simple enough:
Future<void> startRecording(GlobalState curState) async {
setState(() {
recording = true;
});
curState.startRecording();
Directory directory = Directory(pathToAudio);
if (filePathText.isNotEmpty && File(filePathText).existsSync()) {
File(filePathText).deleteSync();
}
if (!directory.existsSync()) {
directory.createSync();
}
await _recordingSession.startRecorder(
toFile: (filePathName()),
codec: (Platform.isIOS ? Codec.pcm16WAV : Codec.aacMP4),
audioSource: AudioSource.microphone,
);
}
Future<void> stopRecording(GlobalState curState) async {
setState(() {
recording = false;
});
String? fileURL = await _recordingSession.stopRecorder();
print("the file is recorded!!!");
print("FILEPATH:");
print(fileURL);
curState.stopRecording();
if (fileURL != null) {
filePathText = fileURL;
if (widget.widgetControlInfo.onChanged != null) {
FileAnswer tempA = FileAnswer.fromBasicQuestion(widget.currentQuestion);
tempA.filePath = fileURL;
tempA.filetype = FileType.recording;
if (widget.widgetControlInfo.onChanged != null) {
widget.widgetControlInfo.onChanged!(tempA);
}
}
} else {
print('sumn went rong wit da recording');
}
}
String filePathName() =>
pathToAudio +
DateTime.now().month.toString() +
DateTime.now().day.toString() +
DateTime.now().hour.toString() +
DateTime.now().minute.toString() +
DateTime.now().second.toString() +
(Platform.isIOS ? ".wav" : ".m4a");
Future<void> playControl() async {
if (playing) {
await stopPlaying();
} else {
await startPlaying();
}
}
Future<void> startPlaying() async {
setState(() {
playing = true;
});
if (filePathText.isEmpty) {
return;
} else {
if (File(filePathText).existsSync()) {
print("the file existssss!!!");
print("FILEPATH:");
print(filePathText);
}
await _playingSession.startPlayer(
fromURI: filePathText,
codec: (Platform.isIOS ? Codec.pcm16WAV : Codec.aacMP4),
whenFinished: () {
print("its over");
stopPlaying();
});
}
return;
}
Future<void> stopPlaying() async {
setState(() {
playing = false;
});
await _playingSession.stopPlayer();
}
void _initializer() async {
if (Platform.isIOS) {
var directory = await getTemporaryDirectory();
print("TIS IOS");
pathToAudio = directory.path + '/';
} else {
pathToAudio = '/sdcard/Download/m-Path/';
}
_recordingSession = new FlutterSoundRecorder(logLevel: Level.debug);
_playingSession = new FlutterSoundPlayer(logLevel: Level.debug);
await _recordingSession.openRecorder();
await _playingSession.openPlayer();
await _recordingSession
.setSubscriptionDuration(Duration(milliseconds: 10))
.then((value) => null);
_recorderSubscription = _recordingSession.onProgress!.listen((e) {
setState(() {
_timerText = e.duration.toString().substring(0, 10);
});
});
await _playingSession.setSubscriptionDuration(Duration(milliseconds: 10));
_playerSubscription = _playingSession.onProgress!.listen((e) {
setState(() {
_timerText = e.position.toString().substring(0, 10);
});
});
await Permission.microphone.request();
await Permission.storage.request();
await Permission.manageExternalStorage.request();
}
Here's what the UI portion looks like. On the iOS emulator, when I press the recording button, the timer starts incrementing, and after I press it again, I can press the play button to listen what I just recorded. On the device, when I press the button, the timer doesn't increment but stays at zero, and when I try to play the audio, I get the following error:
[VERBOSE-2:ui_dart_state.cc(209)] Unhandled Exception: PlatformException(Audio Player, startPlayer failure, null, null)
#0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:607:7)
#1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:177:18)
<asynchronous suspension>
#2 MethodChannelFlutterSoundPlayer.invokeMethod (package:flutter_sound_platform_interface/method_channel_flutter_sound_player.dart:157:12)
<asynchronous suspension>
#3 FlutterSoundPlayer._startPlayer (package:flutter_sound/public/flutter_sound_player.dart:819:19)
<asynchronous suspension>
#4 FlutterSoundPlayer.startPlayer.<anonymous closure> (package:flutter_sound/public/flutter_sound_player.dart:759:11)
<asynchronous suspension>
#5 BasicLock.synchronized (package:synchronized/src/basic_lock.dart:33:16)
<asynchronous suspension>
#6 FlutterSoundPlayer.startPlayer (package:flutter_sound/public/flutter_sound_player.dart:758:5)
<asynchronous suspension>
#7 _RecordingQuestionWidgetS.startPlaying (package:flutter_app2/interactions/widgets/questionWidgets/RecordingQuestionWidget.dart:216:7)
<asynchronous suspension>
#8 _RecordingQuestionWidgetS.playControl (package:flutter_app2/interactions/widgets/questionWidgets/RecordingQuestionWidget.dart:200:7)
<asynchronous suspension>
I asked the flutter-sound author on github already, but it seems he doesn't really know what's wrong either, and the fact that it works on the simulator but not on the device makes me think the problem might be larger than just some faulty code.
A different part of the same app already saves and shows saved images from the same directory so I don't think it's a permission issue.
Apparently, on iOS, this doesn't work unless you create an AudioSession instance (from this package). The new initializer function looks like this:
void _initializer() async {
if (Platform.isIOS) {
var directory = await getApplicationDocumentsDirectory();
pathToAudio = directory.path + '/';
} else {
pathToAudio = '/sdcard/Download/appname/';
}
_recordingSession = new FlutterSoundRecorder();
_playingSession = new FlutterSoundPlayer();
await _recordingSession.openRecorder();
await _playingSession.openPlayer();
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration(
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionCategoryOptions:
AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.defaultToSpeaker,
avAudioSessionMode: AVAudioSessionMode.spokenAudio,
avAudioSessionRouteSharingPolicy:
AVAudioSessionRouteSharingPolicy.defaultPolicy,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
androidAudioAttributes: const AndroidAudioAttributes(
contentType: AndroidAudioContentType.speech,
flags: AndroidAudioFlags.none,
usage: AndroidAudioUsage.voiceCommunication,
),
androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
androidWillPauseWhenDucked: true,
));
await _recordingSession
.setSubscriptionDuration(Duration(milliseconds: 10))
.then((value) => null);
_recorderSubscription = _recordingSession.onProgress!.listen((e) {
setState(() {
_timerText = e.duration.toString().substring(0, 10);
});
});
await _playingSession.setSubscriptionDuration(Duration(milliseconds:
10));
_playerSubscription = _playingSession.onProgress!.listen((e) {
setState(() {
_timerText = e.position.toString().substring(0, 10);
});
});
await Permission.microphone.request();
await Permission.storage.request();
await Permission.manageExternalStorage.request();
}
Any idea what is wrong here?
I have a function which receive data from server by a rest call, which return Future object.
Future _function(){
var future = _getWithID('path');
future.then((data) {
List<SOMEOBJECT> SOMEOBJECT = data["entities"].map((v) {
return new SOMEOBJECT(
id: v["properties"]["id"],
name: titleize(v["properties"]["name"])
);
}).toList();
return new Future.value(SOMEOBJECT.process(SOMEOBJECT));
});
}
Model class for SOMEOBJECT
class SOMEOBJECT extends Model {
int id;
String name;
SOMEOBJECT({this.id, this.name});
static Map process(List<String> SOMEOBJECTS) {
// map = {name: A, value:[{name:list<String>},{name:list<String>}]}
return map;
}
}
Cache object which try to cache in browser
class CacheManager {
Map callbacks;
Map cache;
CacheManager(this.cache){
callbacks = {};
}
Future setData(String key, Function updateFunction) {
return chrome.storage.local.get(key).then( (resp){
cache[key] = resp[key];
return updateFunction().then((data) {
chrome.storage.local.set({key: data});
cache[key] = data;
}).whenComplete( () {
handleCallbacks(key);
});
});
}
void registerCallback(String key, Function callback) {
callbacks[key] = callback;
}
void handleCallbacks(String key){
if (callbacks[key] != null){
callbacks[key](cache[key]);
}
}
}
So I have these two lines before
cacheManager.registerCallback("SOMEOBJECT", loadSomeOBJECT);
cacheManager.setData('SOMEOBJECT', api._function);
and I am getting this error:
ERROR
NoSuchMethodError: method not found: 'then'
Receiver: null
Arguments: [Closure: (dynamic) => dynamic]
#0 Object._noSuchMethod (dart:core-patch/object_patch.dart:42)
#1 Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#2 CacheManager.setData.<anonymous closure> (chrome-extension://ekfcndmmkincdeoolhcebmhcgmkmadip/helpers.dart:27:31)
#3 _RootZone.runUnary (dart:async/zone.dart:1149)
#4 _Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:551)
#5 _Future._propagateToListeners (dart:async/future_impl.dart:637)
#6 _Future._completeWithValue (dart:async/future_impl.dart:424)
#7 _Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:479)
#8 _microtaskLoop (dart:async/schedule_microtask.dart:41)
#9 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)
#10 _ScheduleImmediateHelper._handleMutation (dart:html:49254)
#11 MutationObserver._create.<anonymous closure> (dart:html:27525)
this refer to the line which setData and callback from cache object. then the cache object will call the api.function to get the data from server, and then the raw data is served, it goes to process method in SOMEOBJECT class and return the MAP of JSON representation. Once the data back to cache manager to call the then on the future object it fails. with the error on the question. Any idea?
Thanks
I just had a brief look and saw
Future _function(){
var future = _getWithID('path');
return future.then((data) {
is missing a return. There might be other issues though.
I have a viewmodel which consists of a list(foreach loop) of DoctorPrices and when clicking on an item in the list it open up a CRUD form on the side. However when i update the values on the CRUD the observableArray that is bound to the foreach is not refreshing? (although the values are updates in the DB correctly)
From my data access module i call the following query.
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
return manager.executeQueryLocally(query);
}
In my viewmodel i have the following code:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
services is bound using a foreach loop (not posting here as the code is simple and works)
When i click on a one of the DoctorPrices it gets the data as follows and places it in an observable:
this.selectedPrice = function (data, event) {
self.currentService(data);
self.showEdit(true);
};
I then bind selectPrice to a simple form that has the properties on it to be modified by the user. I then call manager.SaveChanges().
This results in the following problem: the value is being updated correctly but the GUI / Original List that is bound in the foreach is not being updated? Are the properties in breeze not observables? What is the best way to work with something like this.
I thought of a workaround and changing the code with something like this:
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
});
But i feel that clearing the array in that way is sloppy and not the right way of doing things specially with long lists.
Can someone please point me in the right direction on how to bind observableArray properties properly so they are updated?
Additional code my VM Component:
function services() {
var self = this;
this.showForm = ko.observable(false);
this.currentService = ko.observable();
this.services = ko.observableArray(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
this.title = ko.observable();
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
self.showDetails(false);
});
this.show = function (value) {
self.showForm(value);
};
this.showDetails = ko.observable(false);
this.addNewService = function() {
self.currentService(doctorServices.createService(doctorList.viewModel.instance.currentDoctorID()));
console.log(self.currentService().entityAspect.entityState);
self.showDetails(true);
};
this.showDelete = ko.computed(function() {
if (self.currentService() == null)
return false;
else if (self.currentService().entityAspect.entityState.isDetached()) {
self.title('Add new service');
return false;
} else {
self.title('Edit service');
return true;
}
});
this.deleteService = function() {
self.currentService().entityAspect.setDeleted();
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
};
this.closeDetails = function () {
doctorServices.manager.rejectChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.showDetails(false);
};
this.selectService = function (data, event) {
self.currentService(data);
self.showDetails(true);
};
this.saveChanges = function () {
console.log(self.currentService().entityAspect.entityState);
if (self.currentService().entityAspect.entityState.isDetached()) {
doctorServices.attachEntity(self.currentService());
}
console.log(self.currentService().entityAspect.entityState);
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.currentService.notifySubscribers();
self.showDetails(true);
};
}
return {
viewModel: {
instance: new services()
},
template: servicesTemplate,
};
Below is my Breeze Data Class:
define('data/doctorServices', ['jquery', 'data/dataManager', 'knockout','mod/medappBase', 'breeze', 'breeze.savequeuing'], function ($, manager, ko,base, breeze, savequeuing) {
var services = ko.observableArray([]);
return {
attachEntity:attachEntity,
getServices: getServices,
services: services,
manager:manager,
getDoctorServices: getDoctorServices,
getServiceById: getServiceById,
createService:createService,
hasChanges: hasChanges,
saveChanges: saveChanges
};
function getServices() {
var query = breeze.EntityQuery.from("DoctorPrices");
return manager.executeQuery(query).then(function (data) {
services(data.results);
}).fail(function (data) {
console.log('fetch failed...');
console.log(data);
});;
}
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
var set = manager.executeQueryLocally(query);
return set;
}
function getServiceById(serviceId) {
return manager.createEntity('DoctorPrice', serviceId);
//return manager.getEntityByKey('DoctorPrice', serviceId);
}
function handleSaveValidationError(error) {
var message = "Not saved due to validation error";
try { // fish out the first error
var firstErr = error.innerError.entityErrors[0];
message += ": " + firstErr.errorMessage;
base.addNotify('error', 'Could not save.', message);
} catch (e) { /* eat it for now */ }
return message;
}
function hasChanges() {
return manager.hasChanges();
}
function attachEntity(entity) {
manager.addEntity(entity);
}
function createService(doctorId) {
return manager.createEntity('DoctorPrice', { DoctorPricingID: breeze.core.getUuid(), DoctorID:doctorId }, breeze.EntityState.Detached);
};
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
base.addNotify('success', 'Saved.', 'Your updates have been saved.');
}
function saveFailed(error) {
var reason = error.message;
var detail = error.detail;
if (error.innerError.entityErrors) {
reason = handleSaveValidationError(error);
} else if (detail && detail.ExceptionType &&
detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
// Concurrency error
reason =
"Another user, perhaps the server, " +
"may have deleted one or all of the settings." +
" You may have to restart the app.";
} else {
reason = "Failed to save changes: " + reason +
" You may have to restart the app.";
}
console.log(error);
console.log(reason);
}
}
});
Please note this is my frist attempt at both a data class and VM. At the moment i am relying heavily on clearing the array ([]) and using notifySubscribers to make the array refresh :(
I bet you're missing an observable somewhere. I can't tell because you keep hopping from property to property whose definition is not shown.
For example, I don't know how you defined this.currentService.
I'm confused by this:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
Why is it a ko.computed? Why not just make it an observable array.
self.service = ko.observableArray();
// ... later replace the inner array in one step ...
self.service(doctorServices.getDoctorServices(
doctorList.viewModel.instance.currentDoctorID()));
I urge you to follow the observability trail, confident that your Breeze entity properties are indeed observable.
vm.selectedPrice = ko.dependentObservable(function () {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
}, vm);
vm is ur model on which u applied bindings , try this it will work.
Two questions:
Looking at the highlighted line in https://github.com/bwu-dart/bwu_datagrid/blob/master/example/src/composite_editor_item_details/app_element.dart#L120
1) why does the line
var idx = e.validationResults.errors.length;
always throws an error?
Exception: Uncaught Error: The null object does not have a getter 'length'.
NoSuchMethodError: method not found: 'length'
Receiver: null
Arguments: []
Stack Trace:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:45)
#1 validationErrorHandler (http://localhost:8080/epimss_design.html.12.dart:184:42)
#2 _RootZone.runUnaryGuarded (dart:async/zone.dart:1020)
#3 _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:341)
#4 _BufferingStreamSubscription._add (dart:async/stream_impl.dart:270)
#5 _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:346)
#6 _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:237)
#7 EventBus.fire (package:bwu_datagrid/core/event_bus.dart:61:19)
#8 _commitCurrentEdit (package:bwu_datagrid/bwu_datagrid.dart:3626:25)
#9 EditorLock.commitCurrentEdit (package:bwu_datagrid/core/range.dart:235:82)
#10 BwuDatagrid._commitEditAndSetFocus (package:bwu_datagrid/bwu_datagrid.dart:3045:40)
#11 _handleKeyDown (package:bwu_datagrid/bwu_datagrid.dart:2632:39)
The same thing happens for other properties as well such as field and column etc.
2) How can I test that the validationResult returns true? The error handler seems to only fires when there is an ValidationError.
My validator is shown below
import 'package:bwu_datagrid/datagrid/helpers.dart' show Column, GridOptions,
MapDataItem, MapDataItemProvider;
import 'package:bwu_datagrid/bwu_datagrid.dart' show BwuDatagrid;
import 'package:bwu_datagrid/formatters/formatters.dart' show CheckmarkFormatter;
import 'package:bwu_datagrid/editors/editors.dart' show CheckboxEditor, EditorArgs,
IntegerEditor, TextEditor;
import 'package:bwu_datagrid/core/core.dart' show AddNewRow, ActiveCellChanged,
ItemBase, ValidationError;
import 'package:bwu_datagrid/plugins/row_selection_model.dart' show RowSelectionModel;
import 'package:epimss_podo/reg.dart' show Email, EMAIL_FORM_EVENT;
import 'package:epimss_shared/shared.dart' show toggleCoreCollapse, onBwuCellChangeHandler;
import 'package:epimss_shared/validators.dart' show BwuRequiredEmailValidator,
BwuRequiredNounValidator;
Custom validators:
const String REQUIRED_EMAIL_REGEX = r"\b[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}\b";
const String REQUIRED_NOUN_REGEX = r"\b[a-z'-]{2,}\b";
final RegExp _requiredNounValidator = new RegExp( REQUIRED_NOUN_REGEX, caseSensitive: false );
bool isRequiredNounValid( String property ) =>
_requiredNounValidator.hasMatch( property );
final RegExp _requiredEmailPropertyValidator = new RegExp( REQUIRED_EMAIL_REGEX, caseSensitive: false );
bool isRequiredEmailPropertyValid( String property ) => _requiredEmailPropertyValidator.hasMatch( property );
class BwuRequiredEmailValidator extends bwu.Validator {
bwu.ValidationResult call( dynamic value ) {
if ( isRequiredEmailPropertyValid( value ) ) {
return new bwu.ValidationResult( true );
} else {
return new bwu.ValidationResult( false, 'Valid email address required.' );
}
}
}
class BwuRequiredNounValidator extends bwu.Validator {
bwu.ValidationResult call( dynamic value ) {
if ( isRequiredNounValid( value) ) {
return new bwu.ValidationResult( true );
} else {
return new bwu.ValidationResult( false, 'Valid noun is required.' );
}
}
}
Validation error handler:
void validationErrorHandler( ValidationError e ) {
//print ( e.validationResults.errors.length );
print ( e.column.field );
if ( e.validationResults.isValid )
print( 'retVal is true' );
else
print( 'retVal is false' );
errorMsg = e.validationResults.message;
var editor = e.editor;
print ( 'valResult valid |' + e.validationResults.isValid.toString() );
var result = e.validationResults;
if ( e.validationResults.isValid ) {
errorMsg = 'EMAIL';
} else {
errorMsg = result.message;
}
print( editor.runtimeType ); // aslways print TextEditor
if ( editor != null ) {
//var colId = editor.column.id;
if ( editor is TypeEditor ) {
email.isTypeValid = true;
}
if ( editor is AddressEditor ) {
email.isAddressValid = false;
}
//print( encode ( email ) );
}
}
You get the exception because the field e.validationResults.errors is null.
You can't access the length property of null thus the exception is thrown.
The field errors is null because in this call
return new bwu.ValidationResult( false, 'Valid email address required.' );
you didn't pass a value for the optional errors parameter
class ValidationResult {
bool isValid = false;
String message;
List<ValidationErrorSource> errors;
ValidationResult(this.isValid, [this.message, this.errors]);
}
Hint:
As far as I know breakpoints don't work for code within <script> tags (I saw this in the example code I got in an email).
I therefore advise to move your code from the email_form.html file to a email_form.dart file.
Then you can use the debugger and investigate the values at runtime which also helps a lot to learn what others peoples code is actually doing.
Fire custom event from Editor
class AddressEditor extends bwu.TextEditor {
static const VALIDATION_SUCCEEDED = const EventType<ValidationError>(
'custom-validation-succeeded');
...
#override
bwu.ValidationResult validate() {
var result = super.validate();
args.grid.eventBus.fire(AddressEditor.VALIDATION_SUCCEEDED, new ValidationError(this,
editor: this,
cellNode: args.grid.getActiveCellNode(),
validationResults: result,
cell: args.grid.getActiveCell(),
column: column));
return result;
}
// you can register the same handler as for the validation error event
grid.eventBus.onEvent(AddressEditor.VALIDATION_SUCCEEDED).listen(validationErrorHandler);
I have not tried this code but it should work.
I am trying to use dart isolate library to improve my application performance.
Look at following code:
import 'dart:isolate';
import 'package:dbcrypt/dbcrypt.dart';
main() {
var pwConPort = new ReceivePort();
pwConPort.listen((data) {
print(data);
pwConPort.close();
}, onError: (err) {
print(err);
});
Isolate.spawn(generatePasswordConcurrency, pwConPort.sendPort);
}
void generatePasswordConcurrency(SendPort sendPort) {
sendPort.send(_generateHashPassword('Passsowr1222!'));
}
String _generateHashPassword(String password) {
var regex = new RegExp(r'^.*(?=.{7,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).*$');
if (!regex.hasMatch(password)) {
throw new StateError('Errors');
}
return new DBCrypt().hashpw(password, new DBCrypt().gensalt());
}
Everything works fine but i can only pass a static password, or better to say, i don't know, how to pass something dynamically. Here you can see, password is hardcoded, but i want to pass a variable for example.
void generatePasswordConcurrency(SendPort sendPort) {
sendPort.send(_generateHashPassword('Passsowr1222!'));
}
If the method _generateHashPassword will throw an error, how can I handling this error? I try to catch the error on listen method from ReceivePort
pwConPort.listen((data) {
print(data);
pwConPort.close();
}, onError: (err) {
print(err);
});
but still got unhandling exceptions message.
Observatory listening on http://127.0.0.1:51433
in ShutdownIsolate: Unhandled exception:
Bad state: Errors
#0 _generateHashPassword (file:///D:/Dart/samples/bin/isolate_error.dart:26:9)
#1 generatePasswordConcurrency (file:///D:/Dart/samples/bin/isolate_error.dart:19:40)
#2 _startIsolate.isolateStartHandler (dart:isolate-patch/isolate_patch.dart:221)
#3 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:124)
Conclusion my question:
How can I pass a variable to called method on isolate?
How can I handling error on isolate?
First of all,
Isolate are not thread, they are independant process more like a fork() than a thread
dartApi: Isolate
Concurrent programming using isolates:
independent workers that are similar to threads but don't share memory, communicating only via
messages.
So, you can't access to the same variable than your parent process. It's a choice made by the dart team, because it's a mechanism usable when you compile your dart code in js. So it need to be possible in JS
How can I pass a variable to called method on isolate?
To do this, you need to see ReceivePort() like a unidirectionnal way of communication, so to pass variable in two way, you need two.
So on you main process:
pwConPort.listen((data) {
if (isolateSendPort == null && data is SendPort) {
isolateSendPort = data; // Receive the communication object of the isolate
isolateSendPort.send("Passsowr1222!");
} else {
print("Generated password: ${data}");
pwConPort.close();
}
}, onError: (err) {
print("SendPortError: ${err}");
});
});
In you isolate entry point :
sendPort.send(isolateConPort.sendPort);
isolateConPort.listen((data) {
// code ....
});
Note: be careful of what message you send. message send between one process and another need to respect some rules
DartApi: SendPort
The content of message can be: primitive values (null, num, bool,
double, String), instances of SendPort, and lists and maps whose
elements are any of these. List and maps are also allowed to be
cyclic.
How can I handling error on isolate?
Isolate get one method to listen throw error send by the isolate : addErrorListner
That is a useful function.
BUT ! this method is not implement in every plate-forme, so you need to do this in a others.
The way i chose is to send 2 SendPort in the entry point function :
One for the communication
One for the error.
So the spawn function looks like :
Isolate.spawn(generatePasswordConcurrency, [pwConPort.sendPort, errorPort.sendPort])
and the generatePasswordConcurrency :
void generatePasswordConcurrency(List<SendPort> commList) {
var sendPort = commList[0];
var errorPort = commList[1];
var isolateConPort = new ReceivePort();
sendPort.send(isolateConPort.sendPort);
isolateConPort.listen((data) {
try {
sendPort.send(_generateHashPassword(data));
} catch (e) {
errorPort.send("error: ${e.toString()}");
}
});
}
Here the full code :
import 'dart:isolate';
import 'package:dbcrypt/dbcrypt.dart';
main() {
var pwConPort = new ReceivePort();
var errorPort = new ReceivePort();
SendPort isolateSendPort = null;
Isolate.spawn(generatePasswordConcurrency, [pwConPort.sendPort, errorPort.sendPort])
.then((Isolate pcs) {
errorPort.listen((err) {
print("Error: ${err}");
pwConPort.close();
errorPort.close();
});
print(pcs);
pwConPort.listen((data) {
if (isolateSendPort == null && data is SendPort) {
isolateSendPort = data;
isolateSendPort.send("Passsowr1222!");
} else {
print("Generated password: ${data}");
pwConPort.close();
errorPort.close();
//pcs.kill();
}
}, onError: (err) {
print("SendPortError: ${err}");
});
});
}
void generatePasswordConcurrency(List<SendPort> commList) {
var sendPort = commList[0];
var errorPort = commList[1];
var isolateConPort = new ReceivePort();
sendPort.send(isolateConPort.sendPort);
isolateConPort.listen((data) {
try {
sendPort.send(_generateHashPassword(data));
} catch (e) {
errorPort.send("error: ${e.toString()}");
}
});
}
String _generateHashPassword(String password) {
var regex = new RegExp(r'^.*(?=.{7,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).*$');
if (!regex.hasMatch(password)) {
throw new StateError('Errors');
}
return new DBCrypt().hashpw(password, new DBCrypt().gensalt());
}