In Web front-end development, we often encounter the need to dynamically bind some HTML strings from the backend or dynamic splicing to the page DOM display, especially in the content management system (CMS: Content Management System) abbreviation), such needs are everywhere.
Readers of angular will definitely first think of ngBindHtml. Yes, angular provides us with this instruction to dynamically bind HTML. It will bind the calculated expression result to the DOM using innerHTML. However, the problem is not that simple. In Web security, XSS (Cross-site scripting, script injection attack) is a typical computer security vulnerability in Web applications. XSS attacks refer to injecting executable client-side code into web pages and successfully executing them by the browser to achieve the purpose of the attack, forming an effective XSS attack. Once the attack is successful, it may obtain some sensitive information of the user. Changing the user experience, inducing users and other illegal behaviors, sometimes XSS attacks are combined with other attack methods, such as SQL injection attacks on servers and databases, Click hijacking, relative link hijacking, etc. to implement phishing. The harm it brings is huge, and it is also a web The number one enemy of security. For more web security issues, please refer to the wiki https://en.wikipedia.org/wiki/Cross-site_scripting%E3%80%82
In angular, the default is not to believe the added HTML content. The added HTML content must first use $sce.trustAsHtml to tell angular that this is trustworthy HTML content. Otherwise you will get $sce:unsafe exception error.
Error: [$sce:unsafe] Attempting to use an unsafe value in a safe context.
The following is a demo that binds a simple angular link:
HTML:
<div ng-controller="DemoCtrl as demo"> <div ng-bind-html="demo.html"></div> </div>
JavaScript:
angular.module("com.ngbook.demo", []) .controller("DemoCtrl", ["$sce", function($sce) { var vm = this; var html = '<p>hello <a href="https://angular.io/">angular</a></p>'; vm.html = $sce.trustAsHtml(html); return vm; }]);
For simple static HTML, this The problem is solved. But for complex HTML, complexity here refers to HTML templates with angular expressions and instructions. For them, we not only hope to bind large DOM displays, but also hope to get angular's powerful two-way binding mechanism. ngBindHhtml will not be associated with $scope for two-way binding. If there are ngClick, ngHref, ngSHow, ngHide and other angular instructions in HTML, they will not be compiled. Clicking these buttons will not cause any reaction. The expression of binding The formula will not be updated. For example, if you try to change the last link to: ng-href="demo.link", the link will not be parsed, and the original HTML string will still be seen in the DOM.
To take effect, all instructions in angular need to go through compile. Compile contains pre-link and post-link, and is connected to specific behaviors before they can work. In most cases, compile will be automatically compiled when angular starts. But if it is a dynamically added template, you need to compile manually. Angular provides us with the $compile service to implement this function. The following is a more general compile example:
HTML:
<body ng-controller="DemoCtrl as demo"> <dy-compile html="{{demo.html}}"> </dy-compile> <button ng-click="demo.change();">change</button> </body>
JavaScript:
angular.module("com.ngbook.demo", []) .directive("dyCompile", ["$compile", function($compile) { return { replace: true, restrict: 'EA', link: function(scope, elm, iAttrs) { var DUMMY_SCOPE = { $destroy: angular.noop }, root = elm, childScope, destroyChildScope = function() { (childScope || DUMMY_SCOPE).$destroy(); }; iAttrs.$observe("html", function(html) { if (html) { destroyChildScope(); childScope = scope.$new(false); var content = $compile(html)(childScope); root.replaceWith(content); root = content; } scope.$on("$destroy", destroyChildScope); }); } }; }]) .controller("DemoCtrl", [function() { var vm = this; vm.html = '<h2>hello : <a ng-href="{{demo.link}}">angular</a></h2>'; vm.link = 'https://angular.io/'; var i = 0; vm.change = function() { vm.html = '<h3>change after : <a ng-href="{{demo.link}}">' + (++i) + '</a></h3>'; }; }]);
A directive called dy-compile is created here, which will first listen for bindings Changes in the value of the attribute html. When the html content exists, it will try to first create a subscope, and then use the $compile service to dynamically connect the incoming html and replace the current DOM node; the reason for creating the subscope here is It is convenient to destroy the scope easily every time the DOM is destroyed, remove the watchers function brought by HTML compile, and when the last parent scope is destroyed, it will also try to destroy the scope.
Because of the above compile compilation and connection, the ngHref instruction can take effect. Here is just an attempt to give an example of dynamic compile angular module. For specific implementation methods, please refer to your business to declare specific directives.
The above is the detailed content of How to dynamically bind HTML. For more information, please follow other related articles on the PHP Chinese website!