angular dart js interop with async / promise awaited in client - dart

clientside.js
async function callClientAsyncFuncWithResult () {
let result = await someService.request();
return result;
}
page.dart
import 'dart:js' as js;
var result = js.context.callMethod('callClientAsyncFuncWithResult');
//I want to do something like var result = await js.context.callMethod('callClientAsyncFuncWithResult');
How in AngularDart do you wait for a client side javascript Promise to return with result before continuing execution in dart? Right now it just flows over the call and I've tried setting the result of callMethod to Future or Promise and it never waits.
I don't think my implementation is correct. How can I achieve this?

You can easily convert a Javascript Promise to a Dart Future, by using the convertNativePromiseToDartFuture API, which is available in dart:html_common.
A simple implementation might look like :
Javascript :
function myCoolFunc () {
return new Promise((resolve, reject) => {
resolve(myLongAwaitedData);
});
}
Dart Interop file :
#JS()
library coolLib;
import 'package:js/js.dart';
import 'dart:async';
#JS()
external Future<T> myCoolFunc ();
Dart file :
import 'dart:html_common';
import 'cool_lib.dart';
main() async {
var myVar = await convertNativePromiseToDartFuture(myCoolFunc());
print(myVar);
}
I found this deeply buried in the Gitter of the Dart Sdk. I hope it can help future Angular Dart users.
Update : This API has changed in Dart 2.6 convertNativePromiseToDartFuture has been replaced with promiseToFuture

Related

How to use Dart's analysis server method search.findTopLevelDeclarations?

I'm trying to get list of top level class declarations with Dart's analysis server. So, I'm sending search.findTopLevelDeclarations request, but search results are always empty.
It seems to me that analysis server don't know where to search. I've tried to set my project's root as execution context (execution.createContext) root and/or analysis root (analysis.setAnalysisRoots), but search results are still empty.
What should I do to make server understand where to search declarations?
Never played with this before so I got into quite a journey...
I don't know how you are interacting with the analysis server but I have made a working example using the analysis_server_client package. One problem doing that is that the version on pub.dev is quite old so I ended up fetching the version from the stable branch of Dart SDK:
https://github.com/dart-lang/sdk/tree/stable/pkg/analysis_server_client
You can then import the package in your pubspec.yaml by doing:
dependencies:
analysis_server_client:
path: /path/to/analysis_server_client
I then made a simplified version of the example code from:
https://github.com/dart-lang/sdk/blob/stable/pkg/analysis_server_client/example/example.dart
import 'dart:io' show exit;
import 'package:analysis_server_client/handler/connection_handler.dart';
import 'package:analysis_server_client/handler/notification_handler.dart';
import 'package:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/server.dart';
final server = Server();
Future<void> main(List<String> args) async {
const targetDirPath = r'C:\tmp\simple_project';
const searchPattern = 'main';
// Launch the server
await server.start();
// Connect to the server
final handler = _Handler(server);
server.listenToOutput(notificationProcessor: handler.handleEvent);
if (!await handler.serverConnected(timeLimit: const Duration(seconds: 15))) {
exit(1);
}
await server.send(ANALYSIS_REQUEST_SET_ANALYSIS_ROOTS,
AnalysisSetAnalysisRootsParams([targetDirPath], const []).toJson());
await server.send(SEARCH_REQUEST_FIND_TOP_LEVEL_DECLARATIONS,
SearchFindTopLevelDeclarationsParams(searchPattern).toJson());
}
class _Handler with NotificationHandler, ConnectionHandler {
#override
final Server server;
_Handler(this.server);
#override
void onSearchResults(SearchResultsParams params) {
print('-- Start of result --');
params.results.forEach(print);
print('-- End of result --');
server.stop();
}
}
The project at C:\tmp\simple_project is a simple project created with the following which means it just contains a single main method:
dart create -t console-simple simple_project
When I run my analyzer program I get the following output:
-- Start of result --
{"location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":5,"length":4,"startLine":1,"startColumn":6,"endLine":1,"endColumn":10},"kind":"DECLARATION","isPotential":false,"path":[{"kind":"FUNCTION","name":"main","location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":5,"length":4,"startLine":1,"startColumn":6,"endLine":1,"endColumn":10},"flags":8,"parameters":"(List<String> arguments)","returnType":"void"},{"kind":"COMPILATION_UNIT","name":"simple_project.dart","location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":0,"length":0,"startLine":1,"startColumn":1,"endLine":1,"endColumn":1},"flags":16},{"kind":"LIBRARY","name":"","location":{"file":"C:\\tmp\\simple_project\\bin\\simple_project.dart","offset":0,"length":0,"startLine":1,"startColumn":1,"endLine":1,"endColumn":1},"flags":0}]}
-- End of result --
If I change searchPattern to an empty String, I gets a long list of top level declarations around the default included Dart SDK libraries. I am sure there are a way to exclude those.
But as far as I can see, the searchPattern is a regular expression tested against the name of each top level declaration and includes the declaration if its name contain any part of the regular expression.
I found the code responsible for the search here:
#override
Future<List<SearchMatch>> searchTopLevelDeclarations(String pattern) async {
var allElements = <Element>{};
var regExp = RegExp(pattern);
var drivers = _drivers.toList();
for (var driver in drivers) {
var elements = await driver.search.topLevelElements(regExp);
allElements.addAll(elements);
}
return allElements.map(SearchMatchImpl.forElement).toList();
}
https://github.com/dart-lang/sdk/blob/1278bd5adb6a857580f137e47bc521976222f7b9/pkg/analysis_server/lib/src/services/search/search_engine_internal.dart#L113-L123
Which calls into:
/// Returns top-level elements with names matching the given [regExp].
Future<List<Element>> topLevelElements(RegExp regExp) async {
List<Element> elements = <Element>[];
void addElement(Element element) {
if (!element.isSynthetic && regExp.hasMatch(element.displayName)) {
elements.add(element);
}
}
List<FileState> knownFiles = _driver.fsState.knownFiles.toList();
for (FileState file in knownFiles) {
var unitResult = await _driver.getUnitElement(file.path);
if (unitResult is UnitElementResult) {
CompilationUnitElement unitElement = unitResult.element;
unitElement.accessors.forEach(addElement);
unitElement.classes.forEach(addElement);
unitElement.enums.forEach(addElement);
unitElement.extensions.forEach(addElement);
unitElement.functions.forEach(addElement);
unitElement.mixins.forEach(addElement);
unitElement.topLevelVariables.forEach(addElement);
unitElement.typeAliases.forEach(addElement);
}
}
return elements;
}
https://github.com/dart-lang/sdk/blob/1278bd5adb6a857580f137e47bc521976222f7b9/pkg/analyzer/lib/src/dart/analysis/search.dart#L166-L192

