dart append data to html on future .then() - dart

So i have this code:
import 'dart:html';
import 'dart:json';
class BaseModel {
Map values;
String _url;
// another basic properties
// constructor defined here
fetch() {
var el = document.query('#container');
HttpRequest.getString(_url).then(
(result) {
values = new Map.from(parse(result));
el.innerHtml = values['name'];
return result;
})
}
}
void main() {
BaseModel bm = new BaseModel(url: /path/to/test.json);
bm.fetch();
}
And i have a json data like this:
{
"name" : "Andrew",
"age" : 20
}
I expect to see "Andrew" on the DOM, but i see nothing. If i change the
el.innerHtml = "SOME_TEXT"
then i can see the "SOME_TEXT" text displayed.
Can you guys help me?

What do you see in the JavaScript console if you print the name?
What happens if you add an error handler?
Something along the following lines:
HttpRequest.getString(_url)
.then((result) {
values = new Map.from(parse(result));
print(values['name']);
el.innerHtml = values['name'];
return result;
})
.catchError((e) => print(e));

Related

how to return a string from a function which is listening some stream in dart?

i have a function called foo which is listening to the stdout, what i want is to return some string which i got from stdout. here is my function;
dynamic foo(process) {
return (
process.stdout.transform(UTF8.decoder).listen((data) {
String s = data.toString();
// print(s);
if (s.contains("received event of")) {
var s1 = s.split(":");
print("${s1[1]}");
return s1[1];
}
}));
}
I want to return s1 to the calling function
here a callback function do the trick
foo(process, callback) {
process.stdout.transform(UTF8.decoder).listen((data) {
String s = data.toString();
if (s.contains("received event of")) {
String message = s.split(":")[1];
callback(message);
}
});
}
and here i am calling the method and printing the data which i get get from stream.
foo(process,(data){print(data);})
This should do what you want
Future<String> dynamic foo(process) {
return process.stdout.transform(UTF8.decoder).map((data) {
String s = data.toString();
// print(s);
if (s.contains("received event of")) {
var s1 = s.split(":");
print("${s1[1]}");
return s1[1];
} else {
return null;
}
}).where((val) => val != null).first;
}
Your custom code either returns a valid value or null.
I changed listen to map to be able to use additional stream methods.
where filters invalid values (null) and returns the first non-null value.
The caller of the foo method needs to handle the returned Future (using for example async/await) to get the value when it becomes available.
Use it like
bar() async {
...
var input = await foo(proc);
print(input);
}
I think that everybody wants this:
import 'dart:io';
import 'dart:convert';
// Using system encoding:
var outputStr = await process.stdout.transform(systemEncoding.decoder).join();
// Using UTF-8 encoding:
var outputStr = await process.stdout.transform(utf8.decoder).join();

dart, how to define a class so it can be used as a class attribute?

