Jane Zhao
Jane Zhao

Reputation: 51

Calling angular function from swift

I'm trying to pass a value from swift to angularJS. The following is the swift code that calls the javascript function:

let userScriptSetToTrue = WKUserScript(
    source: "setTrackingToggleToTrue();",
    injectionTime: WKUserScriptInjectionTime.AtDocumentEnd,
    forMainFrameOnly: true
)
contentController.addUserScript(userScriptSetToTrue)

...
let js = "setTrackingToggleToTrue();"
self.webView?.evaluateJavaScript(js) {
     (_, error) in print(error)
}

And then the javascript calls the angular controller function:

<script>
    function setTrackingToggleToTrue() {
        angular.element(document.getElementById('trackingToggle')).scope().setTracking(true);
    }
</script>

But I got the following error in the javascript side:

TypeError: undefined is not an object (evaluating 'angular.element(document.getElementById('trackingToggle')).scope().setTracking')

It seems the document.getElementById('trackingToggle') is not available but I can see the following in the safari web inspector elements tab

<div class="list-group-item">
    <tracking-toggle-button
            id="trackingToggle"
            state="trackingEnabled"
            class="pull-right"
            on-toggle="updateTrackingPreference(newState);">
    </tracking-toggle-button>
    <label class="tracking-label">My Location</label>
</div>

What would be the reason for this error? or is there a better way to pass a value from swift all the way to angular controller?

Update The angular app is using UI routing and loads page content from different templates based on routing. I later find out the function setTrackingToggleToTrue() is called before its template is loaded and thus no trackingToggle is available at function call. Is there another way to call an angular function outside the controller without referencing any document element?

Thanks.

Upvotes: 2

Views: 2099

Answers (2)

Jane Zhao
Jane Zhao

Reputation: 51

It turns out WKWebView.evaluateJavaScript() can only call javascript on the top scope. The called top scope javascript can't use angular.element() to call an angular controller function based on an element loaded from a template based on routing.

I end up using an awkward way to work around it. The top scope java script set the toggle button value in local storage and the angular controller function will check the value till it's available. I also have to use $scope.$apply() manually to reflect the change in UI.

The same might be achieved via promise. I'll try the promise way later.

Upvotes: 1

Varit J Patel
Varit J Patel

Reputation: 3520

Please find working fiddle here: http://jsfiddle.net/varit05/06Ldnys8/25/

Controller:

var app = angular.module('app', []).controller('ctrl', function($scope) {
  $scope.inside = { 'name': 'guy', 'idk': 'blah' };
  $scope.abc = function(value) {
    console.log(value);
  }
});

var getStuff= function() {
  var outside = angular.element(document.getElementById('stuff')).scope().abc('called');
 //console.log(outside.inside)
}

HTML:

<div ng-app="app" ng-controller="ctrl" id="stuff">
</div>

<button onclick="getStuff()">get stuff</button>

Hope it helps you!

Cheers!

Upvotes: 0

Related Questions