Reputation: 962
I have an app where I need to create html and add it to an object array. This html should then be outputted into page. Simple enough. However, I also need to make the html responsive to user actions (click) in which case angular requires me to use $compile to create an angularized template.
See the Plunkr. In this example, what should happen is that when you click on one of the buttons, a popup is generated with the html code embedded in the object, which you can see in the JSON output.
As soon as I do this, I get the error Converting circular structure to JSON. If I don't, the ng-click="Go()" is not called.
SCRIPT
var template = "<ul class='unstyled'>" +
"<li ng-click='go()' style='background-color:lightcyan;'><ul class='inline'><li>1...</li><li>1...</li></ul></li>" +
"<li ng-click='go()'><ul class='inline'><li>1...</li><li>1...</li></ul></li>" +
"<li ng-click='go()' style='background-color:lightcyan;'><ul class='inline'><li>1...</li><li>1...</li></ul></li>" +
"</ul>";
// template = $compile(template)($scope);
$scope.data = [
{"id": 1, "html": template},
{"id": 2, "html": template}
];
$scope.go = function () {
alert('It works');
};
$scope.openPopin = function (html) {
var e = window.event;
var popin = document.getElementById('popin');
var innerdiv = document.getElementById('innerdiv').innerHTML=html;
popin.style.top= e.pageY - 20+"px";
popin.style.left = e.pageX - 20+"px";
popin.style.marginLeft = -500+"px";
popin.style.marginTop = -100+"px";
};
$scope.closePopin = function () {
var popin = document.getElementById('popin');
popin.style.top = -500+"px";
popin.style.left = -500+"px";
};
HTML
<div class="popin grey-border" id="popin">
<button class="close" ng-click="closePopin()">×</button>
<div id="innerdiv"></div>
</div>
<pre>{{ data |json }} </pre>
<br/>
<table style="float: right;">
<tr ng-repeat="d in data" id="{{$index}}">
<td>{{ d.id }} -
<button class="btn btn-mini btn-info" ng-click="openPopin(d.html)"><i class="icon-info-sign"></i></button>
</td>
</tr>
</table>
Upvotes: 1
Views: 1826
Reputation: 962
Thanks towr for your help - see the last comment above
HTML
<script type="text/ng-template" id="cmpbpopin.html">
<button class="btn btn-mini btn-info"><i class="icon-info-sign"></i></button>
<div class="popin grey-border">
<button class="close-button">×</button>
<div></div>
</div>
</script>
<table style="float: right;">
<tr ng-repeat="d in data" id="{{$index}}">
<td>{{ d.id }}</td>
<td>
<div cm-pb-popup="d.html"></div>
</td>
</tr>
</table>
</body>
SCRIPT
var app = angular.module('app', []);
app.controller('Ctrl', function ($scope, $compile, $http) {
var template = "<table class='pblist table table-condensed table-hover'>" +
"<tr ng-click='go()'><td>1...</td><td>1...</td></tr>" +
"<tr ng-click='go()'><td>1...</td><td>1...</td></tr>" +
"<tr ng-click='go()'><td>1...</td><td>1...</td></tr>" +
"</table>";
$scope.data = [
{"id": 1, "html": template},
{"id": 2, "html": template}
];
});
app.directive("cmPbPopup", function ($compile, $timeout) {
return{
templateUrl: "cmpbpopin.html",
scope: {
cmPbPopup: "="
},
link: function (scope, elem, attrs) {
elem.bind("click", function (e) {
var popupDiv = elem.find('div');
var innerDiv = popupDiv.find('div');
var closeButton = popupDiv.find('.close-button')
if (e.srcElement.nodeName != 'DIV') {
if (e.srcElement.className == 'close-button') {
closePopup();
} else if(e.srcElement.nodeName == 'TR' || e.srcElement.nodeName == 'TD'){
// set values in scope
closePopup();
}
else {
innerDiv.html(scope.cmPbPopup);
$compile(innerDiv)(scope);
popupDiv.css({
'top': e.pageY - e.offsetY + 20,
'left': e.pageX - e.offsetX -10,
'height': 100,
'width': 500,
'marginLeft': -500});
$timeout(function (){
closeButton.css('display', 'block');
},500);
}
}
function closePopup(){
popupDiv.css({
'height': 0,
'width': 0,
'marginLeft': 0});
$timeout(function (){
popupDiv.css({
'top': -500,
'left': -500
});
},500);
}
});
}
}
})
CSS
div.popin {
position: absolute;
width: 0;
height: 0;
top: -500px;
left: -500px;
background-color: #ffffff;
transition: width 0.5s, height 0.5s, margin-left 0.5s;
-webkit-transition: width 0.5s, height 0.5s, margin-left 0.5s; /* Safari */
overflow: hidden;
}
div.popin div {
position: absolute;
top: 0 !important;
left: 500px !important;
width: 470px !important;
transition: width 0.2s, height 0.2s, margin-left 0.2s;
-webkit-transition: width 0.2s, height 0.2s, margin-left 0.2s;
}
.close-button{
width: 20px;
height: 20px;
float: right;
font-size: 20px;
font-weight: bold;
line-height: 20px;
color: #000000;
text-shadow: 0 1px 0 #ffffff;
opacity: 0.2;
filter: alpha(opacity=20);
}
.pblist{
margin-left: 10px !important;
margin-top: 10px;
width: 470px;
float: left;
}
.grey-border {
border: 1px #d3d3d3 solid;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
padding: 3px;
}
Upvotes: 0
Reputation: 4167
I got it to work (for me) by moving the compile step to the openPopin
function, and replacing the style-property changes with a more angular alternative. And I'm also ignoring the window.event
which is not cross-browser compatible (and not part of the issue).
app.controller('MainCtrl', function($scope, $compile) {
var template = "<ul class='unstyled'>" +
"<li ng-click='go()' style='background-color:lightcyan;'><ul class='inline'><li>1...</li><li>1...</li></ul></li>" +
"<li ng-click='go()'><ul class='inline'><li>1...</li><li>1...</li></ul></li>" +
"<li ng-click='go()' style='background-color:lightcyan;'><ul class='inline'><li>1...</li><li>1...</li></ul></li>" +
"</ul>";
$scope.data = [
{"id": 1, "html": template},
{"id": 2, "html": template}
];
$scope.go = function () {
console.log("go");
};
$scope.openPopin = function (html) {
var popin = document.getElementById('popin');
var innerdiv = document.getElementById('innerdiv');
innerdiv.innerHTML=html;
$compile(innerdiv)($scope);
angular.element(popin).css({top:'20px', left:'20px'});
};
$scope.closePopin = function () {
var popin = document.getElementById('popin');
angular.element(popin).css({top:'-500px', left:'-500px'})
};
});
So, that's one way to get it working. But the question is, what are you really trying to do, and can't we do it in a more angular way? (Using directives, templates and other tools angular provides.)
Upvotes: 2