Problem with manual initialization and overriding angular services during configuration stage

Trying to make my Angular application work in live mode and prototype mode by overriding services. When the prototype mode is activated in the config, the bootstrap process is paused, mock service files are loaded, and then bootstrapping resumes.

Below is a simplified version of the source code for the demo:

App.js

The main app that calls a service for simulation and displays the result. It requires the StubApp for service overrides.

var app = angular.module('app', ['StubsApp'])

.run([ '$rootScope', 'DataService', function($scope, DataService){
   DataService.getData().then(function(data){
    $scope.name = data;
  });
}]);

DataService.js

A simple service registered with the app.

function DataService($q){
  this.getData = function(){
       return $q.when('I am Real!!');
  }
}
DataService.$inject = ['$q'];
angular.module('app').service('DataService',DataService);

Driver.js

Config registration file that sets up mocking.

angular.module('app').config(['$provide', 'stubServiceProvider', 'AppConfig', function($provide, stubProvider, AppConfig){
    if(AppConfig.StubEnabled){
       stubProvider.loadStubsInModule('plunker');
    }
}]);

StubProvider.js

An interface similar to angular.module for registering stub services. It loads mock services from stubs.json by halting the bootstrap, allowing an App to override existing services with the ones in stubs.json.

var Stubs = {},
    modules = [];
function module(moduleName) {
    return {
        mock: function (func) {
            modules.push(func);
        }, get: function () {
            return modules;
        }
    };
}
Stubs.module = module;

loadStubs();

function loadStubs() {
    window.name = "NG_DEFER_BOOTSTRAP!";
    var injector = angular.injector(['ng']);
    var $q = injector.get('$q');
    var $http = injector.get('$http');
    var scripts = [];

    $http.get('stubs.json').then(function (result) {
        scripts = result.data.map(function (src) {
            var script = document.createElement('script');
            script.src = src;
            script.async = true;
            document.head.appendChild(script);

            var deferred = $q.defer();

            script.onload = function () {
                deferred.resolve();
            };
            return deferred.promise;
        });

        $q.all(scripts).finally(function () {
            angular.element().ready(function () {
                angular.resumeBootstrap();
            });
        });
    });
}

//The provider that handles the service overriding
angular.module('StubsApp', []).provider('stubService', function ($provide) {
    ...... //Code in plunker
});

DataService Mock.js

A mock service using the Stubs interface to register the mock

Stubs.module('app').mock(MockService)
. It includes a property stubFor="serviceName" which specifies the service it mocks.

function MockService($q, $log){
this.getData = function(){
       return $q.when('I am Mock!!');
  }
}
MockService.$inject = ['$q', '$log'];

MockService.stubFor="DataService";

Stubs.module('app').mock(MockService);

stubs.json

A JSON file listing mock services.

["DataServiceMock.js"]

index.html

<script src="app.js"></script>
<script src="DataService.js"></script>
<script src="Driver.js"></script>
<script src="stubprovider.js"></script>

When Driver.js is moved above DataService.js, the mocking stops working. The specific code causing the issue in "StubProvider.js" is:

   Stubs.module(moduleName).get().forEach(function (mod) {
        var serviceName = mod.stubFor;
        var ctor = mod;
        if (serviceName) {
            $provide.service(serviceName, ctor);
        }
    });

Check out the Demo Plnkr. Commenting out the line in Driver.js will show real service output instead of mock service. To replicate the issue, move Driver.js before DataService.js in the index.html file, preventing the override of DataService with MockDataservice.

  • Why does the order of config registration matter when the config phase should run before service instantiation?

  • Is there a better way to ensure all scripts are loaded before resuming the bootstrap process without using the deferred pattern?

Answer №1

Utilize the createElement and appendChild DOM techniques, along with the src and onload attributes of the script element, and employ the bootstrap, element, and injector methods from AngularJS:

    /* Create script element */
    var script = document.createElement('script');
    /* Set src */
    script.src = "https://ajax.googleapis.com/ajax/libs/angularjs/1.3.1/angular.min.js";
    /* Append to head */
    document.getElementsByTagName("head")[0].appendChild(script);
    
    function performTask()
      {
      //local datastore
      this.mvvm = {};
    
      //template string
      var html = "<div>ID: {{$id}}</div>".replace("|",'"',"g");
      //template object
      var template = angular.element(html);
      //template transformer
      var compiler = angular.injector(["ng"]).get("$compile");
      //template result
      var linker = compiler(template);
      //scope object
      var scope = angular.injector(["ng"]).get("$rootScope");
      //scope binding
      var result = linker(scope)[0];
    
      /* Append result to body */
      document.body.appendChild(result);
    
      /* Render */
      angular.bootstrap(document, ['ng']);
      }
    
    script.onload = performTask;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a method to retrieve the bounds (northeast and southwest) of the map display when there is a change in the bounds, center, or view area?

In my NextJs project, I am utilizing the TomTom Map SDK to implement a feature where, upon loading the map based on its bounds, I query for nearby restaurants in that specific area. Additionally, when there are zoom or drag events on the map, I want to mak ...