Bad state: Mock method was not called within `when()`. Was a real method called?

I'm trying to make a mock of an httpRequest in flutter using mockito.
Here I define a global http client:
library utgard.globals;
import 'package:http/http.dart' as http;
http.Client httpClient = http.Client();
Then I replace in integration testing:
import 'package:flutter_driver/driver_extension.dart';
import 'package:http/http.dart' as http;
import 'package:utgard/globals.dart' as globals;
import 'package:mockito/mockito.dart';
import 'package:utgard/main.dart' as app;
class MockClient extends Mock implements http.Client {}
void main() {
final MockClient client = MockClient();
globals.httpClient = client;
enableFlutterDriverExtension();
app.main();
}
Then I try to use when of mockito:
test('login with correct password', () async {
final client = MockClient();
when(globals.httpClient.post('http://www.google.com'))
.thenAnswer((_) async => http.Response('{"title": "Test"}', 200));
await driver.enterText('000000');
await driver.tap(loginContinuePasswordButton);
});
But I receive the following error:
Bad state: Mock method was not called within when(). Was a real method called?
This issue may happen when you implement a method you want to mock instead of letting Mockito do that.
This code below will return Bad state: Mock method was not called within when(). Was a real method called?:
class MockFirebaseAuth extends Mock implements FirebaseAuth {
FirebaseUser _currentUser;
MockFirebaseAuth(this._currentUser);
// This method causes the issue.
Future<FirebaseUser> currentUser() async {
return _currentUser;
}
}
final user = MockFirebaseUser();
final mockFirebaseAuth = MockFirebaseAuth(user);
// Will throw `Bad state: Mock method was not called within `when()`. Was a real method called?`
when(mockFirebaseAuth.currentUser())
.thenAnswer((_) => Future.value(user));
What do you want instead is:
class MockFirebaseAuth extends Mock implements FirebaseAuth {}
final user = MockFirebaseUser();
final mockFirebaseAuth = MockFirebaseAuth();
// Will work as expected
when(mockFirebaseAuth.currentUser())
.thenAnswer((_) => Future.value(user));
Also this issue happens when you try to call when() on a non-mock sublass:
class MyClass {
String doSomething() {
return 'test';
}
}
final myClassInstance = MyClass();
// Will throw `Bad state: Mock method was not called within `when()`. Was a real method called?`
when(myClassInstance.doSomething())
.thenReturn((_) => 'mockedValue');
There is one more possibility. If this happens to your mock object, then probably it may be outdated. In this case, try regenerating your mock objects by using
flutter pub run build_runner build --delete-conflicting-outputs
please declare resetMocktailState() in teardown or at the end of setup, so the test cases should not affect later test cases.
The solution I found was to define the mock in test_driver/app.dart and call the runApp function after that, this way you can apply the mock even with flutter integration testing:
import 'package:flutter/widgets.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:utgard/business/config/globals.dart';
import 'package:utgard/main.dart' as app;
class MockClient extends Mock implements http.Client {}
void main() {
enableFlutterDriverExtension();
final MockClient client = MockClient();
// make your mocks here
httpClient = client;
runApp(app.MyApp());
}
Since it can become a huge code to mock all the requests there you can make a separate function in order to better organize the code:
import 'package:flutter/widgets.dart';
import 'package:flutter_driver/driver_extension.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:utgard/business/config/globals.dart';
import 'package:utgard/main.dart' as app;
class MockClient extends Mock implements http.Client {}
void main() {
enableFlutterDriverExtension();
final MockClient client = MockClient();
makeMock();
httpClient = client;
runApp(app.MyApp());
}

