I am trying to write a functionality where if we pass the scope and template, it should show the given template in a jQuery UI dialog with the all working bindings from the given scope.
Here is the code:
HTML
<div id="mainDiv" ng-controller="myCtrl">
<input type="button" value="show dialog" ng-click="showTemplateDialog()" />
</div>
<script type="text/ng-template" id="dialogTemplate">
<div>
This is template div.
<span>Message: </span>
<p>{{message}}</p>
</div>
</script>
JS
var app = angular.module('myApp', []).controller('myCtrl', function($scope, $compile) {
$scope.showTemplateDialog = function() {
alert("hi")
var newScope = $scope.$new();
newScope.message = "This is some message";
$scope.showDialog(newScope, "dialogTemplate")
};
$scope.showDialog = function(dialogScope, template) {
var div = $("<div style='' id='dialog' title='Test Dialog' ng-include=\"'" + template + "'\"></div>");
$compile(div)(dialogScope);
$("#mainDiv").append(div);
div.dialog();
}
})
The problem is when I call div.dialog(), it throws following error in jquery-ui.min.js
Unhandled exception at line 9, column 26027 in http://localhost/TestApp/scripts/jquery-ui.min.js
0x800a138f - JavaScript runtime error: Unable to get property 'display' of undefined or null reference
Please suggest some way to resolve this error. Or suggest some other way to show and html/template in jQuery dialog using angularjs.
I think the value of div would be a JQuery object. I would try passing the HTML string directly to compile(). The result of compile(html)(scope) is an array IIRC, so you probably want to append its first element [0].
EDIT: I just tried that and it seems Angular won't allow ng-include being compiled this way.
I just tried what Hubert suggested, but still it was not working. Then I realize when I am creating the div, it's not properly getting created and was showing localName of the html element as null.
Then I did some changes in my code and it worked. I just added another div inside the div I was creating and put ng-include on that.
Here is the updated code:
$scope.showDialog = function(dialogScope, template) {
var div = $("<div id='dialog' title='Test Dialog' ><div ng-include=\"'" + template + "'\"></div></div>");
$compile(div)(dialogScope);
$("#mainDiv").append(div);
div.dialog();
}
Related
I would like to be able to select a button using querySelector and set an attribute of "ng-click=doSomething()"
I have tried selecting the button and then setAttribute("ng-click", "doSomething()") but its not working
my DOM:
<body>
<div ng-app="myApp" ng-controller="mainCtrl">
<button id="myBtn">click Me</button>
</div>
<script src="./js/app2.js"></script>
</body>
my javascript:
(function() {
"use strict";
angular.module("myApp", []).controller("mainCtrl", mainCtrl);
/** #ngInject */
function mainCtrl($scope) {
init();
function init() {
$scope.doSomething = () => {
console.log("doing something");
}
let btn = document.querySelector('#myBtn');
btn.setAttribute("ng-click", "doSomething()");
}
}
})();
when I click the button it should console log something.
Generally speaking, if you dynamically add "AngularJS-ified" stuff to a document after it's created - such as dynamically creating <button> elements and then adding ng-click attributes to them - those elements will neither be tracked by watchers, nor be part of the normal digest cycle. So, for example, consider the following simple example:
const myApp = angular.module('stuff', [])
.controller('stuff-cont', function($scope) {
const targ = document.querySelector('#target');
for (let i = 0; i < 10; i++) {
let newBtn = document.createElement('button');
newBtn.setAttribute('ng-click', 'sayRandNum()');
newBtn.innerText = `Button ${i}`
targ.append(newBtn);
}
$scope.sayRandNum = () =>{
alert('Your random number is '+Math.ceil(Math.random()*100));
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app='stuff' ng-controller='stuff-cont'>
<div id='target'>
</div>
The buttons above are clickable, they have an appropriately "structured" ng-click, but they <i>don't trigger</i>!
</div>
Here, we're (for some reason...) creating 10 nearly-identical buttons. However, because of when we built these ng-click'ed buttons (namely, after the initial compilation phase), and specifically when we added the ng-click attributes (also after the initial compilation phase), the buttons are effectively not "known" to the AngularJS cycle".
Looked at another way, when AngularJS is first "loaded" on a page, it first walks through the HTML on that page, and looks for any databinds ({{likeThis}}; we'll ignore these for now) or directives. ng-click, ng-repeat, and other Babbys First AngularJS stuff are just standardized directives, so they're part of that "looking for directives" procedure. When AngularJS finds said directives, it says "Okay, you've got an ng-click on this element; I'll keep an eye on that".
To actually add new AngularJS-ified elements - or add AngularJS behavior to existing elements, as I believe is more the case with you - you'll need to use the $compile function, which basically says "hey, AngularJS! I made a new thing and want you to watch it!"
This SO answer -- Working with $compile in angularjs has a pretty decent explanation of how to use the $compile function.
(function() {
"use strict";
var btn = document.querySelector('#myBtn');
btn.setAttribute("ng-click", "doSomething()");
angular.module("myApp", []).controller("mainCtrl", mainCtrl);
function mainCtrl($scope){
$scope.doSomething = function(){
alert('abc');
}
}
angular.bootstrap(document, ['myApp']);
})();
Please check the JSFiddle , the difference is you have to modified the html before angular bootstrapped so your modified html and js code can be compiled properly. Here is a AngularJS Developer Guide - Bootstrap with more infomation of angularjs bootstrap
I'm trying to show a tooltip with html content and the html I would like to be fetched from a children div which has angular markup in it.
Before I switched to ui-bootstrap I used the default bootstrap tooltip, which I created in a directive filling the content with ('.my-tooltip-content',element).html()
Now with ui-bootstrap I tried using the same directive/logic on the ui-tooltip except that now I tried setting attribute variables. The problem is, that I don't know how to get the html from my .my-tooltip-content div rendered inside the tooltip, without loosing the bindings. If I use $interpolate, I get the html of my div properly rendered, but the output is fixed then of course (doesn't update anymore), $compile I have never used before, but I thought this would be the right place for such a use, maybe I don't know how to use it, but $compile gives me an exception about circular structure.
This is a shortened version of what my app looks like:
http://plnkr.co/edit/46NsEPArtm4hph0ROlPS?p=preview
Excerpt:
<div class="hover" tooltip-html-unsafe="" tooltip-trigger="mouseenter" tooltip-placement="bottom">
<div>
<p>Hover me {{booking.name}} !</p>
<!-- about 20 more lines -->
</div>
<div class="my-tooltip-content"><!-- hidden by default -->
<p class="booker-name">{{booking.name}}</p>
<p>Does the name in this tooltip update?</p>
<!-- about 10 more complex lines -->
</div>
</div>
anApp.directive('hover', ['$compile','$interpolate', function($compile,$interpolate){
return{
restrict: 'C',
link: function(scope,element,attrs){
var html1,html2,html3;
var content = $(element).find('.my-tooltip-content');
// This works, but no binding
html1 = $interpolate(content.html())(scope);
// This I'd like to work, but I get "Can't interpolate: {{tt_content}} TypeError: Converting circular structure to JSON"
html2 = $compile(content.html())(scope);
// This brings my html into the tooltip but curlybraces are curlybraces (not compiled)
html3 = content.html();
attrs.tooltipHtmlUnsafe = html1;
}
}
}])
Is there an easy way to to get the html of .my-tooltip-content with all it's angular markup/bindings injected into the tooltip-content variable ?
PS: I know that I could squeeze all the html just directly into the tooltip-html-unsafe attribute, but I have so many lines in my real-world my-tooltip-content that this just wouldn't work (would make the tooltip-content html unreadable and unchangeable for humans).
If you need to use a child div, one solution use a function to pass the interpolated contents of the div to the tooltip. For example:
anApp.directive('hover', ['$compile','$interpolate', function($compile,$interpolate){
return{
restrict: 'C',
link: function(scope,element,attrs){
scope.myTooltip = function() {
var content = $(element).find('.tooltip-content');
return $interpolate(content.html())(scope);
};
}
}
}])
And your html would would need to be updated to use the new scope value for the tooltip:
<div class="hover" tooltip-html-unsafe="{{myTooltip()}}" tooltip-trigger="mouseenter" tooltip-placement="bottom">
Here's the updated plnkr
Have a template that I'd like to load using ng-include and assign a controller instance to. This new template/scope/controller needs to be loaded in response to a user interaction (hover or click).
The content of the template has to be set using element.innerHTML because the content is set by a 3rd party.
The user can then click out of the new div and I would like to destroy the controller/scope that was created.
Pseudocode for what I want to achieve:
popup.setContent("<div ng-controller='PopupController'><div ng-include=\"views/LayerPopup.html\"></div></div>");
How do I tell angular to process the ng-include and ng-controller just as though the page was being loaded for the first time?
Thanks!
Edit:
Add plunker to illustrate question
http://plnkr.co/edit/DPuURCoq2hJ0LCLIN2dc?p=preview
http://jsfiddle.net/ADukg/5420/
Not using ngInclude, but it does fill these criteria:
You pass in a templateURL.
Pass in the name of the controller you would like to use.
Pass in the third party content (which in turn gets set with $element.innerHTML).
Setup a click listener someplace outside the $scope of the popup, which triggers a kill command on the popup.
This is how I imagine you would instantiate it:
<directive tpl="tpl.html"
ctrl="DirectiveController"
third-party-content="{{thirdPartyContent}}">
</directive>
Not sure this will suit you, but I had a fun time putting it together and maybe it'll prove useful to someone else.
In any case, I have to agree with the comments you've recieved so far. It's a bit cryptic as to what you have to work with right now and what possible options are available to you.
Here is a plunker of what you are trying to do. If you click on a button, a popup will show a template, and you can click on the template and it will stay up, but if you click out of it, it will get removed.
HTML
<body ng-controller="MainCtrl" ng-click="closePopup()">
<button ng-click="openPopup($event)" id="clicktarget">Click</button>
<p>Hello {{name}}!</p>
<div ng-include="getPopup()" ng-click="$event.stopPropagation()">
</div>
<script type="text/ng-template" id="theTemplate.html">
<div ng-controller="PopupController">
<div ng-include="'LayerPopup.html'"></div>
</div>
</script>
</body>
JS
angular.module('plunker', [])
.controller('MainCtrl', function($scope, $templateCache) {
$scope.name = 'World';
$scope.popupTmpl = null;
$scope.openPopup = function($event){
$scope.popupTmpl = 'theTemplate.html';
$event.stopPropagation();
};
$scope.getPopup = function(){
return $scope.popupTmpl;
};
$scope.closePopup = function(){
$scope.popupTmpl = null;
};
})
.controller('PopupController', ['$scope', function($scope) {
$scope.aVariableMaybe = 'lulz something';
}]);
On a side note, try to get rid of that JQuery stuff when you are using Angular. Angular can do everything on its own
I am new to Angular getting stuck after making ajax call. How do I render/compile the html content once you inject in DOM so that I can still use the AngularJs functions.
Due to the way my backend is set up I have to get content via ajax ($http). And I am making the app without jQuery. I tried $compile and $apply but didn't work. What am I missing here.
I have the code set up at http://jsfiddle.net/rexonms/RB7FQ/3/ . I want the second div content to have the same properties as the first div.
HTML
<div ng-controller="MyCtrl" class="section">
<input ng-model="contentA">
<div>
And the input is: {{contentA}}
</div>
</div>
<div ng-controller="MyAjax" class="section">
<div id="dumpAjax">
{{ajaxData}}
</div>
<button ng-click=getajax()> Get Ajax</button>
</div>
SCRIPT
var myApp = angular.module('myApp',[]);
//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});
function MyCtrl($scope) {
}
function MyAjax($scope){
var data = '<input ng-model="contentB">{{contentB}}';
$scope.getajax = function(){
$scope.ajaxData = data;
}
}
Thanks in advance.
ng-bind-html-unsafe is not available 1.2 and later verison of angular...
so you should use ng-bind-html which creates a binding that will innerHTML the result of evaluating the expression into the current element in a secure way.
using $scope variable in your string make it unsafe, so you should use $sce.trustAsHtml but this time variables in your string cannot be bind because they will be not compiled...
basically you should compile your string in order to bind your variables. Here comes custom directives you can create a directive which can replace with ng-html-bind...
Writing a custom directive which extends ng-bind-html with some extra functions can be a solution...
here is my PLUNKER
and here is your updated JSFIDDLE with my solution...
Instead of {{ajaxData}}, you should use something like:
<div ng-bind-html-unsafe="ajaxData"></div>
However, you'd still need to set the proper model to bind the contentB and get it working.
So I have some html that gets loaded into the #panel div dynamically depending on which questionNumber the user is on. This is not all of the code but all of the relevant code I think. Anyway, the <input> get's loaded into the page but it doesn't actually do anything. what am I missing here? I have the same problem when the questionNumber === 1, where the binded variables just show up as {{variable}} etc
var readingController = function (scope, Romanize){
scope.usersRomanization;
//alert(scope.usersRomanization);
}
var app = angular.module('Tutorials', ['functions', 'tutorials']).controller('getAnswers', function ($scope, $element, Position, Romanize) {
$scope.sectionNumber = Position.sectionNumber;
if ($scope.sectionNumber === 0){
$('#panel').html('<div ng-controller="readingController"><input ng-model="usersRomanization"></input></div>');
readingController($scope, Romanize);
}
<body ng-controller="getAnswers">
<div id="panel">
</div>
</body>
If you add HTML to the DOM, you have to tell Angular to $compile it. This should be done in a directive. You'll need to inject $compile then do something like this:
var content = '<div ng-controller=...></div>';
var compiled = $compile(content)(scope);
// then put the content where you want
Or better, define a directive and use a template, which will automatically get compiled for you by Angular.
Other alternatives are ng-include (which will compile the loaded content for you) and ng-switch, which would allow you to put the templates into the HTML.