Updating a URL once AngularJS has finished processing it

Currently, I am utilizing Primo, an AngularJS app developed by Ex Libris. This application functions as a library catalog, enabling users to explore both physical and digital collections within the library. Despite not having direct access to the original AngularJS codebase, I do possess the capability to define and append AngularJS modules as needed.

My immediate task seems straightforward at first glance: establish a link within Primo that directs users to a third-party app, inclusive of a query string encompassing the specific item ID which the patron is viewing, for example,

https://library.example.edu/ill/primo.php?docID=cdi_proquest_ebookcentral_EBC7007769
.

By using the Primo control panel, I was able to successfully insert the link with options to set the URL, link text, and other parameters. The screenshot below illustrates this section of the control panel:

The URL Template field permits the input of the desired link URL and offers placeholders to incorporate data related to the item. For instance, {rft.atitle} would be substituted with the article title. Regrettably, it lacks a method to insert the docID directly. Although I attempted including an AngularJS token like {{pnx.control.recordid[0]}}, presumably to fetch the required data, the UI rejected this input as an invalid parameter. Consequently, I specified my URL as

https://example.com/incoming.php?docID=replaceMe
. My intention was to craft JavaScript logic to extract the docID from the dynamic URL and update the link prior to user interaction.

However, upon implementation, Primo generated the link as an AngularJS component, generating the following code snippet when rendered on an actual page:

<md-list-item ng-repeat="service in $ctrl.filteredServices() track by $index" role="listitem">
    <button type="button" ng-transclude="" (click)="$ctrl.onClick(service)" aria-label="Interlibrary Loan, opens in a new window">
        <span ng-if="service['service-type'] === 'OvL'">
            <a
                ng-href="https://library.example.edu/ill/primo.php?docID=replaceMe"
                ng-click="$event.preventDefault()"
                translate="Interlibrary Loan"
                href="https://library.example.edu/ill/primo.php?docID=replaceMe"
            >Interlibrary Loan</a>
        </span>
    </button>
</md-list-item>

To enhance clarity, extraneous container DIVs and styling information have been excluded. Moreover, the indentation has been refined for readability.

Evidently, the link incorporates both an ng-href attribute and an href attribute storing the initial URL inputted. Despite attempting to dynamically update these attributes via Javascript manipulation, the modifications failed to persist when clicking the link, subsequently redirecting users to the unaltered URL. Supposedly, AngularJS retains the original URL within its internal data structure during processing, thereby circumventing any alterations made to the corresponding tag attributes.

Notably, a redundant <button> encompassing the link was introduced, effectively replicating the functionality of the link itself. While this design choice appears aimed at expanding the clickable area, managing the URL updates across both elements complicates the debugging process.

In essence, my goal centers around substituting the placeholder "replaceMe" with the actual docID and updating the associated URL accordingly. Although a pragmatic solution could involve manipulating the DOM to replicate and replace the entire HTML structure produced by AngularJS, such brute force tactics contradict my preference of collaborating harmoniously with AngularJS rather than working against its innate functionalities.

This predicament raises two pivotal inquiries:

  1. How can I pinpoint where AngularJS stores the URL? Identifying the exact location of this data remains elusive.
  2. Once located, is there an AngularJS-specific approach to modifying it? Adhering to AngularJS conventions is imperative to avoid unforeseen conflicts.

After exhaustively scouring through search results predominantly yielding AngularJS coding resources instead of practical insights on real-time data updates, my continuous efforts over the past three days have yet to yield substantial progress. Your guidance or assistance would be immensely valued and appreciated.

UPDATE, six days later:

Progress is being made, albeit at a gradual pace.

An initial hurdle encountered involved the discovery that debug info had been disabled by the original developers at Ex Libris:

$compileProvider.debugInfoEnabled(false)

Further exploration led me to stumble upon a Firefox extension titled AngularScope, promising the ability to inspect scopes linked to arbitrary DOM elements. Upon installation, the extension alerted me regarding the disabled debug info, prompting me to re-enable it via the console with:

angular.reloadWithDebugInfo()

Discovering that the scope for my current link contains an object referred to as service housing a property named

link-to-service</code shed light on where the URL data was stored. Subsequently, I captured a reference to the target link under investigation denoted as <code>myLink
and formulated the following script:

angular.element(myLink).scope().$apply(function () {
    angular.element(myLink).scope().service["link-to-service"] = newURL;
});

Remarkably, the execution yielded successful outcomes! Updating the URL content, users are now directed to the correct destination by clicking either the link or accompanying button.

