javascriptangularjsdom

How to make out of an HTML text a DOM object


I have an app where a page generator creates a start page that is saved in the database. The saved information is just the outerHTML of the DOM object. To create the edit function, I need an inverse function that recreates the DOM object out of the HTML information. Unfortunately, assigning the decrypted information to the outerHTML section results in nothing, as the changes in the DOM object are ignored.

My question: How can I recreate a DOM object out of saved outerHTML information? If this is impossible, what would be the right way to do such a thing?

EDIT Thank you for your reply! Here are some snippets of code that show what I am doing. The prototype MainWidget is getting information from the HTML page & wants to assign the information. Used functions are part of another object (prototype) called Widgets.

var asserts = new Asserts();
var calls = new Calls();
var constants = new MainConst();
var strings = new Strings();
var widgets = new MainWidget();

MainWidget.method('assign2Widget', function (call, label, value, isValid) {
    isValid = toBoolean(isValid), value = strings.toString(value);

    if (calls.isValid(call) && strings.hasMinimalLength(label, 2)) {
        var section = (!label.includes('Builder')  // A marker, not an element!
                        && !label.includes(constants.NewsTicker))
            ? widgets.getSgSectionByLabel(document, label)
            : null;
        value = strings.decode(decodeURIComponent(value));      //<<<< Code decoded from DB

        if (asserts.isObject(section)) {
            section.outerHTML = value;
        } else if (label.includes(constants.Elements)) {
            if (asserts.isValid(infos.Elements) && infos.Elements.length > 0) {
                // HERE, THE CODE FROM DB SHOULD BE ADDED TO WIDGET.
                // The "label" gives the label of the widget, and the "value" is
                // what is to be assigned to.
            }
        } else if (label.includes(constants.NewsTicker)) {
            […]
        }
        […]
});

Widget.method('getElementTypeByLabel', function (thing, elementType, label) {
    if (asserts.isObject(thing)
        && strings.hasMinimalLength(elementType, 2)
        && strings.hasMinimalLength(label, 2)) {

        var element, elements = document.getElementsByTagName(elementType);

        if (asserts.isValid(elements)
            && asserts.isDefined(elements.length) && elements.length === 0) {

            elements = thing.children;
        }
        for (var i = 0; asserts.isDefined(elements.length) && i < elements.length; i++) {
            element = elements*;

            if (element.getAttribute !== null) {
                var labelName = element.getAttribute(constants.label);

                if (label === labelName && element !== null) {
                    return element;
                }
            } else {
                return null;
            }
        }
        // If the element was not found, search recursively the elements!
        for (var i = 0; asserts.isDefined(elements.length) && i < elements.length; i++) {
            return this.getElementTypeByLabel(elements*, elementType, label);
        }
    } else {
        // Error! //messages.invalidFunctionCall(functionName);
    }
    return null;
});

Widget.method('getSgSectionByLabel', function (element, label) {
    return this.getElementTypeByLabel(element, 'sg-section', label);
});

EDIT 2: I used only the changes done in the sg-section to recreate the contents when editing an existing page.

MainWidget.js:
[…]
MainWidget.method('assign2Widget', function (call, label, value, isValid) {
    isValid = toBoolean(isValid), value = strings.toString(value);

    if (calls.isValid(call) && strings.hasMinimalLength(label, 2)) {
        var section = (!label.includes('Builder')  // A marker, not an element!
                        && !label.includes(constants.NewsTicker))
            ? widgets.getSgSectionByLabel(document, label)
            : null;
        value = strings.decode(decodeURIComponent(value));

        if (asserts.isObject(section)) {
            section.outerHTML = value;
        } else if (label.includes(constants.Elements)) {
            if (asserts.isString(value) && value.includes(LeftBracket)) {
                value = arrays.toArray(value);  //value.replace(LeftBracket, '').split(RightBracket);
            }
            if (arrays.isArray(value)) {
                for (var i = 0; i < value.length; i++) {
                    infos.insertGridItem(value[i]);
                }
            }
            […]

MainInfos.js:
[…]
function MainInfos() {
    this.Elements;                      // Object for startGridGenerator
    this.insertGridItem;                // Function of startGridGenerator
    this.PrototypeName = 'MainInfos';
}

MainInfos.inherits(Infos);

MainWidget.method('assign2Widget', function (call, label, value, isValid) {
    isValid = toBoolean(isValid), value = strings.toString(value);

    if (calls.isValid(call) && strings.hasMinimalLength(label, 2)) {
        var section = (!label.includes('Builder')  // A marker, not an element!
                        && !label.includes(constants.NewsTicker))
            ? widgets.getSgSectionByLabel(document, label)
            : null;
        value = strings.decode(decodeURIComponent(value));

        if (asserts.isObject(section)) {
            section.outerHTML = value;
        } else if (label.includes(constants.Elements)) {
            if (asserts.isString(value) && value.includes(LeftBracket)) {
                value = arrays.toArray(value);  //value.replace(LeftBracket, '').split(RightBracket);
            }
            if (arrays.isArray(value)) {
                for (var i = 0; i < value.length; i++) {
                    infos.insertGridItem(value[i]);
                }
            }
        } else if (label.includes(constants.NewsTicker)) {
            […]
        }
});

startGridGenerator.js:
[…]
(function () {
    var moduleName = 'ivmGridBuilder';

    var controllerName = moduleName + Controller;

    angular.module(moduleName, [
        'ionic',
        'ui.sortable',
        'ivmColorpicker',
        'ivmIconpicker'
    ])
        .controller(controllerName, function ($scope,
                                              $ionicScrollDelegate,
                                              $ionicModal,
                                              $sce,
                                              CommunicationService) {
            […]
            /**
             * Inserts an existing grid item to the item's array
             *
             * @param item
             */
            $scope.insertGridItem = function (item) {
                if (asserts.isObject(item)) {
                    $scope.items.push(item);
                    infos.Elements.push(item);
                    $ionicScrollDelegate.resize()
                } else {
                    // First call with no valid item & "remember" this function
                    // by having a link to in in object MainInfos!
                    infos.insertGridItem = $scope.insertGridItem;
                }
            };

Solution

  • The solution should be like, you get a HTML string with the contents. Then you use the $sce.trustAsHtml() and assign to a scope variable which will ensure the HTML is safe then you can use the HTML below which has the directive ng-bind-html to render the HTML String as actual HTML.

    Here is a working example:

    var app = angular.module("angularApp", []);
    app.controller("appController", function($scope, $sce) {
      $scope.content = "This text is <em>html capable</em> meaning you can have <a href=\"#\">all</a> sorts <b>of</b> html in here.";
      $scope.output = $sce.trustAsHtml($scope.content);
    });
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <h2>Rendering HTML with angularJS</h2>
    <div ng-app="angularApp" ng-controller="appController">
      <div ng-bind-html="output"></div>
    </div>

    Another option is to use ng-sanitize.

    If you use ng-sanitize - Inputs are sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are then serialized back to properly escaped html string. This means that no unsafe input can make it into the returned string.

    Refer to this question for a more detailed explanation.


    var app = angular.module("angularApp", ['ngSanitize']);
    app.controller("appController", function($scope) {
      $scope.content = "This text is <em>html capable</em> meaning you can have <a href=\"#\">all</a> sorts <b>of</b> html in here.";
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.6/angular-sanitize.min.js"></script>
    <h2>Rendering HTML with angularJS</h2>
    <div ng-app="angularApp" ng-controller="appController">
      <div ng-bind-html="content"></div>
    </div>

    As seen in the above example there is no need for calling $sce.trustAsHtml(); since ngSanitize will take care of it automatically.