In Angular 1.x , we can use angular.element(appElement).scope() to get $scope, and then use the $apply(), so that the native javascript can directly call angular functions or two-way binding. While with Angular 4, how could we call angular functions or two-way binding by native javascript or the android native.
For example :
The web is developed by angular 4, and it will be used in the android webview, so it needs an interaction with android, how can i handle the interaction?
I can think of many ways, but have never read anything in the manuals which clarifies as to the most Angular way.
Zones
You have to keep in mind that Angular uses zones and a change detection tree at the core of it's engine. So any outside access needs to happen in that context.
You have to run external code in the Angular zone:
zone.run(() => {
// do work here
});
If you make changes to any data that will directly or indirectly effect a template expression you run the risk of a change detection error. So a component needs to inject ChangeDetectorRef and call markForCheck.
So if you code runs inside a component but from outside of Angular. You need to do this:
zone.run(() => {
// do work here.
this.ChangeDetectorRef.markForCheck();
});
Still, that raises the question. How do I get to the component?
Accessing Angular
You have to bootstrap your Angular application so that it can be accessed.
When you bootstrap your Angular application the browser service returns a promise to the main module. That main module contains the injector and from there you can access any exported services.
platformBrowserDynamic()
.bootstrapModule(AppModule)
.then((modRef: NgModuleRef<AppModule>) => {
window.myGlobalService = modRef.injector.get(MyServiceClass);
});
That would place a service class as a global variable. You would have to create an API that forwards to Angular's zones.
#Injectable()
export class MyServiceClass {
public dataEmitter: Subject<any> = new Subject();
public constructor(private zone: NgZone) {}
public fooBar(data: any): any {
return this.zone.run(()=>{
// do work here
this.dataEmitter.next(data);
return "My response";
});
}
}
You can return a result from zone.run out of the service. The key is that Angular code is run in the correct zone.
Easy Component One-Way Binding
The easiest solution for one-way data binding is to just use the Event module for the DOM.
#Component({....})
export class MyComponent {
#HostListener('example',['$event'])
public onExample(event: Event) {
console.log(event.fooBar);
}
}
// else where in external JavaScript
var elem; // the DOM element with the component
var event = new Event('example', {fooBar: 'Hello from JS!'});
elem.dispatchEvent(elem);
I prefer this approach since Angular handles the event listener as it would any other kind of event (i.e. click events)
You can also do this the other way around. Have the component emit DOM events on it's ElementRef for external JavaScript to listen for. This makes the whole two-way communications more DOM standard.
Related
I have added flutter to a native iOS code. I have UIButton and on pressing this button, I am presenting a FlutterViewController. I have been following this.
Here's my code for presenting the FlutterViewController:
#objc
#IBAction func openProfile() {
print("open profile")
lazy var flutterEngine = FlutterEngine(name: "patient_profile_engine")
flutterEngine.run(withEntrypoint: "", libraryURI: "");
let profileVC =
FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(profileVC, animated: true, completion: nil)
}
This code is working fine and the flutter view is opening and since I haven't specified and entry point yet so it using the main.dart.
The problem is that I need to pass some information to the Flutter dart code. e.g. a dictionary ["patient_id": "123456"] during the initialization.
I will have to do the same in Android native Java code as well.
Is there any easy way?
You will probably want to write a platform channel to communicate between the native code and the flutter engine.
An alternative, for this purpose, would be to use runWithEntrypoint:initialRoute: method to start the engine, encode the data into the initial route, and parse it on the flutter side.
This question relates to both ios (swift) and android.
I'm working on building the backend implementation for a banner system that our mobile apps need to integrate with. Some of the banners need to redirect a user to a section of the app when pressed.
What is the best practice for triggering route navigation based on data returned in an API call?
For example, a list of banner objects in JSON where a key references the page to navigate to - would deep linking apply here, does it make sense for the apps to create a mapping of routes that I can reference by passing a string?
I feel like there has to be a simple solution here but the mobile team I'm working with seems pretty adamant.
In my current project (iOS), we are using deep links to map navigation to a certain tab of our app. So you could send it through JSON and then post a Notification, which your main controller is listening to (TabBarController for example), and then select the wanted index to go to, or any other navigation logic you need.
// Registering the deeplink with DeepLinkKit pod (https://github.com/button/DeepLinkKit)
self.router?.register("a/created/path") { link in
if let link = link {
debugPrint("Router: \(String(describing: link.url))")
NotificationCenter.default.post(
name: Notification.Name(Configuration.App.kGoToSomewhere),
object: nil
)
}
}
Or just extracted from your JSON and then setup the notification
call { URL in
NotificationCenter.default.post(
name: Notification.Name("ANotification"),
object: nil
)
}
And then in your main controller
class MainTabBarController: UITabBarController {
func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(selectTab),
name: Notification.Name("ANotificaton"),
object: nil
)
}
}
func selectTab() {
// Do your navigation logic here
}
I hope it helps!
We are looking to integrate NativeScript Angular into existing iOS and Android apps. We would like to have existing menus in our app be able to route to sections of the NS Angular app. /home /contact for example. One of our concerns with embedding an NS Angular app is the large overhead for frameworks / vendor files / etc and having to duplicate these sections over and over for multiple embedded apps. We would rather have one NS Angular app that contains various views and be able to navigate to those views as the starting view based on the initial link tapped in the previous Native app that hosts the embed.
Has anyone achieved this already and can share some examples or any info that would help? Would sending a variable from the native app to the embedded NS Angular app that routes be on option?
in https://github.com/NativeScript/sample-ios-embedded/blob/master/HOWTO.md
there is mention of _runScript
- (void)activateNativeScript:(id)sender {
NSString *source = #"var application = require('application');"
"application.start({ moduleName: 'main-page' });";
[self _runScript: source];
}
Would we be able to send routes through _runScript or possibly initialize various sections but still maintain the same vendor bundle.js ?
thanks!
I don't think you could access Angular from outside Angular context. A workaround could be, store the RouterExtensions reference on global variable while your appcomponent.ts is initiated,
export class AppComponent {
constructor(private routerExtensions: RouterExtensions) {
(<any>global)._routerExtensions = routerExtensions;
}
}
You may do this with _runScript then
NSString *source = #"global._routerExtensions.navigate([...])";
But make sure you access _routerExtensions once it's initialised. Also the code to start Angular app is slightly different, you will find it in the same docs if you go down a little further.
- (void)activateNativeScript:(id)sender {
NSString *source = #"var platform = require('nativescript-angular/platform');"
"var AppModule = require('./app.module' });"
"platform.platformNativeScriptDynamic().bootstrapModule(AppModule);";
[self _runScript: source];
}
I am including react native to my existing android application. I have one activity(say A) that is starting activity B. A activity sends some data in Bundle(consider some Custom Objects POJO) that B needs to render its view.
Now I want my Activity B to contain React Native View. How will the objects(POJO) come from Activity A be sent to React Native code**(JavaScipt Code)**?
I know some time has passed, but maybe someone is still looking for an answer.
If you want to send object from JavaScript to ReactNative (let's say as an method argument), which is unfortunately not mentioned in documentation:
let map = {
name: 'message1',
surname: 'message2',
}
NativeModules.BluetoothModule.sendObject(map);
And get it in android:
#ReactMethod
public void sendObject(ReadableMap readableMap){
Log.i(TAG, readableMap.toString());
//you can decode it here like:
String name = readableMap.getString("name");
//or just parse it as json
}
Now for the other way (from Java to Javascript) you can use either Callbacs, Promises or Events. This part is described in documentation here
From Java (Native Thread) to JavaScript (UI Thread):
#ReactMethod
public void sendObjectFromNative(Promise promise){
WritableMap map = Arguments.createMap();
map.putString("name", name);
map.putDouble("rotationDegrees", rotationDegrees);
map.putBoolean("isLandscape", isLandscape);
promise.resolve(map);
}
In JavaScript(UI Thread):
import { NativeModules } from 'react-native';
NativeModules.AwesomeModule.sendObjectFromNative().then(result=>console.log(result));
Above described how to do it vice versa
Yesterday I tried RadPHP, it looks very similar to Delphi with it's properties which is very nice.
I set-up some demo app to test it and after compiling (to android) it seems to be a compiled app that creates an webview to show HTML content. Sounds a littlebit weird to me or isn't it?
Does RadPHP creates a similair package for iOS or is this a real native app?
I cannot test it for a iOS version, because i don't have a key and i don't know anything about the compiled format.
If it does, will apple accept the created app?
EDIT/UPDATE:
I have compiled the app (with fake apple-id) which fails ofcourse but generates some C-Objective (.h,.m) files in the output directory. In the directory classes there is a file named AppDelegate.m. When you view the file you can see that it creates a HTML wrapper to a WebView (here is a snippet from the code):
/**
Called when the webview finishes loading. This stops the activity view and closes the imageview
*/
- (void)webViewDidFinishLoad:(UIWebView *)theWebView
{
// only valid if StreambutlerRemoteControl.plist specifies a protocol to handle
if(self.invokeString)
{
// this is passed before the deviceready event is fired, so you can access it in js when you receive deviceready
NSString* jsString = [NSString stringWithFormat:#"var invokeString = \"%#\";", self.invokeString];
[theWebView stringByEvaluatingJavaScriptFromString:jsString];
}
return [ super webViewDidFinishLoad:theWebView ];
}
- (void)webViewDidStartLoad:(UIWebView *)theWebView
{
return [ super webViewDidStartLoad:theWebView ];
}
/**
* Fail Loading With Error
* Error - If the webpage failed to load display an error with the reason.
*/
- (void)webView:(UIWebView *)theWebView didFailLoadWithError:(NSError *)error
{
return [ super webView:theWebView didFailLoadWithError:error ];
}
/**
* Start Loading Request
* This is where most of the magic happens... We take the request(s) and process the response.
* From here we can re direct links and other protocalls to different internal methods.
*/
- (BOOL)webView:(UIWebView *)theWebView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *url = [request URL];
if ([[url scheme] isEqualToString:#"http"] || [[url scheme] isEqualToString:#"https"]) {
return YES;
}
else {
return [ super webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType ];
}
}
CONCLUSION:
It will be a native app but is a wrapper to Webview (not all is native). It does exactly the same like said in the accepted answer. All mobile apps will be created using a Webview. I think RadPHP is only useful when you like the programming interface but is not necessary to create mobile apps (you can use phonegap directly instead). In the examples of RadPHP you still need a mac to create the app (you need XCode).
Apple accepts phonegap generated applications but it still not sure if it made it to the AppStore, it depends on what you doing with it. I think simple apps will made it.
See also: http://www.phonegap.com/faq
NOTICE on HTML produced by RadPHP
It is not W3C.
RadPHP uses PhoneGap to package up your app as an Android, iOS or Blackberry app. It generally works the same way for all three of those mobile platforms.