compile ng-bind-html inside ng-repeat - javascript

I have a special template problem... I have a array of products, every products have a property "button_code", this property is a result in plain text of HTML laravel template with some angular code inside.
Actually im using a ng-bind-html="product.button_code" inside a and use this template inside a ng-repeat, the html code is correctly inserted in every repeat iteration, but the code is plain text, and I need to "wake up" the ng-controllers ng-clicks etc inside this html
I try with this:
var targets = $('.buy-button-container').toArray();
for (var target in targets) {
console.log($(targets[target]));
$compile($(targets[target]))($scope);
}
$scope.$apply();
But this make the code inside the container (all html code inserted in the ng-bind-html) dissapear of the DOM.
How i can do this?
PD: and yes, im forced to use these template in these product.button_code because special things...)
Thanks
EDIT: This is a piece of code i want to bind:
<button class="buy-link btn btn-default" data-toggle="modal" role="button" ng-controller="BuyController" ng-click="doProduct({'id':'8888','title':'testestest','price':13.99,'currency':'EUR''preorder_enabled':false,'crossedPrice':100,'stock':true,'short_desc':'bla bla bla.','lbonus':false,'bonus_txt':false})">
<span class="left">
<i class="fa fa-cart"></i>
<span itemprop="price">€13.99</span>
</span>
<span class="right">
{{GETIT}}</span>
</button>

Use the transclude function furnished as the second argument of the function created by the $compile service:
app.directive("compileBindExpn", function($compile) {
return function linkFn(scope, elem, attrs) {
scope.$watch("::"+attrs.compileBindExpn, function (html) {
var expnLinker = $compile(html);
expnLinker(scope, function transclude(clone) {
elem.empty();
elem.append(clone);
})
});
};
});
The above directive evaluates the compile-bind-expn attribute as an AngularJS expression. It then uses the $compile service to bind the evaluated HTML to the element. Any existing content will be removed.
Usage:
<div class="buy-button-container" compile-bind-expn="buttonCode">
<p>This Node disappears when expression binds</p>
</div>
Note that the directive uses a one-time binding in the $watch to avoid memory leaks.
The DEMO on JSFiddle

In order to make HTML render you have to use the following function:
$sce.trustAsHtml('<b>Your html</b>');
You will have to inject $sce into your Controller.
If you are doing this in a ng-repeat you will need a function in your controller that does this. Ex:
$scope.transformHTML = function(html) {
return $sce.trustAsHtml(html);
}
in your template...
<div ng-repat="foo in bar">
<div ng-bind-html="transformHTML(foo.html)"></div>
</div>
Anyway, I don't think that the "Angular" magic within your HTML will work.

Related

ng-if not working with simple javascript

ng-if is not working when I change the values through simple javascript function.My function is getting called but the changes in values cannot be seen in view. Please refer below code.
HTML
<div id="span" ng-app='MyModule' ng-cloak ng-controller="MyController">
<div ng-if="!bool">
This is for true
</div>
<div ng-if="bool">
This is False
</div>
{{bool}}
<br>
<input type="submit" ng-click = "myfunction('test')" value="ng-if button">
</div>
<input type="submit" onClick = "check1()" value="simple JS button">
JS
angular.module('MyModule', [])
.controller('MyController', function ($scope) {
$scope.bool = true;
$scope.myfunction = function (data) {
$scope.bool = !$scope.bool;
};
});
function check1() {
angular.element(document.getElementById('span')).scope().myfunction('test');
}
When I use ng-click button it changes value of bool changes, but same doesn't happens with simple JS button . Actually I am implementing Angular in a page that already uses jQuery, so I need to use simple JS button.
JS Fiddle : JS Fiddle
At first, ng-click is able to parse an angular expression.
Second, it handles the reference to the current scope and performs a call to $scope.$apply to notify any watchers to update. If you would add a call to angular.element(document.getElementById('span')).scope().$apply() in your function, it should work as expected.
Use $scope.apply . This is because angulars digest cycle will not know if your value changes outside of its scope like in a simple JS function.

How to re-register a dynamically loaded angular templates' directive