AngularJS Bootstrap CSS implementation for Hand Cursor Grab

Is there a way to ensure the cursor is always a hand / grab for sortable containers in AngularJS & Bootstrap? What specific HTML modification would achieve this change? <div ui-sortable="sortableOptions" ng-model="responses" class="container-f ...

Synchronize the currently chosen option between the radio button and dropdown menu

Issue Summary I am working with two forms - one with Radio Buttons and one with Dropdowns (select). Both have a similar structure where changing the value of one element should reflect in the other. Constraints: The ID and name attributes of each radi ...

Packages starting with @ are found in the Node.js ecosystem

What's the deal with libraries in Node.js starting with @ symbols? Is there a specific convention for this? I feel like I'm missing something obvious here. ...

With TypeScript, you have the flexibility to specify any data type in the generic types when using the axios.get method

axios.get('/api') When working with TypeScript as shown above, it is important to designate types for better clarity. This allows us to reference the type definition of axios, like so: (method) AxiosInstance.get<any, AxiosResponse<any> ...

What can I do to prevent object.style.backgroundColor in my JavaScript file from overriding my class:hover effect in my CSS file?

I am looking to enhance my user experience by allowing them to either hover over an option to highlight it or press a number key to highlight the corresponding option. However, I am facing an issue where the object.style.backgroundColor function overrides ...

Guide on verifying jQuery selector using jasmine

We are currently tasked with testing a function that determines the top position of an element. Specifically, we need to evaluate the top position of an element with the ID 'test'. function findTopPosition(){ var targetElement = $('#tes ...

Best practices for avoiding string concatenation in Oracle-db with Node.js

Is there a way to prevent SQL injection caused by string concatenation in the SQL query? The searchParameter and searchString parameters, which come from a GET request, are optional. They should be added to the WHERE clause to filter results based on user ...

Can we retrieve the CSS of an element?

Using Selenium's webdriverJS, I have automated tasks on an HTML5 page. To incorporate a CSS selector into a function, I had to rely on XPath for selecting elements: var complexXpath = "//*/div/a"; /* This is just an example */ var element = mydri ...

Transferring variables between vanilla JS and Angular 2: A guide

I am facing a challenge where I need to retrieve an object title from vanilla JavaScript and then access it in my Angular 2 component. Currently, I am storing the variable in localStorage, but I believe there must be a better approach. The issue arises wh ...

Angular 12: An issue has occurred due to a TypeError where properties of undefined cannot be read, specifically pertaining to the element's 'nativeElement

Within my phone number input field, I have integrated a prefixes dropdown. Below is the code snippet for this feature. HTML code in modal <div class="phone" [ngClass]="{ 'error_border': submitted && f.phoneNumber.er ...

Swapping out the initial icon in a v-list-item with Vuetify

I have a list of items with icons that need to change when clicked. The issue I am facing is that all icons are changing at once because they share the same v-model. How can I modify my code so that only the icon being clicked changes? Here is my current i ...

Next.js application shows 404 errors when trying to access assets within the public directory

I've been struggling to display the favicon for my site (which uses next.js). Despite going through numerous Stack Overflow posts and tutorials, I'm feeling increasingly frustrated. The structure of my project, particularly the public directory, ...

Extracting names from HTML can be done easily by looking for the "@" symbol that the

My HTML structure looks like this: <table id="table-15244" cellspacing="0" class="comment"><tbody> <tr id="comment-37"> <td style="color: #999" class="NV"> </td> <td class="hidden-mob"> ...

Keystroke to activate Ant Design Select and start searching

I'm currently using the 'react-hotkeys-hook' library and have successfully implemented a hotkey that logs in the console when triggered (via onFocus()). My goal now is to use a hotkey that will open a Select component and add the cursor to i ...

What is the best way to send a list generated from ng-repeat to an API using AngularJS?

HTML <body class="res layout-subpage" ng-app="cartApp" ng-controller="cartController"> <table class="table table-bordered"> <thead> <tr> <td class="text-center">Image</td> ...

Variables for NPM Configuration

After researching the best way to securely store sensitive information, I decided to utilize the config package and environment variables for added security. Here is how I implemented this setup: Created a config directory containing two files: default.js ...

Utilizing AngularJS for toggling data with ng-click

In my project, I have a unique issue with a list item that toggles a modal and sets a parameter using ng-click. Strangely, when calling a specific function in another location, Course.SelectedCourse returns as undefined, even though Course.ID has a valid ...

Conceal rows in a table that are not selected using Jquery when the table has been loaded from a given URL

Below is the HTML code for an user search input field and the results table: <div class="input-group"> <input type="text" id="q" class="form-control" placeholder="Search for User"> <span class="input-group-btn"> ...

Customize your Jquery UI Calendar with Changing Background Images for Each Month!

How can I change the background of jQuery UI datepicker based on the selected month? I have created 12 classes, each representing a month, and the selected month already has the background. I tried using "onChangeMonthYear: function(year, month, inst) { ...