AngularJS: Incorporating directives into dynamically generated HTML from an API call

I have been working with a REST API that returns pre-generated HTML like this:

<p class="p">
    <sup id="John.3.16" class="v">16</sup>
    <span class="wj">“For </span>
    <span class="wj">God so loved </span>
    <span class="wj">the world,</span>
    <span class="wj">that he gave his only Son, that whoever believes in him should not </span>
    <span class="wj">perish but have eternal life.</span>
</p>

This has posed a new challenge for me in learning AngularJS. Since I can't control the HTML from the API, I wanted to build a class directive on the "v" class to enable an ng-click attribute on the verse number and pass the verse information to another part of my application.

Here is the current code I have, which doesn't seem to be functioning as expected:

var app = angular.module('ProjectTimothy');

app.filter("sanitize", ['$sce', function($sce) {
    return function(htmlCode){
        return $sce.trustAsHtml(htmlCode);
    }
}]);

app.controller("timothy.ctrl.search", ['$scope', '$http', function($scope, $http){
    $scope.url = "http://apiendpoint.com/";
    $scope.copyright = "";

    $scope.search = function() {
        $http.post($scope.url, { "query" : $scope.searchwords, "version": "eng-ESV"})
        .success(function(data, status) {
            $scope.copyright = data.response.search.result.passages[0].copyright;
            $scope.result = data.response.search.result.passages[0].text; 
        })
        .error(function(data, status) {
            $scope.data = data || "Request failed";
            $scope.status = status;         
        });

    };
}]);

app.directive("v", ['$compile', function($compile) {
    return {
        restrict: 'C',
        transclude: true,
        link: function(scope, element, attrs) {
            element.html("<ng-transclude></ng-transclude>").show();
            $compile(element.contents())(scope);
        },
        scope: { id:'@' },
        /*template: "<ng-transclude></ng-transclude>",*/
        replace: false
    };
}]);

HTML template populated with the returned API HTML:

<div class="bible_verse_search_container" ng-controller="timothy.ctrl.search">
    <div class="input-group">
        <input type="text" class="form-control" placeholder="Bible Verse To Read (i.e. John 11:35)" ng-model="searchwords">
        <span class="input-group-btn">
            <button class="btn btn-default" type="button" ng-click="search()">Search</button>
        </span>
    </div>
    <div class="well" ng-show="copyright" ng-bind-html="copyright | sanitize"></div>
    <div ng-bind-html="result | sanitize"></div>
</div>

I hope to populate the bottom div with HTML containing clickable verse numbers by converting them into directives. My goal is to make each verse number clickable and extract the ID attribute to send it along with user content back to my API.

If anything is unclear, please let me know. I'll continue working on this and update if I find a solution.

IN PROGRESS

Referenced this question for guidance:

I'm contemplating whether converting the verse display section into a directive and having the search controller populate a scope variable with server HTML as the directive template would be a better approach... thinking hard!

Answer №1

In my opinion, implementing your second idea by converting the section where the verse is displayed into a directive would be a great approach.

Instead of having:

<div ng-bind-html="result | sanitize"></div>

You could create a directive like this:

<verse-display verse-html="{{result}}"></verse-display>

The directive definition could be as follows:

app.directive('verseDisplay', ['$compile', function($compile) {

    function handleVerseClick(e) {
        var verseNumber = e.target.id;

        // Add your functionality for handling the verse number here
    }

    return {
        restrict: 'E',
        scope: {
            verseHtml: '@'
        },
        replace: true,
        transclude: false,
        template: '<div ng-bind-html="verseHtml | sanitize"></div>',
        link: function(scope, element, attrs) {
            $compile(element.contents())(scope);
            element.on('click', '.v', handleVerseClick);
        }
    };
}]);

This way, you can easily add your own click event handler to the element.

Check out this demo. (Remember to open the console to view the logged verse numbers.)

Answer №2

While this may not be the most prudent decision on my part, I have to admit that the code is quite intriguing. I can't wholeheartedly recommend running it, but if you're feeling brave, check out this jsfiddle.

I must caution that using this code can potentially execute directives beyond what you intend, opening up various security vulnerabilities. However, if you are confident in the source of your HTML content, feel free to give it a try.

For the complete code snippet, please refer to the fiddle link provided above:

function riskyCompile($compile) {
    return function (scope, element, attrs) {
        var compileWatch = scope.$watch(
          function (scope) { return scope.$eval(attrs.riskyCompile); },
          function (risky) {
            element.html(risky);
            $compile(element.contents())(scope);
            // for optimized performance, only compile once before un-watching
            if (scope.runOnce) {
                // stop watching
                compileWatch();
            }
          });
    };
}

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

Creating self-referencing MongoDB schema with post routes in nodejs: A step-by-step guide

I am currently working on building a nested parent-child system where the child schema mirrors that of the parent. Below is the schema for the parent, where children refer back to the parentSchema: var mongoose = require("mongoose"); var parentSchema ...

jQuery Hide/Show Not Working as Expected