I have a directive which loads a template with a bunch on input fields. One of which is the jQuery/Bootstrap datepicker.
<my-directive-buttons></my-directive-buttons>
When a user selects/clicks on the datepicker field, the calendar is displayed. I have also attached an ng-click to the input field:
<div class='col-sm-6'>
<div class="form-group">
<div class='input-group datepick'>
<input type='text' class="form-control" ng-click="addCalendarFooter()"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
On click, the calender is displayed and $scope.addCalendarFooter is called:
app.directive('myDrectiveButtons', function($compile) {
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
},
templateUrl: 'controls/input-fields.html',
link: function(scope, elem, attrs) {
},
controller: function($scope) {
$scope.addCalendarFooter = function() {
$('#datepicker').append($('<div></div>').load('myDir/calendar/customFooter.html'));
}
}
}
});
I am successful in appending the contents of customFooter.html to the calendar, however, within customFooter.html are further ng-clicks, which when pressed, are not being called. E.g
customFooter.html
<div>
<button ng-click="controlClick()">Click Me</button>
</div>
Yet, if i move this button out of customFooter.html and in to input-field.html, to test the button logic is correct, the click is called.
I have tried $scope.$apply and $scope.digest after the .append, however i get a 'digest already in progress error'
UPDATE:
Based on comments, below, have tried to remove jQuery and use an 'angular way'
$scope.addCalendarFooter = function() {
var html = "<div ng-include src=\"myDir/calendar/customFooter.html\"><\div>";
var myEl = angular.element(document.getElementsByClassName('datepicker');
myEl.html(html);
$compile(myEl)($scope)
}
The above inserts my .html template via the ng-include however is it replacing the contents of the datepicker rather than inserting at the bottom of it. Tried .append but that didn't worth either.
You're basically loading the footer manually and bypassing angular completely. Angular doesn't see that you're loading the html and so doesn't compile the html template and bind directives, including ng-click, at all.
You can use ng-include directive that loads the specified template, instead of making a custom one yourself. Or if your directive needs other functionality, just have the ng-include in the directive template itself.
Quoting from your UPDATE:
The above inserts my .html template via the ng-include however is it replacing the contents of the datepicker rather than inserting at the bottom of it. Tried .append but that didn't worth either.
The aforementioned issue is due to using .html() method which inserts HTML to a DOM node and overwrites any existing content of the selected node:
var html = "<div ng-include src=\"myDir/calendar/customFooter.html\"><\div>";
var myEl = angular.element(document.getElementsByClassName('datepicker');
myEl.html(html); // <== here
$compile(myEl)($scope)
What you are doing with the above logic is that you first select the .datePicker element, then you replace its inner HTML with the .html() method.
As a workaround, you could have used .append() method instead.
NOTE: angular.element() is Angular's wrapper for an element just as in jQuery you had $(). Therefore, using document.getElementByClassName() method is redundant in your case.
Although the above workaround might solve your problem, but it is better to stick to a cleaner and concise approach which AngularJS may offer.
Angularesque Approach
You don't need to load a template partial by programmatically adding/appending the template in a controller function - at least in Angular's way. This way you might end up not binding the angular directive(s) within the dynamically added HTML correctly with a scope.
Instead, just include the partial within the original directive template (with ng-include) and use ngIf or ngShow to display it when the button is clicked.
Therefore, assuming that you've the footer template (customFooter.html) in the original directive template, you can achieve the expected result as in the following:
Directive Template
<div class='col-sm-6'>
<div class="form-group">
<div class='input-group datepick'>
<input type='text' class="form-control" ng-click="addCalendarFooter()"/>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
</div>
</div>
<div ng-include="myDir/calendar/customFooter.html" ng-if="isCalenderFooterAdded"></div>
Directive Controller
controller: function($scope) {
$scope.addCalendarFooter = function() {
$scope.isCalendarFooterAdded = true;
// or use this if you want to toggle it
// $scope.isCalendarFooterAdded = !$scope.isCalendarFooterAdded ? true: false;
}
}
Plunkr mimicking a similar situation.
What if you include your footer in your template initially but hide it with ng-if / ng-show. Then the function would only change a flag and show the previously hidden footer.
You said that your problem is the fact that after calling $scope.$apply or $scope.digest you get the $digest already in progress error.
A clever way to bypass this is using $evalAsync. One of its biggest advantages is that it knows if it should perform an extra digest cycle of not.
If you're using ng-include don't forget the singlequotes at the begining and end of the path.
Angular doc about ng-include: https://docs.angularjs.org/api/ng/directive/ngInclude
make sure you wrap it in single quotes, e.g. src="'myPartialTemplate.html'"
it should be:
var html = "<ng-include src=\"'myDir/calendar/customFooter.html'\"><\ng-include>";
or:
var html = "<div ng-include=\"'myDir/calendar/customFooter.html'\"><\div>";
but not:
var html = "<div ng-include src=\"myDir/calendar/customFooter.html\"><\div>";

Passing Html DOM inside an element as an string to AngularJS controller

I wanted to grab the Html DOM inside an element as an string to angular controller. I didn't find good resource online. I have following Html Code:
<div class="form-group ng-controller="straightRunningBeltsCtrl"">
<button class="btn btn-success btn-lg" ng-click="post()">Get Service Factor</button>
<div id="pop-up"><h1>My content here!!</h1></div>
</div>
And I have following JS code
angular.module('straightRunningBelts', [ ])
.controller('straightRunningBeltsCtrl', straightRunningBeltsCtrl)
function straightRunningBeltsCtrl($stateParams, $scope, $http, $sce){
$scope.post= function () {
var template=$sce.trustAsHtml(angular.element(document.getElementById('pop-up')));//Results an error( $sce.trustAsHtml needs string input)
}
Variable template needs to get value from DOM. Right now, angular.element(document.getElementById('pop-up') returns object. I wanted to do sometime like JQuery Does by using html() function here. Any help or reference to it is welcomed.
To retrieve the HTML inside your DOM element, you can use innerHTML. In your case it would be
document.getElementById('pop-up').innerHTML
Working Plunkr

Passing more than one variable to Angular JS directive

I am new to writing directives. I have this snippet of code that works and I am trying to make it a directive:
<div ng-click="details=!details" class="row toggleHeader" ng-class="{open : details}">
<div class="col-md-12">
<h4>Some title</h4>
</div>
</div>
<div id="the-details" class="row" ng-show="details">
<div class="col-md-2">
...content...
</div>
</div>
So, my initial pass was
app.directive('pbTogglePanel', function() {
return {
restrict: 'A',
scope: {
variableName: '#',
panelTitle: '#'
},
template: '<div ng-click="somevariable=!somevariable" class="row toggleHeader" ng-class="{open : somevariable}">\n<div class="col-md-12">\n<h4>{{panelTitle}}</h4>\n</div>\n</div>\n\n<div id="the-somevariable" class="row" ng-show="somevariable">\n<div class="col-md-2">\n...\n</div>\n</div>',
};
});
with the html
<div pb-toggle-panel panel-title="My Directive Panel Test" variable-name="foobar"></div>
This works, in the sense that the panel toggles as expected. I obviously did not try to use the variable "foobar" passed from the html attribute. When I try to use it, the code throws an error and the toggle does not work. I tried using the template:
'<div ng-click="{{variableName}}=!{{variableName}}" class="row toggleHeader" ng-class="{open : {{variableName}}}">\n<div class="col-md-12">\n<h4>{{panelTitle}}</h4>\n</div>\n</div>\n\n<div id="the-{{variableName}}" class="row" ng-show="{{variableName}}">\n<div class="col-md-2">\n...\n</div>\n</div>'
Although "foobar" shows up in all those places, the toggling fails and the console logs an angular error.
So, what's the correct way to pull in more than one html attribute?
Also, the goal would be to allow the contents of this DIV to replace ...content... placeholder in my template. Not sure how to pass that either.
I feel if I can just get my head around how this data is passed, I'll be good from there out.
TO HELP CLARIFY
This works: http://codepen.io/smlombardi/pen/MYygpy
But I wanted to pass the name of the variable for the toggler to use from an attribute too, and this does NOT work:
http://codepen.io/smlombardi/pen/KwzPaw
The problem of your last, non-working example is not about passing multiple parameters but rather abusing the angular's interpolation mechanism. If all you want is to use the variableName, the minimal change to make your last code example work (that is to collapse/expand) would be to change the template from string to function with concatenation:
{...
template: function(tElem, tAttr){return '<div ng-click="' + tAttr.variableName + '=!' + tAttr.variableName +...;
}
But there is no point to do so as long as you use directive isolating scope, which effectively hides the value of the variableName from you. You will also probably want to change to scope:false and get attributes from the tAttr of the template function instead. Modified example.

ng-click won't work with ng-bind-html

I have html template like this:
$scope.template = '<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()"></i></span>';
I want to bind this template using ng-bind-html, I tried to use it and also I used ng-bind-html-unsafe, but unfortunately It bind the html string as it is with no click action.
<span ng-bind-html="template"></span>
<span ng-bind-html-unsafe="template"></span>
I read about similar problem and it said that ng-click is loaded after ng-bind, so can anybody introduce to me how to solve this problem?
Perhaps you need to compile the template inside the controller?
angular.controller('ABCDCtrl', function($scope, $compile){
var templateHTML = '<span class="pointer"><i class="icon-refresh pointer" ng-click="refresh()"></i></span>';
$scope.template = $compile(templateHTML)($scope);
});
You could try ng-include and put your template in to a static file instead.
Putting HTML content in scope variables kind of goes against some angular philosophy guidelines, I believe.
If you were to later change the template, would you want it to rebind itself and be processed again?

Categories