데이터베이스에서 동적 HTML 문자열 컴파일


132

그 상황

Angular 앱 내에는 ng-bind-html-unsafe 속성을 가진 div를 포함하는 컨트롤러가 지원하는 Page라는 지시문이 중첩되어 있습니다. 이것은 'pageContent'라는 $ scope var에 할당됩니다. 이 var는 데이터베이스에서 동적으로 생성 된 HTML을 할당받습니다. 사용자가 다음 페이지로 넘어 가면 DB에 대한 호출이 이루어지고 pageContent var가이 새로운 HTML로 설정되어 ng-bind-html-unsafe를 통해 화면에 렌더링됩니다. 코드는 다음과 같습니다.

페이지 지시문

angular.module('myApp.directives')
    .directive('myPage', function ($compile) {

        return {
            templateUrl: 'page.html',
            restrict: 'E',
            compile: function compile(element, attrs, transclude) {
                // does nothing currently
                return {
                    pre: function preLink(scope, element, attrs, controller) {
                        // does nothing currently
                    },
                    post: function postLink(scope, element, attrs, controller) {
                        // does nothing currently
                    }
                }
            }
        };
    });

페이지 지시문 템플릿 (위의 templateUrl 속성의 "page.html")

<div ng-controller="PageCtrl" >
   ...
   <!-- dynamic page content written into the div below -->
   <div ng-bind-html-unsafe="pageContent" >
   ...
</div>

페이지 컨트롤러

angular.module('myApp')
  .controller('PageCtrl', function ($scope) {

        $scope.pageContent = '';

        $scope.$on( "receivedPageContent", function(event, args) {
            console.log( 'new page content received after DB call' );
            $scope.pageContent = args.htmlStrFromDB;
        });

});

작동합니다. 브라우저에서 DB의 페이지 HTML이 멋지게 렌더링되는 것을 볼 수 있습니다. 사용자가 다음 페이지로 넘어 가면 다음 페이지의 내용 등이 표시됩니다. 여태까지는 그런대로 잘됐다.

문제

여기서 문제는 페이지 내용 내에 대화 형 내용을 포함하려는 것입니다. 예를 들어, HTML은 사용자가 그것을 클릭 할 때 팝업 모달 창을 표시하는 것과 같은 멋진 작업을 수행하는 축소판 이미지를 포함 할 수 있습니다. 데이터베이스의 HTML 문자열에 Angular 메소드 호출 (ng-click)을 배치했지만 물론 Angular는 HTML 문자열을 구문 분석하고 인식하여 컴파일하지 않는 한 메소드 호출이나 지시문을 인식하지 못합니다.

우리의 DB에서

1 페이지 내용 :

<p>Here's a cool pic of a lion. <img src="lion.png" ng-click="doSomethingAwesone('lion', 'showImage')" > Click on him to see a large image.</p>

2 페이지 내용 :

<p>Here's a snake. <img src="snake.png" ng-click="doSomethingAwesone('snake', 'playSound')" >Click to make him hiss.</p>

Page 컨트롤러로 돌아가서 해당 $ scope 함수를 추가합니다 :

페이지 컨트롤러

$scope.doSomethingAwesome = function( id, action ) {
    console.log( "Going to do " + action + " with "+ id );
}

DB의 HTML 문자열 내에서 'doSomethingAwesome'메서드를 호출하는 방법을 알 수 없습니다. Angular는 어떻게 든 HTML 문자열을 구문 분석해야한다는 것을 알고 있지만 어떻게해야합니까? $ compile 서비스에 대한 모호한 내용을 읽었으며 몇 가지 예를 복사하여 붙여 넣었지만 아무 효과가 없습니다. 또한 대부분의 예는 지시문의 연결 단계 동안에 만 동적 내용이 설정되는 것을 보여줍니다. 우리는 Page가 앱 수명 동안 살아 있기를 원합니다. 사용자가 페이지를 넘길 때 지속적으로 새로운 컨텐츠를 수신, 컴파일 및 표시합니다.

추상적 인 의미에서, 우리는 Angular 앱 내에서 Angular 덩어리를 동적으로 중첩하려고 시도하고 있음을 말할 수 있다고 생각합니다.

