I am trying to print to the screen custom html using angular. I am using $sce.trustAsHtml in combination with ng-bind-html to accomplish this. The goal is not only to be able to print this custom html, but that it will retain directives such as ng-click and they will be usuable. Examples I have seen in articles such as follows are promising:
AngularJS render HTML within double curly brace notation
However in my implementation I find that although the html renders correctly including references to ng-click, the directive doesn't seem to work anymore when trying to click on the link I am using it on; here is some sample code:
$scope.htmlExpression = $sce.trustAsHtml("<a ng-click='test();'>Click Me</a>");
$scope.test = function() {
console.log('Hello World!');
}
<div>
<p ng-bind-html="htmlExpression"></p>
</div>
As everything renders fine and nothing appears lost in translation when analyzing the source; I am left feeling as if I have left something out. Any help is appreciated.
Use https://docs.angularjs.org/api/ngSanitize and bind the html. If this does not work, $digest to reboot the digest cycle.
Related
This is a weird issue i have started to encounter after the latest JQuery update.
We use angularjs 1.7.9 in out app. We have following component. It is basically a date field wrapper .
export class DateFieldComponent {
...
transclude: any = {
iconContent: "?iconContent",
};
template: string = `
<div>
<span
ng-click="$ctrl.focusInput()"
ng-transclude="iconContent"
/>
<input
...
></input>
</div>
`;
}
angular.module("App").component("dateField", new DateFieldComponent());
As you can see, we use ng-transclude to load an icon on to the input field. We made it in this way so that whoever using this component can dynamically add the icon from the client side.
This component was working fine until we updated JQuery from 3.4.0 to 3.5.0 . After the update the dateField has issues. However there are no console errors. But i was able to pin point the issue to below line
ng-transclude="iconContent"
So if i remove this line, everything works fine. Adding this line back makes the component to not to render properly.
I know for a fact that JQuery update has no effect on angularjs cause angularjs uses its own jquery-lite version. Also as far as i know, ng-transclude has nothing to do with JQuery.
Also i noticed that there are similar issues in all the places we use ng-transclude in our app, after this update.
What am i missing here ? Any clue would be much appreciated.
Turns out we made a simple mistake in the markup which is not working anymore with JQuery 3.5.0 version
this was the culprit
<span
ng-click="$ctrl.focusInput()"
ng-transclude="iconContent"
/>
fix was to close the span properly like below
<span
ng-click="$ctrl.focusInput()"
ng-transclude="iconContent">
<span/>
I’m using onsen ui version 1, followed https://onsen.io/v1/guide.html to make changes to DOM
This section to be exact
// Add another Onsen UI element
var content = document.getElementById("my-content");
content.innerHTML="<ons-button>Another Button</ons-button>";
ons.compile(content);
The problem is nothing changed on the page.
If i dump “content” variable or dump the HTML element it shows the newly edited version on browser console. but on page still the old one.
ons object is instantiated, compile method is callable, tried different HTML elements.
Either you are doing something incorrectly or it's an angular refresh issue.
For the first scenario it's easier if you provide a codepen, so that we can see the problem. Currently the code you are mentioning is working fine for me here.
For the second scenario actually the third line ons.compile(content); should remove this problem imo, it might be a version issue, or there is some context which I am missing.
If you're doing something angular related then you should also show where you are calling these 3 lines from. In order to work it should be called from something like an ng- event (for example ng-click).
JS:
app.controller('yourControllerName', function($scope) {
$scope.addButton = function () {
var content = document.getElementById("my-content");
content.innerHTML="<ons-button>Another Button</ons-button>";
ons.compile(content);
}
});
HTML:
<ons-button ng-click="addButton()">Add another button</ons-button>
Finally if you are unable to make it work you can do something like
app.controller('yourControllerName', function($scope) {
$scope.addButton = function () {
var content = document.getElementById("my-content");
content.innerHTML="<ons-button>Another Button</ons-button>";
ons.compile(content);
$scope.apply(); // should fix the issue, but not recommended
}
});
PS: You can also try out onsen 2 - there you can:
experiment with the interactive tutorial
try out the vanilla version (without any external framework) - which will not have issues like this one
I am trying to get a popover show some html content (it could be angularjs compiled content) and when I click on my "View" link, I do not see my custom directive getting processed and showing correctly (all other content is rendering okay including one that has an ng-repeat inside it suggesting that angularjs saw my angular content right)
http://plnkr.co/edit/Jy8Qlp1rsghwj4NNF2Dn?p=preview
<div ng-controller="ChordCtrl">
<chord-layout chord-matrix="{{matrix}}">
<div id="chordLayoutHolder"></div>
</chord-layout>
</div>
There is a lot going on in this plunkr but the bottom line is when I click on any of the "View" inside the table, I expect dynamic contents of report1.html to show up in my tooltip - Clearly all the other contents show up but it somehow failed to do my complex chord layout rendering - The chord layout diagram rendering is tested independently in another plunkr - http://plnkr.co/edit/q5DDdKHs11OuW6SfLtTG?p=preview
Any help in determining why my chord layout chart is not rendering would be helpful.
Regards
You're having issues because you're using id for your chordLayoutHolder. To be honest, I'm not sure why this is an issue. Perhaps Angular pre-compiles the template and clones it, such that the id is no longer unique. I would be curious to know.
In any case, remove <div id="chordLayoutHolder"></div> and just append to the directive element:
<chord-layout chord-matrix="{{matrix}}">
</chord-layout>
Then, in your directive, change the following lines (that refer to chordLayoutHolder):
$("#chordLayoutHolder").empty();
var svg = d3.select("#chordLayoutHolder")
...
to:
element.empty();
var domElement = angular.element(element)[0];
var svg = d3.select(domElement)
...
to append to the element itself, rather than another placeholder.
Then it would work. Here's the plunker.
I have a AngularJS application where I am loading data from a REST service.
Now what sometimes happens is that the brackets {{}} used to access values from scope are rendered and after that replaced by the real values. Now what I d like to do is add a ng-switch to the top DIV of the application and check whether a global var (e.g. pageLoaded (true|false)) is true or false. If its true, I d like to load the normal page, if its false, I d like to print something like "loading...". So my problem is how can I get notified (e.g. through a Angular Event) if all the data is ready, and is added to scope? Because after that I dlike to set pageLoaded to true.
Is there a way to do this in a generic way? I don't like to implement this per page.
Thanks a lot in advance.
Greets
Marc
You should use ng-cloak for that - http://docs.angularjs.org/api/ng.directive:ngCloak
For showing a loading panel, you can do something like:
<div ng-hide="true">Loading...</div>
So when angular finishes loading, ng-hide will occur and hide the loading panel.
Use ng-cloak to get rid of this sort of problems. And make sure you apply the ng-cloak directive to the body of the page so that this doesn't show up till the data is loaded properly. And use the following styling in your CSS file.
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none;
}
Note: you can even create some other element or div, thats something like a popup or notification bar, which shows "please wait till the data is comnpletely loaded". Set this div to display:none initially and in the Ajax call, change the display property to block/inline as needed and finally make it dispay:none after the success call back.. :)
One of the solutions is you can use ng-bind instead of using {{}} which will show ugly {{}} when the value is not rendered.
<div ng-bind="value">Loading ...</div>
For anyone who is having a problem more to do with the actual question than OP's specific scenario:
I had a fragment that was getting loaded-in after/by the main partial that came in via routing.
I needed to run a function after that subpartial loaded and I didn't want to write a new directive and figured out you could use a cheeky ngIf
Controller of parent partial:
$scope.subIsLoaded = function() { /*do stuff*/; return true; };
HTML of subpartial
<element ng-if="subIsLoaded()"><!-- more html --></element>
I'm working on a directive for AngularJS that builds a taggable element and utilizes TagsInput
Here's a working fiddle: http://jsfiddle.net/mgLss/
Not sure why but when I add that directive to my application IT works fine, but anything else below it running angular fails and I get this error message:
Error: node is undefined
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
nodeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:4216
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3834
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
nodeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:4216
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3834
compositeLinkFn#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3837
compile/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:3746
bootstrap/</<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:932
Scope.$eval#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:7808
Scope.$apply#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:7888
bootstrap/<#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:930
invoke#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:2788
bootstrap#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:929
angularInit#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:904
#http://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js:14397
f.Callbacks/o#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
f.Callbacks/p.fireWith#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
.ready#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
B#http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js:2
I've spent the last hour on IRC but cant get any acknowledgment of my question so here's hoping Stack will come to the rescue as it has so many times before.
This is something related to the plugin you are using, it manipulates the dom in a way angular does not like it, I didn't to go into the source code the point you to the root of the issue, to be honest. But here is a way (an ugly one) to fix it.
<div ng:controller="TestCtrl">
{{ hello }}
<div><taggable default="changed"></taggable></div>
</div>
Just wrap that directive within another DOM element, making sure the plugin is isolated.
http://jsfiddle.net/mgLss/33/
Building on #fastreload's answer, a slightly less ugly solution, which does not require changing your HTML:
// as per #fastreload, wrap input in a div to prevent Angular from getting confused
template: "<div><input type=\"text\"></div>",
link: function(scope, elm, attrs) {
elm.children().tagsInput({ // note children()
You should also include Angular last in your fiddle (under Manage Resources) (and in your code in general), then elm is automatically a wrapped jQuery element, rather than a wrapped Angular element, and hence we don't need to use $(elm) in the link function.