Sytuacja
W naszej aplikacji Angulara jest zagnieżdżona dyrektywa o nazwie Page, wspierana przez kontroler, która zawiera element div z atrybutem ng-bind-html-unsafe. Jest to przypisane do zmiennej $ scope o nazwie „pageContent”. Ta zmienna otrzymuje dynamicznie generowany kod HTML z bazy danych. Kiedy użytkownik przechodzi do następnej strony, następuje wywołanie bazy danych, a zmienna pageContent jest ustawiana na ten nowy kod HTML, który jest renderowany na ekranie przez ng-bind-html-unsafe. Oto kod:
Dyrektywa strony
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
}
}
}
};
});
Szablon dyrektywy strony („page.html” z właściwości templateUrl powyżej)
<div ng-controller="PageCtrl" >
...
<!-- dynamic page content written into the div below -->
<div ng-bind-html-unsafe="pageContent" >
...
</div>
Kontroler strony
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;
});
});
To działa. Widzimy, że kod HTML strony z bazy danych jest ładnie renderowany w przeglądarce. Kiedy użytkownik przechodzi do następnej strony, widzimy zawartość następnej strony i tak dalej. Na razie w porządku.
Problem
Problem polega na tym, że chcemy mieć interaktywną zawartość w treści strony. Na przykład kod HTML może zawierać miniaturę, w której po kliknięciu przez użytkownika Angular powinien zrobić coś niesamowitego, na przykład wyświetlić wyskakujące okienko modalne. Umieściłem wywołania metod Angular (ng-click) w łańcuchach HTML w naszej bazie danych, ale oczywiście Angular nie rozpozna ani wywołań metod, ani dyrektyw, chyba że w jakiś sposób przeanalizuje ciąg HTML, rozpozna je i skompiluje.
W naszej DB
Treść strony 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>
Treść strony 2:
<p>Here's a snake. <img src="snake.png" ng-click="doSomethingAwesone('snake', 'playSound')" >Click to make him hiss.</p>
W kontrolerze Page, dodajemy następnie odpowiednią funkcję $ scope:
Kontroler strony
$scope.doSomethingAwesome = function( id, action ) {
console.log( "Going to do " + action + " with "+ id );
}
Nie mogę dowiedzieć się, jak wywołać tę metodę „doSomethingAwesome” z ciągu znaków HTML z bazy danych. Zdaję sobie sprawę, że Angular musi jakoś przeanalizować ciąg HTML, ale jak? Przeczytałem niejasne mamrotania o usłudze $ compile, skopiowałem i wkleiłem kilka przykładów, ale nic nie działa. Ponadto większość przykładów pokazuje, że zawartość dynamiczna jest ustawiana tylko w fazie linkowania dyrektywy. Chcielibyśmy, aby Page działała przez cały okres użytkowania aplikacji. Stale otrzymuje, kompiluje i wyświetla nowe treści, gdy użytkownik przegląda strony.
W sensie abstrakcyjnym myślę, że można powiedzieć, że próbujemy dynamicznie zagnieżdżać fragmenty Angular w aplikacji Angular i musimy mieć możliwość ich wymiany.
Wielokrotnie czytałem różne fragmenty dokumentacji Angular, a także wszelkiego rodzaju posty na blogu i JS Fiddled z kodem ludzi. Nie wiem, czy kompletnie nie rozumiem Angulara, czy po prostu przeoczę coś prostego, czy może jestem wolny. W każdym razie przydałaby mi się rada.
Odpowiedzi:
ng-bind-html-unsafe
tylko renderuje zawartość jako HTML. Nie wiąże zakresu Angular z wynikowym DOM. W tym celu musisz skorzystać z$compile
usługi. Stworzyłem ten plunker, aby zademonstrować, jak używać$compile
do tworzenia dyrektywy renderującej dynamiczny HTML wprowadzany przez użytkowników i wiążący się z zakresem kontrolera. Źródło jest zamieszczone poniżej.demo.html
<!DOCTYPE html> <html ng-app="app"> <head> <script data-require="[email protected]" 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>'; }
źródło
$compile(ele.contents())(scope);
- ta linia rozwiązała mój problem z niekompilowaniem komponentów kątowych, które są dodawane dynamicznie. Dzięki.W kątowym 1.2.10 linia zwracała
scope.$watch(attrs.dynamic, function(html) {
błąd nieprawidłowego znaku, ponieważ próbowała obserwować wartość,attrs.dynamic
której był tekst html.Naprawiłem to, pobierając atrybut z właściwości zakresu
scope: { dynamic: '=dynamic'},
Mój przykład
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); }); } }; });
źródło
$compile(ele.contents())(scope);
- ta linia rozwiązała mój problem z niekompilowaniem komponentów kątowych, które są dodawane dynamicznie. Dzięki.Znaleziono w grupie dyskusyjnej Google. Pracuje dla mnie.
var $injector = angular.injector(['ng', 'myApp']); $injector.invoke(function($rootScope, $compile) { $compile(element)($rootScope); });
źródło
Możesz użyć
dyrektywa do dynamicznego wiązania html. Jednak musisz uzyskać dane za pośrednictwem usługi $ sce.
Zobacz demo na żywo pod adresem 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>
źródło
var myApp = angular.module('myApp', ['ngSanitize']);
Wypróbuj poniższy kod, aby powiązać HTML za pomocą attr
.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); }); } }; });
Wypróbuj ten element.html (scope.dynamic); niż element.html (attr.dynamic);
źródło