I've seen in polymer.dart they have:
class CustomTag {
final String tagName;
const CustomTag(this.tagName);
}
but how does that interact with the rest of the code? from just the code above I can't see how using #CustomTag('my-tag') actually does anything but creates a CustomTag which is then garbage collected since nothing is referencing it.
To answer the question in the title; these are called Annotations; they are simply const constructors.
To answer the second question; these are usually used for tooling (eg. #deprecated) or rewriting via a Transformer. You can access them at runtime using mirrors, but that's probably not practical/advisable for a production web app that gets converted to JavaScript.
Here's some sample code taken from this answer
import "dart:mirrors";
void main() {
var object = new Class1();
var classMirror = reflectClass(object.runtimeType);
// Retrieve 'HelloMetadata' for 'object'
HelloMetadata hello = getAnnotation(classMirror, HelloMetadata);
print("'HelloMetadata' for object: $hello");
// Retrieve 'Goodbye' for 'object.method'
var methodMirror = (reflect(object.method) as ClosureMirror).function;
Goodbye goodbye = getAnnotation(methodMirror, Goodbye);
print("'Goodbye' for object: $goodbye");
// Retrieve all 'Goodbye' for 'object.method'
List<Goodbye> goodbyes = getAnnotations(methodMirror, Goodbye);
print("'Goodbye's for object.method': $goodbyes");
// Retrieve all metadata for 'object.method'
List all = getAnnotations(methodMirror);
print("'Metadata for object.method': $all");
}
Object getAnnotation(DeclarationMirror declaration, Type annotation) {
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (reflectee.runtimeType == annotation) {
return reflectee;
}
}
}
return null;
}
List getAnnotations(DeclarationMirror declaration, [Type annotation]) {
var result = [];
for (var instance in declaration.metadata) {
if (instance.hasReflectee) {
var reflectee = instance.reflectee;
if (annotation == null) {
result.add(reflectee);
} else if (reflectee.runtimeType == annotation) {
result.add(reflectee);
}
}
}
return result;
}
#HelloMetadata("Class1")
class Class1 {
#HelloMetadata("method")
#Goodbye("method")
#Goodbye("Class1")
void method() {
}
}
class HelloMetadata {
final String text;
const HelloMetadata(this.text);
String toString() => "Hello '$text'";
}
class Goodbye {
final String text;
const Goodbye(this.text);
String toString() => "Goodbye '$text'";
}
Output:
'HelloMetadata' for object: Hello 'Class1'
'Goodbye' for object: Goodbye 'method'
'Goodbye's for object.method': [Goodbye 'method', Goodbye 'Class1']
'Metadata for object.method': [Hello 'method', Goodbye 'method', Goodbye 'Class1']

How do I create a blank Future in Dart + how do I return a future currently in progress?

