17

I am looking for some reverse of new JsObject.jsify. Something, that would convert javascript Object back to Dart Map Is there something like that available?

I know that I can use JSON conversion to string, but this does not address transfer of Objects containing functions, Dart objects, Dom Elements, etc... Is there any better method?

7 Answers 7

6

Here's an adapter to handle a JsObject like a Map<String, dynamic> :

import 'dart:collection' show Maps;
import 'dart:js';

class JsMap implements Map<String,dynamic> {
  final JsObject _jsObject;
  JsMap.fromJsObject(this._jsObject);

  operator [](String key) => _jsObject[key];
  void operator []=(String key, value) {
    _jsObject[key] = value;
  }
  remove(String key) {
    final value = this[key];
    _jsObject.deleteProperty(key);
    return value;
  }
  Iterable<String> get keys => context['Object'].callMethod('keys', [_jsObject]);

  // use Maps to implement functions
  bool containsValue(value) => Maps.containsValue(this, value);
  bool containsKey(String key) => keys.contains(key);
  putIfAbsent(String key, ifAbsent()) => Maps.putIfAbsent(this, key, ifAbsent);
  void addAll(Map<String, dynamic> other) {
    if (other != null) {
      other.forEach((k,v) => this[k] = v);
    }
  }
  void clear() => Maps.clear(this);
  void forEach(void f(String key, value)) => Maps.forEach(this, f);
  Iterable get values => Maps.getValues(this);
  int get length => Maps.length(this);
  bool get isEmpty => Maps.isEmpty(this);
  bool get isNotEmpty => Maps.isNotEmpty(this);
}
Sign up to request clarification or add additional context in comments.

1 Comment

I guess the essence is in context['Object'].callMethod('keys', [_jsObject])
6

You can generate json and parse it:

    Map map = JSON.decode(
       context['JSON'].callMethod(
           'stringify',
           [context['map']]
       )
    );

Comments

5

If you want to do it deeply and handle also other cases then simple maps AND preserve functions (unlike the json solution) then use this simple function:

_toDartSimpleObject(thing) {
 if (thing is js.JsArray) {
  List res = new List();
  js.JsArray a = thing as js.JsArray;
  a.forEach((otherthing) {
    res.add(_toDartSimpleObject(otherthing));
  });
  return res;

 } else if (thing is js.JsObject) {
  Map res = new Map();
  js.JsObject o = thing as js.JsObject;
  Iterable<String> k = js.context['Object'].callMethod('keys', [o]);
   k.forEach((String k) {
   res[k] = _toDartSimpleObject(o[k]);
  });
  return res;

 } else {
  return thing;
 }
}

1 Comment

"Replace Iterable<String> k" with "JsArray<dynamic> k" and "String k" with "dynamic k" :)
1

Will convert recursively any JS object to a Dart map, list or scalar value:

/// js_interop.dart
import 'dart:js';

/// Converts the specified JavaScript [value] to a Dart instance.
dynamic convertToDart(value) {
  // Value types.
  if (value == null) return null;
  if (value is bool || value is num || value is DateTime || value is String) return value;

  // JsArray.
  if (value is Iterable) return value.map(convertToDart).toList();

  // JsObject.
  return new Map.fromIterable(getKeysOfObject(value), value: (key) => convertToDart(value[key]));
}

/// Gets the enumerable properties of the specified JavaScript [object].
List<String> getKeysOfObject(JsObject object) => (context['Object'] as JsFunction).callMethod('keys', [object]);

Usage:

/// config.js
window.$environment = 'staging';
window.$config = {
  name: 'FooBar',
  params: {
    assets: ['css', 'js'],
    forceSsl: true
  }
};

/// main.dart
import 'dart:js' as js;
import 'js_interop.dart';

void main() {
  var environment = convertToDart(js.context[r'$environment']);
  assert(environment is String);
  assert(environment == 'staging');

  var config = convertToDart(js.context[r'$config']);
  assert(config is Map<String, dynamic>);
  assert(config.length == 2);

  assert(config['name'] is String);
  assert(config['name'] == 'FooBar');

  assert(config['params'] is Map<String, dynamic>);
  assert(config['params'].length == 2);

  assert(config['params']['forceSsl'] is bool);
  assert(config['params']['forceSsl'] == true);

  assert(config['params']['assets'] is List<String>);
  assert(config['params']['assets'].length == 2);
  assert(config['params']['assets'].first == 'css');
  assert(config['params']['assets'].last == 'js');
}

Caveats: the created instance does not reflect the changes from the original JS object. If you need this feature, Alexandre Arduin's answer is the right one.

Comments

0

All of the above answers didn't work for me.

matanlurey provided a solution which seems more up to date and actually worked for me (Dart SDK 1.24.0), here:

https://stackoverflow.com/a/41291503/5280562

It's very similar in a nutshell but uses the new package:js instead of dart:js.

Comments

0

The node_interop package provides a dartify method that easily converts Javascript Objects to Dart Maps/Lists!

Comments

0

I know I'm reviving a dead topic, but I was pulling out my hair trying to figure this out and while this is the top google result, the answers didn't help.

Here's a much simpler implementation, though I believe it'll only work in Flutter Web.

I've included both mapify (turn Json object into a dart map) and it's opposite (creates a json object from dart map)

import 'dart:convert' show jsonDecode;
import 'dart:js';

Map? mapify(JsObject? obj) {
  if (obj == null) return null;
  return jsonDecode(context['JSON'].callMethod('stringify', [obj]));
}

JsObject jsify(object) => JsObject.jsify(object);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.