🚀 Welcome to MDriven Learn –  MDriven is now on Discord!  Don’t miss the latest Release Notes.
Finding angular scope from javascript
This page was created by Hans.karlsen on 2020-07-20. Last edited by Wikiadmin on 2026-07-29.

You can access the current Turnkey AngularJS ViewModel from JavaScript callbacks, page overrides, and EXT_Components when your code runs outside the normal AngularJS scope.

Choose the right way to access data

An AngularJS scope is the object AngularJS supplies to a bound element or directive. In Turnkey, the current client-side ViewModel root is available as ViewModelRoot on the scope obtained from #viewmodelWrapper.

Use the narrowest available access point. A directive or an element-local scope is preferable to looking up the page-wide ViewModel wrapper.

Situation Recommended approach Example
Your code is an external callback and has no element or scope Find the controller on #viewmodelWrapper and read ViewModelRoot. A payment-provider callback returns a value.
Your handler was called from an element on the page Get the scope from that element. An HTML onclick handler receives this.
You are building an EXT_Component Use the scope supplied to the component directive's link function. A component renders a canvas or subscribes to a browser event.
You can write a value to an existing bound input Set the input value and dispatch its change event so AngularJS handles the interaction. A third-party widget supplies a customer ID for an input.

For event binding and directives, see Documentation:Ng-click ( ngClick ) not working. For component structure and directive registration, see Documentation:EXT Components.

Get the ViewModel root from a JavaScript callback

Use this pattern when a callback is not invoked by AngularJS and therefore has no scope argument. For example, an integration callback can retrieve the active local ViewModel representation and assign a ViewModel property.

var vmroot = angular.element('#viewmodelWrapper').controller().$scope.ViewModelRoot;

vmroot.MyViewModelColumn = someValueFromACallBack;

Replace MyViewModelColumn with a column in the active ViewModel and replace someValueFromACallBack with the value received from the integration. Turnkey detects the ViewModel change and handles signaling to the server and UI binding.

Check that the controller is available

The page controller may not yet be available when an external script runs. Put the lookup behind a function and handle an unavailable result before writing data.

function GetVMRoot() {
    var ctrl = angular.element('#viewmodelWrapper').controller();
    if (ctrl !== undefined) {
        return ctrl.$scope.ViewModelRoot;
    }
    return undefined;
}

function receiveResult(result) {
    var vmroot = GetVMRoot();
    if (vmroot !== undefined) {
        vmroot.MyViewModelColumn = result;
    }
}

Do not assume that a callback arriving early can update the ViewModel. Keep the received value until the ViewModel root is available, or initialize the integration from an AngularJS directive as described in Documentation:EXT Components.

Get the scope from the current element

When code is called from a page element, retrieve the scope from that element rather than locating #viewmodelWrapper.

<button onclick="DelayedSave(angular.element(this).scope())">Save later</button>
function DelayedSave(scope) {
    setTimeout(function () {
        scope.StreamingViewModelClient.CallServerAction('JournalSeeker', 'LazySave');
    }, 2000);
}

This approach is useful in a page override, where an ordinary HTML onclick handler can pass the element's scope to JavaScript. A button can have both ng-click and onclick; both handlers run. The action-call API and the ViewModel/action names in the example are specific to that page, so use the names available in your own scope.

Let a bound input carry the value into the ViewModel

If an external script can target an existing input that AngularJS binds to a ViewModel property, update the input and dispatch a change event. AngularJS then processes the value as an input change.

function setResult(result) {
    // Input bound to ViewModel "BiljettCheck" and column "Id".
    var input = document.getElementById('BiljettCheck.Id');
    if (input === null) {
        return;
    }

    input.value = result;
    input.dispatchEvent(new Event('change'));
}

Setting input.value alone does not notify AngularJS. Dispatching change is the required step that lets AngularJS update the underlying ViewModel property; Turnkey can then discover and stream the change to the server.

Use the actual rendered element ID in your application. Verify that the element exists before updating it because dynamically rendered UI may not exist when an external script first runs.

Run code after AngularJS has bound the page

Register an AngularJS ready callback when initialization depends on AngularJS having bound the page.

angular.element(function () {
    console.log('page loading completed');
    startTheProcess();
});

This means AngularJS has bound the page; it does not guarantee that all server data has arrived. If your integration needs a particular ViewModel value, watch that value and act only when it has the expected content.

var initiated = false;

function startTheProcess() {
    var scope = angular.element('#viewmodelWrapper').controller().$scope;

    if (initiated) {
        return;
    }
    initiated = true;

    scope.$watch('ViewModelRoot.DataIsLoaded', function (newValue) {
        if (newValue === 'DataIsLoaded') {
            scope.ViewClient.CallServerAction(
                'SwedbankPay',
                'SCRIPTSCALLCheckIn',
                null);
        }
    });
}

The initiated flag prevents registering the same watcher more than once. In this example, the server action is delayed until the DataIsLoaded value indicates that the data is ready. Replace the ViewModel path, comparison value, ViewModel name, and action name with the values used by your application.

You can register additional watchers for individual values. For example, a callback can start work when a script URL becomes non-empty:

scope.$watch('ViewModelRoot.FetchedScriptUrlForCheckin', function (newValue) {
    if (newValue && newValue !== '') {
        DoCheckInScript(newValue, scope);
    }
});

Prefer a directive when UI creation is the dependency

A document-ready callback is not the best lifecycle hook when your code must find dynamically created component elements. In an EXT_Component, AngularJS calls the directive's link function when it has the element and its scope. Initialize code that depends on that element in link.

function InstallTheDirectiveFor_example(streamingAppController) {
    streamingAppController.directive('example', ['$document', function ($document) {
        return {
            link: function (scope, element, attr) {
                // element is the rendered element; scope is its AngularJS scope.
                // Initialize browser integration here.
            }
        };
    }]);
}

InstallTheDirectiveFor_example(angular.module(MDrivenAngularAppModule));

For a concrete component directive pattern, see Documentation:EXT Components. If you are overriding a specific control's rendering and need its bound data object, see Documentation:UIOverride.

Common issues

Symptom Cause What to do
controller() is undefined The AngularJS controller has not been created, or the selector does not identify the Turnkey ViewModel wrapper. Check for undefined; initialize later through an AngularJS ready callback or directive.
The input displays the new value but the ViewModel does not change The DOM value was assigned without notifying AngularJS. Dispatch the input's change event after assigning value.
Initialization code cannot find a dynamically rendered element Document readiness occurred before the dynamic UI element was created. Put the initialization in an EXT_Component directive's link function.
ng-click cannot call a global JavaScript function AngularJS evaluates ng-click in its scope; a global function is outside that scope. Create a directive, or use the workaround described in Documentation:Ng-click ( ngClick ) not working.

See also