However, a crucial caveat emerged - this method exclusively operates when AngularJS's debug info feature is enabled. By default, this option remains inactive, causing the AngularJS scopes to remain detached from the DOM, rendering angular.element(myLink).scope() inaccessible and ineffectual.

An attempt to restore access to the scopes by executing the following script methodology provided limited success:

var scope;
angular.element(document.body).injector().invoke(function ($rootScope) {
    scope = $rootScope;
    console.log(scope);
});

Unfortunately, encountering a similar setback where

angular.element(document.body).injector()
returned as undefined suggests potential revisions to AngularJS framework may have transpired since the referenced answer back in 2015.

A strategic maneuver entailing the establishment of a novel AngularJS module integrated with its .run() function to interact with the DOM post-AngularJS bootstrap phase but premised before template compilation had little to no impact, likely due to delayed loading subsequent to page initialization by the time my module integration took effect.

Contemplation ensued surrounding leveraging event handling mechanisms like DOMContentLoaded to preemptively modify URLs ahead of AngularJS script injection. Realization dawned upon realizing the counterproductive nature of such attempts given my scripts were appended by AngularJS itself; essentially translating to missed opportunities for timely intervention towards URL updates.

Hence, the focal issue stands resolute: How does one obtain access to an AngularJS component's scope sans debug info?

If empowered with scope accessibility, the URL update procedure becomes trivial. Alas, despite laboring intensively over the course of nine days, achieving this objective persists as an ongoing challenge. A growing temptation lingers urging one towards devising a potential workaround involving radical JavaScript interventions aimed at reconstructing the AngularJS-generated HTML structure entirely in favor of facilitating URL adjustments. While undoubtedly cumbersome and grossly inefficient, the reassurance of attaining successful outcomes merits contemplation amid looming hurdles stalling further progress.

Answer №1

After countless hours diving into documentation, scouring StackOverflow answers, browsing various websites, and dissecting AngularJS code morsels, I finally cracked the code.

Setting

$compileProvider.debugInfoEnabled(false)
detaches scopes from the document, but they are still accessible with some effort.

Referencing a helpful snippet provided by user Estus Flask in this StackOverflow answer, I realized that targeting an AngularJS-initialized element was crucial for success.

var scope;
angular.element(document.body).injector().invoke(function ($rootScope) {
  scope = $rootScope;
  console.log(scope);
});

Initially encountering issues due to an unconnected <body> within my app, redirecting attention to an AngularJS-initialized element like <primo-explore> proved to be the solution. Additionally, thanks to user sahbeewah's comment on another insightful answer discussing AngularJS scopes, a more concise syntax was presented.

To obtain the root scope, locate the main element of your app marked by the ng-app attribute or via a custom element such as <primo-explore>, then retrieve the root scope with:

let angRoot = document.getElementsByTagName("primo-explore")[0];
let rootScope = angular.element(angRoot).injector().get('$rootScope');

Depending on the scenario, methods like getElementById or getElementsByQuerySelector may be necessary instead of using getElementsByTagName.

With the root scope acquired, an insightful recursive function shared by user larspars facilitated discovering all defined scopes across the page.

// Function definition omitted for brevity
getScopes($rootScope)

Subsequently, leveraging a simple for loop allowed me to sift through over 600 scopes for the desired one:

// Code block iterating through scopes for target selection

Following this process, cleaning up redundant memory usage by setting scopes = undefined; and updating links ensured accurate display of information.

Through perseverance spanning nearly two work-weeks, victory was achieved in rectifying a single link - a triumph well-earned.

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

Reproducing scripts in Google Tag Manager and React/Next applications

Currently, I am delving into the realm of Google Tag Manager and React + Next.js for the first time. This experience is proving to be quite intriguing as my familiarity with GTM is limited and my exposure to React is even less. Nonetheless, it's not a ...

Is there a way to selectively import specific functions from a file in NextJs/React rather than importing the entire file?

Imagine this scenario: we have two files, let's call them File A - export const a = () => {} export const b = () => {} Now, consider importing this into File B - import { a } from 'path' When I tried running npm run analyze, it showe ...

Why does the return value of a function in Node.js and JavaScript sometimes appear as undefined?

I am completely stumped by this issue. I've been trying to figure it out, but so far, no luck.. this is the code snippet function part1(sql, controltime, headers_view, results_view, tmp){ var timerName = "QueryTime"; var request = ne ...