I'm trying to create a server-side Dart class that performs various data-related tasks. All of these tasks rely on the database having been first initialized. The problem is that the init of the database happens asynchronously (returns a Future). I first tried to put the init code into the constructor, but have given up on this approach as it seems to not be viable.
I am now attempting to figure out how to force the DB initialization as a first step in any method call that accesses data. So in other words, when attemptLogin() is called below, I'd like to first check if the DB has been initialized and initialize it if necessary.
However, there are two obstacles. If the database hasn't been initialized, the code is straightforward - initialize the db, then use the then() method of the returned future to do the rest of the function. If the db is not yet initialized, what do I attach my then() method to?
Second related question is what happens when a database is currently being initialized but this process is not yet complete? How can I pull in and return this "in-progress" Future?
This is the basic gist of the code I'm trying to wrangle:
class DataManager {
bool DbIsReady = false;
bool InitializingDb = false;
Db _db;
Future InitMongoDB() {
print("Initializing MongoDB");
InitializingDb = true;
_db = new Db("mongodb://127.0.0.1/test");
return _db.open().then((_) {
DbIsReady = true;
InitializingDb = false;
});
}
Future<List> attemptLogin(String username, String password) {
Future firstStep;
if ((!DbIsReady) && (!InitializingDb) {
Future firstStep = InitMongoDB()
}
else if (InitializingDb) {
// Need to return the InitMongoDB() Future that's currently running, but how?
}
else {
// How do I create a blank firstStep here?
}
return firstStep.then((_) {
users = _db.collection("users");
return // ... rest of code cut out for clarity
});
}
}
Thanks in advance for your help,
Greg
Just return
return new Future<bool>.value(true);
// or any other value instead of `true` you want to return.
// or none
// return new Future.value();
Just keep the future alive:
class DataManager {
Future _initializedDb;
Future initMongoDb() { ... }
Future<List> attemptLogin(String username, String password) {
if (_initializedDb == null) {
_initializedDb = initMongoDB();
}
return _initializedDb.then((db) {
users = db.collection("users");
return // ... rest of code cut out for clarity
});
}
}
You might need to pay attention for the error-case. It's up to you if you want to deal with errors in the initMongoDB or after it.
One of the possible solutions:
import "dart:async";
void main() {
var dm = new DataManager();
var selectOne = dm.execute("SELECT 1");
var selectUsers = dm.execute("SELECT * FROM users");
var users = selectOne.then((result) {
print(result);
return selectUsers.then((result) {
print(result);
});
});
users.then((result) {
print("Goodbye");
});
}
class Event {
List<Function> _actions = new List<Function>();
bool _raised = false;
void add(Function action) {
if (_raised) {
action();
} else {
_actions.add(action);
}
}
void raise() {
_raised = true;
_notify();
}
void _notify() {
if (_actions.isEmpty) {
return;
}
var actions = _actions.toList();
_actions.clear();
for (var action in actions) {
action();
}
}
}
class DataManager {
static const int _STATE_NOT_INITIALIZED = 1;
static const int _STATE_INITIALIZING = 2;
static const int _STATE_READY = 3;
Event _initEvent = new Event();
int _state = _STATE_NOT_INITIALIZED;
Future _init() {
if (_state == _STATE_NOT_INITIALIZED) {
_state = _STATE_INITIALIZING;
print("Initializing...");
return new Future(() {
print("Initialized");
_state = _STATE_READY;
_initEvent.raise();
});
} else if (_state == _STATE_INITIALIZING) {
print("Waiting until initialized");
var completer = new Completer();
_initEvent.add(() => completer.complete());
return completer.future;
}
return new Future.value();
}
Future execute(String query, [Map arguments]) {
return _init().then((result) {
return _execute(query, arguments);
});
}
Future _execute(String query, Map arguments) {
return new Future.value("query: $query");
}
}
Output:
Initializing...
Waiting until initialized
Initialized
query: SELECT 1
query: SELECT * FROM users
Goodbye
I think that exist better solution but this just an attempt to answer on your question (if I correctly understand you).
P.S. EDITED at 11 July 2014
Slightly modified (with error handling) example.
import "dart:async";
void main() {
var dm = new DataManager();
var selectOne = dm.execute("SELECT 1");
var selectUsers = dm.execute("SELECT * FROM users");
var users = selectOne.then((result) {
print(result);
return selectUsers.then((result) {
print(result);
});
});
users.then((result) {
print("Goodbye");
});
}
class DataManager {
static const int _STATE_NOT_INITIALIZED = 1;
static const int _STATE_INITIALIZING = 2;
static const int _STATE_READY = 3;
static const int _STATE_FAILURE = 4;
Completer _initEvent = new Completer();
int _state = _STATE_NOT_INITIALIZED;
Future _ensureInitialized() {
switch (_state) {
case _STATE_NOT_INITIALIZED:
_state = _STATE_INITIALIZING;
print("Initializing...");
new Future(() {
print("Initialized");
_state = _STATE_READY;
// throw null;
_initEvent.complete();
}).catchError((e, s) {
print("Failure");
_initEvent.completeError(e, s);
});
break;
case _STATE_INITIALIZING:
print("Waiting until initialized");
break;
case _STATE_FAILURE:
print("Failure detected");
break;
default:
print("Aleady intialized");
break;
}
return _initEvent.future;
}
Future execute(String query, [Map arguments]) {
return _ensureInitialized().then((result) {
return _execute(query, arguments);
});
}
Future _execute(String query, Map arguments) {
return new Future.value("query: $query");
}
}
For those that are still wondering how to create a blank Future in Dart and later complete them, you should use the Completer class like in the next example.
class AsyncOperation {
final Completer _completer = new Completer();
Future<T> doOperation() {
_startOperation();
return _completer.future; // Send future object back to client.
}
// Something calls this when the value is ready.
void finishOperation(T result) {
_completer.complete(result);
}
// If something goes wrong, call this.
void _errorHappened(error) {
_completer.completeError(error);
}
}
Future<Type> is non nullable in Dart, meaning that you have to initialize it to a value. If you don't, Dart throws the following error:
Error: Field should be initialized because its type 'Future<Type>' doesn't allow null.
To initialize a Future<Type>, see the following example:
Future<String> myFutureString = Future(() => "Future String");
Here "Future String" is a String and so the code above returns an instance of Future<String>.
So coming to the question of how to create a blank/empty Future, I used the following code for initializing an empty Future List.
Future<List> myFutureList = Future(() => []);
I found this link to be quite useful in understanding Futures in Flutter and Dart: https://meysam-mahfouzi.medium.com/understanding-future-in-dart-3c3eea5a22fb

Prevent to read file many times

I am trying to write an i18n app. The program read a json file, that contains translation from languages and it based on json structure.
{
"EN": {
"TEXT1": "Hello",
"TEXT2": "March"
},
"DE": {
"TEXT1": "Hallo",
"TEXT2": "März"
}
}
My program read the json file in async way with the file class, the whole code
import 'dart:io';
import 'dart:async';
import 'package:json_object/json_object.dart';
abstract class I18n {
static _I18n _i18n;
factory I18n(String file, String lang) {
if(_i18n == null) {
_i18n = new _I18n(file, lang);
return _i18n;
}
return _i18n;
}
Future<String> getTextByMap(String textId);
}
class _I18n implements I18n {
File _file;
String _lang;
JsonObject _jsonContainer;
JsonObject _jsonFiltered;
Future<JsonObject> _imme;
// Parameters:
// file: The whole path and filename
// lang: Expected language
_I18n(String file, this._lang) {
this._file = new File(file);
}
// Read file and return the content of file.
Future<String> _readFileFromStream() {
var com = new Completer();
this._file.exists()
.then((fileExists) {
if(!fileExists) {
throw new StateError('File not found');
}
return this._file.readAsString()
.then((stream) => com.complete(stream));
});
return com.future;
}
void _convertContentToJson(String stream) {
this._jsonContainer = new JsonObject.fromJsonString(stream);
}
Future<JsonObject> _prepareData() {
return this._readFileFromStream().then((stream) {
_convertContentToJson(stream);
this._jsonFiltered = this._jsonContainer[this._lang];
return this._jsonFiltered;
});
}
Future<String> getTextByMap(String textId) {
return this._prepareData().then((filterd) {
return filterd[textId];
});
}
}
and the main code
import 'package:i18n/i18n.dart';
void main() {
var i18n = new I18n('../hello.json', 'EN');
i18n.getTextByMap('TEXT1').then((val) => print(val));
i18n.getTextByMap('TEXT2').then((val) => print(val));
}
Everything here, happen in dart async way, read json file etc. And everytime, when i call the method
i18n.getTextByMap('TEXT1').then((val) => print(val));
it gonna read the json file again and again. I tried to rewrite the method to prevent reading json file many times
Future<String> getTextByMap(String textId) {
if(this._jsonFiltered == null)
{
return this._prepareData().then((filterd) {
return filterd[textId];
});
}
return new Future(() => this._jsonFiltered[textId]);
}
but it doesn't work too, because dart works in async way.
My question is, how can i keep this json file content in an object? Read json file only one time and keep the contents in an object, it is better then read json file everytime, that is my opinion.
It could do everything in sync way, then i wouldn't have such as problem but this is not dart terminology.
In which order do dart execute I/O operations, like this?
Future
I/O Events
My solution would be to create a class with a factory constructor. The factory constructor always returns a object of that file.
Your problem is that futures are parallel. So both calls are executed in parallel. The solution is to let the first future complete and then do other stuff to be able to get cached results.
Then you can have a read() method that reads the value of the file if it is not present in the classes "contents" attribute for example - or if that attribute is not null, it loads the file in background.
In both cases a completer or future is returned you can listen on.
EDIT Example Code:
example_async_file_factory.dart
import 'dart:io';
import 'dart:async';
class FileHolder {
String _contents = null;
String path;
static Map<String, FileHolder> _files;
factory FileHolder(String path) {
if (_files == null) {
_files = {};
}
if (_files.containsKey(path)) {
return _files[path];
} else {
final fh = new FileHolder._internal(path);
_files[path] = fh;
return fh;
}
}
FileHolder._internal(this.path);
Future<String> getContents() {
if(_contents != null) {
print("cached");
return new Future.value(_contents);
} else {
print("read");
File f = new File(this.path);
Future<String> future = f.readAsString();
Completer completer = new Completer();
future.then((String c) {
_contents = c;
completer.complete(_contents);
});
return completer.future;
}
}
}
void main() {
FileHolder f = new FileHolder("example_async_file_factory.dart");
f.getContents().then((String contents) {
print(contents.length);
FileHolder f2 = new FileHolder("example_async_file_factory.dart");
f2.getContents().then((String contents) {
print(contents.length);
});
f2.getContents().then((String contents) {
print(contents.length);
});
f.getContents().then((String contents) {
print(contents.length);
});
});
}
Output:
read
1411
cached
cached
cached
1411
1411
1411
Regards
Robert

How to implement dynamic properties in Dart?

I would like to be able to back a dynamic property with a Map using a lookup in noSuchMethod(). However the latest changes makes the incoming property reference name unavailable. I can understand the minification scenario requiring us to use Symbols rather than Strings for names, but this makes implementing serializable dynamic properties difficult. Anyone have good ideas on how to approach this problem?
I can't use String names since the String names are not fixed between calls to the minifier. (This would completely break serialization)
You can access the original name with MirrorSystem.getName(symbol)
So a dynamic class could look like :
import 'dart:mirrors';
class A {
final _properties = new Map<String, Object>();
noSuchMethod(Invocation invocation) {
if (invocation.isAccessor) {
final realName = MirrorSystem.getName(invocation.memberName);
if (invocation.isSetter) {
// for setter realname looks like "prop=" so we remove the "="
final name = realName.substring(0, realName.length - 1);
_properties[name] = invocation.positionalArguments.first;
return;
} else {
return _properties[realName];
}
}
return super.noSuchMethod(invocation);
}
}
main() {
final a = new A();
a.i = 151;
print(a.i); // print 151
a.someMethod(); // throws
}
You could do something like this:
import 'dart:json' as json;
main() {
var t = new Thingy();
print(t.bob());
print(t.jim());
print(json.stringify(t));
}
class Thingy {
Thingy() {
_map[const Symbol('bob')] = "blah";
_map[const Symbol('jim')] = "oi";
}
final Map<Symbol, String> _map = new Map<Symbol, String>();
noSuchMethod(Invocation invocation) {
return _map[invocation.memberName];
}
toJson() => {
'bob': _map[const Symbol('bob')],
'jim': _map[const Symbol('jim')]};
}
Update - dynamic example:
import 'dart:json' as json;
main() {
var t = new Thingy();
t.add('bob', 'blah');
t.add('jim', 42);
print(t.bob());
print(t.jim());
print(json.stringify(t));
}
class Thingy {
final Map<Symbol, String> _keys = new Map<Symbol, String>();
final Map<Symbol, dynamic> _values = new Map<Symbol, dynamic>();
add(String key, dynamic value) {
_keys[new Symbol(key)] = key;
_values[new Symbol(key)] = value;
}
noSuchMethod(Invocation invocation) {
return _values[invocation.memberName];
}
toJson() {
var map = new Map<String, dynamic>();
_keys.forEach((symbol, name) => map[name] = _values[symbol]);
return map;
}
}
If you only need "dynamic properties", it should be enough to use Symbols as keys in the Map. If you also want to serialize that map, then you need to keep track of the original String names and use those for serialization. When deserializing, you'd have to create new Symbols from those Strings.
Note that all these scenarios (and basically everything that involves new Symbol) require a compiler to create a mapping of original names to the minified ones and put this mapping into the program, which of course makes it bigger.
Thanks for the solution of #Alexandre Ardhuin, I made some modification to make it runnable.
import 'dart:mirrors';
class object {
final _properties = new Map<String, Object>();
object();
object.from(Map<String, Object> initial) {
initial.entries.forEach((element) => _properties[element.key] = element.value);
}
noSuchMethod(Invocation invocation) {
if (invocation.isAccessor) {
final realName = MirrorSystem.getName(invocation.memberName);
if (invocation.isSetter) {
// for setter realname looks like "prop=" so we remove the "="
final name = realName.substring(0, realName.length - 1);
_properties[name] = invocation.positionalArguments.first;
return;
} else {
return _properties[realName];
}
}
return super.noSuchMethod(invocation);
}
#override
String toString() {
return _properties.toString();
}
}
main() {
// we can't use var or object type here, because analysis will consider
// https://dart.dev/tools/diagnostic-messages#undefined_setter
// The setter 'i' isn't defined for the type 'object'
// So dynamic is required here!
dynamic a = object.from({'a': 123, 'b': 234});
a.i = 151;
print(a); // print {a: 123, b: 234, i: 151}
try {
a.someMethod(); // throws NoSuchMethodError
} catch (e) {
print(e);
}
}

Resources