나는 모든 종류의 블로그 게시물뿐만 아니라 다양한 각도의 Angular 문서를 여러 번 읽었으며 사람들의 코드로 JS Fiddled를 읽었습니다. 나는 Angular를 완전히 오해하는지, 아니면 단순한 것을 놓치고 있는지, 아니면 느리게인지 모르겠습니다. 어쨌든 조언을 사용할 수 있습니다.


2
$ compile과 그것을 둘러싼 문서 블로그는 내가 느리다고 느낀다. 비록 js가 상당히 강하다고 생각하더라도-이것에 익숙해지면 바보 스타일 블로그를 만들 것이라고 생각한다.
도착

답변:


248

ng-bind-html-unsafe내용을 HTML로만 렌더링합니다. 결과 범위에 Angular 범위를 바인딩하지 않습니다. $compile그런 목적으로 서비스 를 사용해야 합니다. 사용자가 입력 한 동적 HTML을 렌더링하고 컨트롤러의 범위에 바인딩하는 지시문을 작성하는 방법을 보여주기 위해이 플런저 를 만들었습니다 $compile. 소스는 아래에 게시되어 있습니다.

demo.html

<!DOCTYPE html>
<html ng-app="app">

  <head>
    <script data-require="angular.js@1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js"></script>
    <script src="script.js"></script>
  </head>

  <body>
    <h1>Compile dynamic HTML</h1>
    <div ng-controller="MyController">
      <textarea ng-model="html"></textarea>
      <div dynamic="html"></div>
    </div>
  </body>

</html>

script.js

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

app.directive('dynamic', function ($compile) {
  return {
    restrict: 'A',
    replace: true,
    link: function (scope, ele, attrs) {
      scope.$watch(attrs.dynamic, function(html) {
        ele.html(html);
        $compile(ele.contents())(scope);
      });
    }
  };
});

function MyController($scope) {
  $scope.click = function(arg) {
    alert('Clicked ' + arg);
  }
  $scope.html = '<a ng-click="click(1)" href="#">Click me</a>';
}

6
정말 감사합니다, 부우! 속성 지시문을 만들고 범위 감시 기능을 추가하는 것은 내가 놓친 두 가지입니다. 이것이 작동하고 있기 때문에 지시문과 $ compile을 다시 읽고 후드 아래에서 일어나는 일을 더 잘 이해할 것입니다.
giraffe_sense

11
Angular 팀은 이에 대한 문서를 개선하는 데 실제로 도움이 될 수 있습니다.
Craig Morgan

$compile(ele.contents())(scope);이 줄은 동적으로 추가되는 각도 구성 요소를 컴파일하지 않는 문제를 해결했습니다. 감사.
미탈 프리트 마니

teplateURL 내부의 @BuuNguyen ng-bind-html을 사용하여 동적 htmnl 페이지를 포함하고 컴파일을 사용하면 작동하지 않는 일부 안전하지 않은 내용에서 오류가 발생한다고 가정하십시오. trustAsHTml 만 안전하지 않은 오류를 제거하면 컴파일되지 않습니다.
anam

1
나는이 예를 좋아하지만 내 일을하지 않습니다. 동적으로 사용자 선택으로 인해 switch 문이 있습니다. 그것에 따라 지시문을 포함하는 html을 삽입하고 싶습니다. 지시문은 자연적인 부트 스트랩 단계에 배치하면 작동합니다. 그러나 나는 이것이 단순히 발사되지 않는 이것을 가지고 있습니다. --- 'info': 단절; --- 내가하고 싶은 경우 --- $ compile ($ sce.trustAsHtml ( '<div dynamic = "htmlString"> dddzzz </ div>')); 해결 방법 등에 대한 모든 아이디어를 ...
착륙

19