How do I create a global (on the window) object in Dart lang?

Let's say I want to create a global object called Hello and add the function world on that object, so that any other JavaScript library in the browser can simply call it with window.Hello.world();
How do I create such an object in Dart lang and how do I expose it / place it globally / on the window object?
In plain JavaScript, I would be able to write:
window.Hello = {
world: function() {
console.log("Hello World!");
}
}
window.Hello.world();
But how do you do this in Dart?
I work on Dart-JS interop. Here is the cleanest way to do it using the new package:js/js.dart interop.
#JS()
library your_library;
import 'package:js/js.dart';
#anonymous
#JS()
class HelloObject {
external factory HelloObject({Function world});
external world();
}
#JS()
external set Hello(HelloObject v);
#JS()
external HelloObject get Hello;
main() {
Hello = new HelloObject(world: allowInterop(() { print("Hello from dart"); }));
// You can also call this object from Dart as an added bonus.
// For example you could call this from Dart or Js.
/// Hello.world();
}
I am not sure how it will work with objects, but if you want to do that for methods, it's quite simple:
import 'dart:js' as js;
main() {
js.context['methodFromDart'] = doMyStuff;
}
void doMyStuff(String text) => print(text);
And then in you Javascript you are free to do:
methodFromDart("Hello world to Dart!");
You can try to find a way how to do similar things to objects.

angular2 CanActivate with http

I am having a component that has canactivate
import {isLoggedIn} from '../login/isLoginedIn';
#CanActivate((next, previous) => {
isLoggedIn()
})
My "isLoggedIn" is as below
import {Http, Headers} from 'angular2/http';
class Auth {
constructor( #Inject(Http) private _http: Api) { }
check() {
this._http.get('/Users/LoggedInUser')
}
}
export const isLoggedIn = () => {
let injector = Injector.resolveAndCreate([Auth, Http]);
let auth = injector.get(Auth);
return auth.check();
};
I can't inject a service which has http as dependancy. Can this be done like this or is there a better way to do it?
Since the CanActivate is a decorator instead of a method as with OnActivate or CanDeactivate then you are correct in assuming that constructor dependency injection of the component that you are attempting to authorize is not an option.
The method which you are using will work, but there is a missed #Injectable() on your Auth class.
import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';
#Injectable()
class Auth {
constructor( #Inject(Http) private _http: Api) { }
check() {
this._http.get('/Users/LoggedInUser')
}
}
This approach is sound and I don't think that besides some syntactic sugar or minor refactoring that there would be much to improve this and still achieve the same amount of readability / maintainability for this approach.
One other addition that could be made to improve the flow and prevent a potential bug would be to return the observable in CanActivate so that the navigation will wait for the Http request to complete before deciding to continue or cancel.
#CanActivate((next, previous) => {
return isLoggedIn()
})
or for short
#CanActivate(() => isLoggedIn())
(Single statement arrow functions are auto-returning)

why cant i assign a variable to the result of an async call via await?

I have the following lines, which doesnt work. It says: expected ; after await.
import "package:http/http.dart" as http;
http.Response rslt = await http.post(/*...*/);
Since http.post by definition returns a Future(Response), does await not resolve that? I thought it would.
https://www.dartdocs.org/documentation/http/0.11.3%2B3/http/http-library.html
i always thought in a sense, the await, will unwrap the Future object and assign it to whatever... in this case a Response variable.
I'm pretty sure the method/function containing this code is missing the async modifier.
Future someFunc() async {
import "package:http/http.dart" as http;
http.Response rslt = await http.post(/*...*/);
}
If the function body doesn't have the async modifier, async is a valid identifier because async is not a keyword.

Resources