I am currently developing a Single Page Application using jQuery along with Semantic and Bootstrap for the UI. I have encountered an issue where jQuery is struggling to hide and show two elements on the same level, even though it works fine elsewhere in th ...

Exploring the concepts: AngularJS and the significance of undefined, null,

Take a look at this code snippet: <div ng-controller="SomeCtrl"> <div ng-show="someVariable.someProperty">Value of someProperty is set</div> </div> So, if I set $scope.someVariable.someProperty = 1; in my SomeCtrl controller, ...

Utilize the jQuery function as a callback argument

There is a jQuery plugin that I am currently working on: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head><title></title> <script type="text/javascript" sr ...

Retrieve a variable from a function and store it in a new variable outside of the function's scope

I am currently utilizing Chartist alongside VueJS and I am facing a challenge in accessing data within my chart. My objective is to retrieve information about the index of my click when interacting with the chart. While I am successful in obtaining this da ...

Node.js Error: RegEx Pattern Does Not Match

Here is a regex pattern to consider: ^[^-\s][a-zA-Z\sàèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝâêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿÄËÏÖÜŸçÇßØøÅåÆæœ\d!@#$\+%&\'*]{1,20}$ I tested this regex on w ...

Looking to toggle text back and forth with a simple click? Here's how!

My goal is to toggle the text ____ (a gap) back and forth with Word by simply clicking it. I prefer not to use a button, as I want the user to only need to click on the actual gap itself. I came across this page which outlines exactly what I am looking fo ...

Is there a way to configure a Mui textfield to only allow numeric input? It currently accepts numbers, the letter "e," and dashes

Take a look at my current code. <TextField error={values[1].error} fullWidth id="id" type="number" value={values[1].Id} placeholder='Enter ID' onChange={handleChange(1,'Id')} variant="outlined" inputProps={{min: 0,inputMode: &apos ...

Encountering a syntax error with JSON.parse() when using MVC 4 Razor with Jquery Ajax

I am encountering an issue with my MVC 4 application login page. I am attempting to utilize a jQuery ajax request to my login controller to check if the user exists. Here is the snippet of my jQuery code: $(document).ready(function () { $("#btnSub ...

A container that dynamically adjusts its height based on the number of elements added to it, retaining the expanded height even when items are removed

I have a div called v-chip-group that adjusts its height based on the content. It increases when more content is added and decreases when there is less content. My goal is to make sure that the height only increases, and if items are removed, the last upd ...

How can ETags be utilized in fetching data?

To prevent mid-air collisions caused by multiple users or processes modifying the same item in the database, I am considering using Etags for validation. Here is my proposed approach: The client will call a RESTful API to obtain an Etag from the header, ...

Is it better to utilize AngularJS $http service for making requests, or should I opt for jQuery AJAX if available

When working on my project, I rely on the angularjs framework and always enjoy utilizing the $http service for ajax calls. However, I have come across situations in the project where the UI is not directly impacted by the ajax call and angularjs bindings a ...

"What is the best way to transform tab navigation into a dropdown menu with the help

I have a collection of tabs currently on display. I am looking to transform them into a dropdown menu when the screen size changes, making them more responsive. Each set of tabs corresponds to different content. Despite trying various solutions found on s ...

Tips on incorporating a firebase object into your Angular application using $scope

As a beginner in coding, I would greatly appreciate a simple answer. I am attempting to treat a Firebase object as a JavaScript array for use in HTML via $scope. What is the most effective approach? Database: database Current code: var mainApp = angula ...

There's just something really irritating me about that Facebook Timer feature

Have you ever noticed the timers constantly updating on Facebook? Whether it's an Ajax Request triggered by a timer or a client-side timer, there are numerous timers being used. Does this affect the performance of the website, and is there something c ...

Vue3/Vuex - Issue with state change not being reflected in component when utilizing object destructuring

While working with Vue 3 and Vuex, I encountered an issue where Vue did not react to a state change when using object destructuring assignment to mutate the state. However, I found that Vue does react when the state is mutated by a mutation that only reass ...

Choosing several checkboxes and populating an array with values - HTML and JavaScript

I am working on a modal dialog that displays a table with checkboxes next to user names. My goal is to link these checkboxes to the respective names so that when a box is checked next to 'Jon' in the table, the name 'Jon' gets selected. ...

"I am eager to showcase the text upon pressing the Enter key, implementing an Immediately Invoked Function Expression

My input text box is supposed to store and display the output in an unordered list format when I hit enter. The function works fine without IIFE using onclick event, but it's not working with IIFE. Can anyone assist me with this issue? <html> ...

Making Angular2 Templates More Efficient with Array.prototype.filter()

I have a variable named networkInterface that includes an array called services. My objective is to create a checkbox input that indicates whether a specific service_id exists within the services array of the networkInterface. An illustration of JSON `int ...

merging values from objects within an array

My goal is to merge all the important values from the objects in this array. var currentInventory = [ { name: 'Brunello Cucinelli', shoes: [ {name: 'tasselled black low-top lace-up', price: 1000}, {n ...