각도 1.2.10에서 행 은 html 텍스트 scope.$watch(attrs.dynamic, function(html) {값을 보려고했기 때문에 잘못된 문자 오류를 반환했습니다 attrs.dynamic.

scope 속성에서 속성을 가져 와서 수정했습니다.

 scope: { dynamic: '=dynamic'}, 

내 예

angular.module('app')
  .directive('dynamic', function ($compile) {
    return {
      restrict: 'A',
      replace: true,
      scope: { dynamic: '=dynamic'},
      link: function postLink(scope, element, attrs) {
        scope.$watch( 'dynamic' , function(html){
          element.html(html);
          $compile(element.contents())(scope);
        });
      }
    };
  });

안녕하세요, element.html 을 사용하면 TypeError : null의 'insertBefore'메서드를 호출 할 수 없습니다. 그래서 그것에 대해 약간의 인터넷 검색을 한 후에 element.append 를 사용해야한다는 것을 알았습니다. 그러나 여러 곳에서 해당 지시문을 사용하면 여러 HTML이 생성됩니다. 따라서 2 개의 지시문은 4 개의 동일한 HTML 코드를 생성합니다. 답변 주셔서 감사합니다.
DzeryCZ

나는 당신의 장소에 추가를 사용하지 않을 것입니다, 나는 오늘 밤을 살펴볼 것이고 당신에게 돌아올 것입니다. 솔직히 말해서, 나는이 지시어를 아무 문제없이 페이지의 아주 작은 곳에 사용했습니다. 문제를 재현 해보고 다시 연락 드리겠습니다.
Alexandros Spyropoulos

1
@AlexandrosSpyropoulos 방금 테스트 한 결과 1.2.12에서도 코드가 제대로 실행되는지 확인했습니다. HTML에서 <div dynamic = "html"> 선언을 놓친 것 같습니다. (이 선언으로 $ watch는 언급 한 실제 HTML이 아닌 'html'속성을 범위에서 감시하므로 유효하지 않은 문자 오류가 없어야합니다.) 그렇지 않으면 작동하지 않는 플런저를 보내십시오. 무엇이 잘못되었는지 볼 것이다.
Buu Nguyen

당신 말이 맞을 것입니다. 그 당시 html은 실제로 html : P를 포함하는 변수입니다. 지침에 범위를 설정하는 것이 좋습니다. umur.io/…
Alexandros

$compile(ele.contents())(scope);이 줄은 동적으로 추가되는 각도 구성 요소를 컴파일하지 않는 문제를 해결했습니다. 감사.
Mital Pritmani

5

Google 토론 그룹에서 발견되었습니다. 나를 위해 작동합니다.

var $injector = angular.injector(['ng', 'myApp']);
$injector.invoke(function($rootScope, $compile) {
  $compile(element)($rootScope);
});

3

당신이 사용할 수있는

ng-bind-html https://docs.angularjs.org/api/ng/service/$sce

HTML을 동적으로 바인딩하는 지시어. 그러나 $ sce 서비스를 통해 데이터를 가져와야합니다.

http://plnkr.co/edit/k4s3Bx 에서 라이브 데모를 참조 하십시오

var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$sce) {
    $scope.getHtml=function(){
   return $sce.trustAsHtml("<b>Hi Rupesh hi <u>dfdfdfdf</u>!</b>sdafsdfsdf<button>dfdfasdf</button>");
   }
});

  <body ng-controller="MainCtrl">
<span ng-bind-html="getHtml()"></span>
  </body>

감사! 이것은 나를 도왔다. 그러나 ngSanitize 및 angular-sanitize.js를 포함해야합니다.var myApp = angular.module('myApp', ['ngSanitize']);
jaggedsoft

부트 스트랩 아이콘을 재료 md-list span 요소에 바인딩하는 동안 저에게도 도움이되었습니다.
changtung

1

attr을 통해 HTML을 바인딩하려면 아래 코드를 시도하십시오

.directive('dynamic', function ($compile) {
    return {
      restrict: 'A',
      replace: true,
      scope: { dynamic: '=dynamic'},
      link: function postLink(scope, element, attrs) {
        scope.$watch( 'attrs.dynamic' , function(html){
          element.html(scope.dynamic);
          $compile(element.contents())(scope);
        });
      }
    };
  });

이 element.html (scope.dynamic);을 시도하십시오 element.html (attr.dynamic);보다

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.