javascriptchange the format from <string, string[]> to <string, string>

In my code, I came across a JavaScript object that consists of key/value pairs. Each value in this object is an array of strings: var errors = { "Message": ["Error #1", "Error #2"], "Email": ["Error #3", "Error #4"] }; My goal is to transform thi ...

Discovering all instances of a particular name in JSON using incremented values: a guide

I am looking to automatically detect every occurrence of a specific name in my JSON data. { "servergenre": "Classic Rock", "servergenre2": "pop", "servergenre3": "rock", "servergenre4": "jazz", "servergenre5": "80s", "serverurl": "http://www.n ...

Next.js Server Error: ReferenceError - 'window' is undefined in the application

I am currently in the process of integrating CleverTap into my Next.js application. I have followed the guidelines provided in the documentation Web SDK Quick Start Guide, however, I encountered the following issue: Server Error ReferenceError: window is ...

Storing information upon refresh in Angular 8

When it comes to inter-component communication in my Angular project, I am utilizing BehaviourSubject from RXJS. Currently, I have a setup with 3 components: Inquiry Form Where users enter an ID number to check for summon-related information. This data ...

Navigating through tabs in a Meteor application: How to maintain the active tab when using the back button

I am working on a multi-page meteor application where each page includes a navigation template. To switch between pages, I am using iron-router. The user's current page is indicated by setting the appropriate navigation link's class to 'ac ...

Step-by-step guide on transforming jQuery code into Vue JS:

Recently delving into Vue, attempting to translate previous JS + JQuery code into Vue Here is the list I'm working with: <ul id="progressbar"> <li class="active">integration Ip's</li> <li>T ...

Dealing with rejected responses in AngularJS using UI-Router

I am utilizing a Service in Angular to encapsulate my API requests: var ConcernService = { list: function (items_url) { var defer = $q.defer(); $http({method: 'GET', url: api_url + items_url}) .succe ...

Exploring the possibilities of integrating JavaScript with Flash technology

Currently, there is a simple Flash project in development that connects to an RTMP server and streams video and audio from a webcam. The project allows users to create a stream with a specific name. This project also features an input for entering a strea ...

Using rxjs for exponential backoff strategy

Exploring the Angular 7 documentation, I came across a practical example showcasing the usage of rxjs Observables to implement an exponential backoff strategy for an AJAX request: import { pipe, range, timer, zip } from 'rxjs'; import { ajax } f ...

Is it possible to analyze an API call and determine the frequency of a specific field?

Code: var textArray = new Array(); var allText = results.data._contained.text; for (var i = 0; i < allText.length; i++) { var text1 = allText[i]; var textHtml = "<div id='text_item'>"; textHtml += "& ...

Troubleshooting JSON Array Index Problems

I'm having trouble reaching index 3 in the array using the javascript options on my webpage. The final question "are you satisfied with your choice?" is not showing up for me. I'm not sure what I might be missing or doing incorrectly in this sit ...

[Error]: Unable to access the 'getCroppedCanvas' property as it is undefined in React Cropper

I am currently utilizing the "React Cropper" library (https://www.npmjs.com/package/react-cropper). I have included this code snippet (similar to many examples): import React from 'react'; import Cropper from 'react-cropper'; export ...

Seamlessly adaptive HTML5 video playback

Has anyone figured out how to make an HTML5 video in an absolutely positioned <video> element resize to fit the window width and height without ever getting cropped? Many solutions I've come across rely on using the <iframe> tag, which is ...

Jest - Silence greets the test results

Struggling with Jest has been a common theme for me ever since I first attempted to use it. Regardless of the tests I run or the options I try to pass to Jest, I never seem to get the expected 'Pass' or 'Fail' results in the console. In ...

Tips on modifying a Vue app's property externallyHere are some techniques on how

I am curious about changing the property of a vue app from an external source. I want to create a new vue app named 'app' and set 'app.propertyName' to 'someValue'. However, my attempt below did not yield the desired outcome. ...

Navigating a collection of objects after a button is clicked

My current library project involves looping through an array of objects to display them on a grid based on input values. Here is the code snippet for my loop: const addBook = (ev) => { ev.preventDefault(); let myLibrary = []; let bookIn ...

Tips for transferring information from ng-view to the navbar on the index.html page using AngularJS

Recently, I embarked on a journey to learn the MEAN stack and decided to challenge myself by building an authentication page from scratch instead of using the out-of-the-box solution. My main struggle lies in updating texts on my navbar. Here's a snip ...