HTML Button Appears as Text after $compile [duplicate] - javascript

This question already has answers here:
how can we use $compile outside a directive in Angularjs
(5 answers)
"Thinking in AngularJS" if I have a jQuery background? [closed]
(15 answers)
Closed 4 years ago.
I am trying to create a pop up dialog with two buttons created in JS code with angular. The following code that produces the buttons...
var html = $('<button ng-click = "cancelAlert()" > Cancel</button > <button ng-click="continueAlert()">Continue</button>');
var div = $compile(html);
var content = div($scope);
document.getElementById('dialogboxhead').innerHTML = "header";
document.getElementById('dialogboxbody').innerHTML = "body";
document.getElementById('dialogboxfoot').innerHTML = content;
Gives me the following html text instead of the actual buttons themselves...
[[object HTMLButtonElement], [object Text], [object HTMLButtonElement]]
Am I missing something here that I have forgotten to add in?
The HTML looks like the following...
<div id="dialogoverlay"></div>
<div id="dialogbox">
<div>
<div id="dialogboxhead"></div>
<div id="dialogboxbody"></div>
<div id="dialogboxfoot"></div>
</div>
</div>

The $compile method accepts a string argument if you want to provide markup in this way.
Avoid wrapping the input for $compile with anything (ie $(..)). Instead, just pass the html string directly to the $compile() method, and also attach the div via the DOM append() method, and you should find this will work as expected:
var html = '<button ng-click="cancelAlert()">Cancel</button><button ng-click="continueAlert()">Continue</button>';
var div = $compile(html);
...
document.getElementById('dialogboxfoot').append( div[0] );
For more infromation see the usage on the official docs.
Here's a link to a working jsFiddle

It is not wise to mix AngularJS and jQuery this way.
The major problem with this approach is that $compile adds watchers to the specified scope. Those watchers will remain after added elements are removed from the DOM. This will result in memory leaks. If this is a dialog box that is constantly being added and removed -- beware.
But if you must, don't use innerHTML to append compiled content:
̶d̶o̶c̶u̶m̶e̶n̶t̶.̶g̶e̶t̶E̶l̶e̶m̶e̶n̶t̶B̶y̶I̶d̶(̶'̶d̶i̶a̶l̶o̶g̶b̶o̶x̶f̶o̶o̶t̶'̶)̶.̶i̶n̶n̶e̶r̶H̶T̶M̶L̶ ̶=̶ ̶c̶o̶n̶t̶e̶n̶t̶;̶
var foot = document.getElementById('dialogboxfoot');
$(foot).append(content);
The DEMO
angular.module("app",[])
.controller("ctrl",function($scope, $compile) {
var html = $('<button ng-click = "cancelAlert()" > Cancel</button > <button ng-click="continueAlert()">Continue</button>');
var div = $compile(html);
var content = div($scope);
document.getElementById('dialogboxhead').innerHTML = "header";
document.getElementById('dialogboxbody').innerHTML = "body";
var foot = document.getElementById('dialogboxfoot');
$(foot).append(content);
})
<script src="//unpkg.com/jquery"></script>
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
<div id="dialogoverlay"></div>
<div id="dialogbox">
<div id="dialogboxhead"></div>
<div id="dialogboxbody"></div>
<div id="dialogboxfoot"></div>
</div>
</body>

Related

Trouble setting the HTML of a variable in JQuery

What I've done is loaded some HTML from a file and I am attempting to modify some elements within that HTML.
The initialization looks like this:
var id = player_info["ID"];
$("#main_container").append(
$("<div />").attr({class: "player_container", id: "player_" + id}).css("display", "none")
);
// Add all information to the player container
var player_container = $("#player_" + id);
player_container.load("player_layout.html");
With player_layout.html looking like this:
<div class="player_name">
</div>
<div class="player_chips">
Chips:
<br/>
<span class='bidding'></span>/<span class='chips'></span>
</div>
<div class="player_stats">
Wins / Losses
<br/>
<span class="wins"></span>/<span class="losses"></span>(<span class="total_games"></span>)
<br/><br/>
Chips Won / Chips Lost
<br/>
<span class="chips_won"></span>/<span class="chips_lost"></span>
</div>
<button class="player_won">Player Has Won</button>
I then want to modify some of the elements, specifically classes. An example of the way I was initially doing this is:
player_container.find(".player_name").text(player_info['username']);
This wasn't working so I then tried to switch find with children and text with html but that didn't seem to work. I then tried this:
$('> .player_name', player_container).html(player_info['username']);
but that also didn't work. I understand that I can use DOM to grab the childNodes and compare the class names but there are a lot of classes that need modifying and I'd also like to know if this is possible in JQuery. Thanks in advance for any help.
You need to use complete callback method of .load()
var player_container = $("#player_" + id);
player_container.load("player_layout.html", function(){
player_container.find(".player_name").text(player_info['username']);
});

How to select elements from a handelbars template?

We have a div within our webpage:
<div id="person-details">
<div></div>
...
</div>
We select the relevant elements from this div using the following jQuery code:
var person = $("#person-details");
var children = release.find("div");
var fullname = children.first();
We use a render function to perform an ajax request to get a handlebars template that we're storing in an external file:
function _render() {
var templateScript;
template.getTemplate(filename).done(function(template) {
templateScript = Handlebars.compile(template);
fullname.html(templateScript(context));
});
}
The template looks like the following:
<div>
<div>
<p>{{name}}</p>
</div>
<div>
<p>{{value}}</p>
</div>
</div>
After the render function has been called, we want to select any of the elements from within the template. For example we have tried using:
fullname.children('div');
but as the content has been dynamically generated we aren't able to get the nodes from within the DOM.
Is it even possible to select elements from within a generated handlebars template like this?
Thanks to #DanielShillcock for highlighting render() being asynchronous. Here is a solution:
function _render() {
var templateScript;
template.getTemplate(filename).done(function(template) {
templateScript = Handlebars.compile(template);
selector.html(templateScript(attribute));
value = selector.find("div");
});
}

Angularjs - how to bind to an html element appended by javascript code

I want to bind angular event and model to an html element appended by javascript code.
My code is here. https://jsfiddle.net/hq7qk48n/13/
<div ng-app>
<div ng-controller="MyController">
<input type="text" ng-model="text1">
click1
<div id="append"></div>
<p ng-if="clickedTime1">click1 : {{ clickedTime1.toLocaleString() }}</p>
<p ng-if="clickedTime2">click2 : {{ clickedTime2.toLocaleString() }}</p>
<p>{{ text1 }}</p>
<p>{{ text2 }}</p>
</div>
</div>
function MyController($scope) {
$scope.clickedTime1 = null;
$scope.clickedTime2 = null;
$scope.onClick = function () {
var html = '<input type="text" ng-model="text2" name="text"> click2';
$("#append").empty();
$("#append").append(html);
$scope.clickedTime1 = new Date();
}
$scope.onClick2 = function () {
$scope.clickedTime2 = new Date();
};
}
onClick2() doesn't work. and model "text2" is not updated.
How to bind onClick2 function and text2 model?
Need to compile an html element? How?
This is a little tricky because you have an ng-click in your new element. There are 2 things we need to deal with.
Correctly add the element
Make your new element see the scope
First we will start with your call to the method from your element. You will need to add $event to the call
click1
The $event object will give you information about the calling element.
You also need to add it to your method
$scope.onClick=function($event){
Next, your element required use of the scope so we need to turn it into an element. Otherwise just html would be fine.
var el=angular.element('<input type="text" ng-model="text2" name="text"> <a href="#" ng-click="onClick2()"/>click2</a>');
Now you can use a little jquery but exactly on the target element the way angular would do it.
$($event.currentTarget).empty();
$($event.currentTarget).append(el);
At this point you would see your changes but the bindings will not work because it is not attached to the scope so we need to compile it. You will need to add the $compile server to your controller
function MyController($scope,$compile) {
Now we can use the service to compile the new element
$compile(el)($scope);
You should now see everything functioning the way you would expect
function MyController($scope,$compile) {
$scope.clickedTime1 = null;
$scope.clickedTime2 = null;
$scope.onClick = function ($event) {
var el = angular.element('<input type="text" ng-model="text2" name="text"> click2');
$($event.currentTarget).empty();
$($event.currentTarget).append(el);
$compile(el)($scope);
$scope.clickedTime1 = new Date();
}
$scope.onClick2 = function () {
$scope.clickedTime2 = new Date();
};
}
and don't forget to add the $event to your call as well.
I did not use your exact code but I tested this as I answered to verify and it worked perfectly. It might not be best practice to work with the DOM elements in your controller but sometimes it just works. That is how you do it.

Generate a list in javascript and add it to a div in html

I have a javascript function which gets some values from ajax. From javascript I add that values to a div whose display is by default none. In my function from ajax i also gets an array of values. My task is to make a list on html inside my div based on the array values.
Can anyone help me to do this..
Javascript function:
function showpopup(id)
{
var advid=id;
$.ajax
({
type:"post",
url:"ajax_getadv.php?function=getadv",
data:{id:advid},
cache:false,
success:function(data){
var values=data;
var myarray=values.split("/");
var name=myarray[0];
var email=myarray[1];
var country=myarray[2];
var web=myarray[3];
var advid=myarray[4];
var count=myarray[5];
var val=myarray[6]
var mytitle=val.split(",");
document.getElementById("advname").innerHTML = name;
document.getElementById("advid").innerHTML = advid;
document.getElementById("email1").innerHTML = email;
if(country!="")
{
document.getElementById("country").innerHTML = country;
}
else
{
document.getElementById("country").innerHTML = "Not Available";
}
if(web!="")
{
document.getElementById("website").innerHTML = web;
}
else
{
document.getElementById("website").innerHTML = "Not Available";
}
var generateHere = document.getElementById("newlist");
var mycount=mytitle.length;
alert(mycount);
for( var i = 0 ; i < mycount ; i++)
{
generateHere.innerHTML = '<div class="someclass"><ul><li>My Text</li></ul></div>';
}
}
});
document.getElementById('box-config-modal1').style.display='block';
}
Here mytitle is the array which i want to display as list. I've put a for loop to create a list in the div. I have to show mytitle rather than My text.
HTML div:
<!--SHOW MODAL 1-->
<div id="box-config-modal1" class="modal hide fade in" style="display: none;">
<div class="box" id="box-details">
<h4 class="box-header round-top">Details</h4>
<div class="box-container-toggle" style="padding-left:20px;padding-right:20px;">
<div class="box-content" >
<form name="popup" id="popup" >
<fieldset>
<button class="close" data-dismiss="modal"></button>
<h3>User Details</h3>
<div class="control-group">
<div class="controls"> <label class="control-label" for="typeahead" style="width:100px;float:left;" >Name </label><label id="advname" style="padding-left:150px;"></label></div>
</div>
<div class="control-group">
<div class="controls"><label class="control-label" for="typeahead" style="width:100px;float:left;" >ID </label><label id="advid" style="padding-left:150px;"></label></div>
</div>
<div class="control-group">
<div class="controls"><label class="control-label" for="typeahead" style="width:100px;float:left;">Email </label><label id="email1" style="padding-left:150px;"></label></div>
</div>
<div class="control-group">
<div class="controls"><label class="control-label" for="typeahead" style="width:100px;float:left;">Country </label><label id="country" style="padding-left:150px;"></label ></div>
</div>
<div class="control-group">
<div class="controls"><label class="control-label" for="typeahead" style="width:100px;float:left;">Website </label><label id="website" style="padding-left:150px;"></label ></div>
</div>
<div class="" id="newlist">
</div>
<div class="modal-footer">
Exit
</div>
</fieldset>
</form>
</div>
</div>
</div>
It is in this div i want to create a list. I've added a new div with id newlist
There are a couple of options becoming available in w3 drafts and standards these days and into the future.. webcomponents and shadowDOM specifically..
until those become widely adopted standards..
You can look to building DOM elements in Javascript HtmlDOMElement and than attach it to the document body in 1 assignment to the actual DOM.
function showpopup(id)
{
var advid=id,
options = {
type:"post",
url:"ajax_getadv.php?function=getadv",
data:{id:advid},
cache:false,
success: OnSuccess
};
$.ajax(options);
}
Helper Function
function createControlGroup(options) {
var options = options || {};
options.id = options.id || "ukn-"+Date.now();
options.for = options.for || options.id;
options.displayText = options.displayText || "NotSet";
options.displayValue = options.displayValue || "Unknown";
var cntrlGrpElm = document.createElement("div"),
cntrlElms = document.createElement("div"),
cntrlLabel1 = document.createElement("label"),
cntrlLabel2 = document.createElement("label");
var cntrlGrpElmClass=document.createAttribute("class"),
cntrlElmsClass=document.createAttribute("class"),
cntrlLabel1Class=document.createAttribute("class");
cntrlGrpElmClassAttr.nodeValue="control-group";
cntrlGrpElm.attributes.setNamedItem(cntrlGrpElmClassAttr);
cntrlElmsClass.nodeValue="controls";
cntrlElms.attributes.setNamedItem(cntrlElmsClass);
cntrlLabel1Class.nodeValue="control-label";
cntrlLabel1.attributes.setNamedItem(cntrlLabel1Class);
var cntrlLabel2Id=document.createAttribute("id");
cntrlLabel2Id.nodeValue=options.id;
cntrlLabel2.attributes.setNamedItem(cntrlLabel2Id);
var cntrlLabel1For=document.createAttribute("for");
cntrlLabel1For=options.for;
cntrlLabel1.attributes.setNamedItem(cntrlLabel1For);
var cntrlLabel1Text = document.createTextNode(options.displayText),
cntrlLabel2Text = document.createTextNode(options.displayValue);
cntrlLabel1.appendChild(cntrlLabel1Text);
cntrlLabel2.appendChild(cntrlLabel2Text);
cntrlElms.appendChild(cntrlLabel1);
cntrlElms.appendChild(cntrlLabel2);
cntrlGrpElm.appendChild(cntrlElms);
return cntrlGrpElm;
}
OnSuccess Callback Function
function OnSuccess(e) {
var values=e.responseText;
var myarray=values.split("/");
var name=myarray[0],
email=myarray[1],
country=myarray[2],
web=myarray[3],
advid=myarray[4],
count=myarray[5],
val=myarray[6];
var mytitle=val.split(",");
var title1=mytitle[0],
title2=mytitle[1],
title3=mytitle[2];
use the helper function to create and return the DOMElement, we're passing an ambiguous object here.
var cntrlGrpElms = document.createElement("fieldset");
var cntrlGrpElmsClass = document.createAttribute("class");
cntrlGrpElmsClass.nodeValue = "contrl-group-list";
cntrlGrpElms.attributes.setNamedItem(cntrlGrpElmsClass);
cntrlGrpElms.appendChild(createControlGroup({"id":"item-1","displayText":"Item 1", "displayValue":"Value of Item 1"}));
cntrlGrpElms.appendChild(createControlGroup({"id":"item-2","displayText":"Item 2", "displayValue":"Value of Item 2"}));
cntrlGrpElms.appendChild(createControlGroup({"id":"item-3","displayText":"Item 3", "displayValue":"Value of Item 3"}));
cntrlGrpElms.appendChild(createControlGroup({"id":"item-4","displayText":"Item 4", "displayValue":"Value of Item 4"}));
This is sample data.. You would, of course, use your data, and structure: I just mimicked the elements with the fieldset tag, excluding the button and div.modal-footer. CSS will pick up on the changes, so I didn't see any point in specifying the styles.
At this point, we tapper this function off by adding the DOMElement (and the children) in the document.body.
document.body.appendChild(cntrlGrpElms);
} /* End of - $.ajax - 'OnSuccess' Callback*/
This should really make the process clearer for you. But I don't know exactly how you intended to structure the list. The HTML structure you provided seems more like a control panel.
Also, I want to explicitly point out that your callback function (which you were defining anonymously in your code sample) was handling the eventArg improperly.. so I'm not sure if that was the underline issue you were having or not. If it was, you aside from the documentation, you should use the browser debugger to investigate these matters... F12 will usually invoke the developer console.. depending on the browser, you'll need to set breakpoints on the source (not html dom window.. the 'sources' or external files).. then you can invoke the 'showpopup(id)' function in the console.. it should pause at the breakpoint.. then you can either use the console to evaluate the 'e' eventArg within the scope of the function.. or you can add the 'e' object to the watch expressions' panel.. this is all standard debugger interfacing.. modern web browsers have sure given us developers a great tool to improve and explore the web with.
the event returned is a jqXHR (XmlHttpRequest).. the documentation on it is here
On a complete side note: It is likely that this jqXHR object will change (or at least these documents) in the near future (extended really)..
there are two newer prevalent dataTypes supported by XHR2 now, blob and arraybuffer.. 'bson' (Binary json) is, I think, considered a blob type, but it really has sort of a mixed medium, string and byte data.. so I'm not entirely sure about it.. I know Newtonsoft.json supports it with serialization on the .Net framework already.
You can find more information about XHR2 (which is XHR Level 1 now) and blob, arraybuffer support by googling it..
The Jan 2012 w3.org documentation is here. (when it was still level-2.. and stayed at for pretty darn long)
The newest release spec was in Jan 2014 w3.org: here
But XHR is a Living Standard, and a 'snapshot' of the most recent spec can be found here
To write inside an HTML tag, simply use innerHTML as shown below:
function divCall()
{
document.getElementById('myDiv').innerHTML += 'your newly added text';
}
//make sure your div id is myDiv

Get next textarea outside div without id

I need to get the next textarea, but I'm not being able with next or even find.
Sample code HTML:
<div>
<div class="guides_chapters_button" onclick="surroundtest('[center]', '[/center]');">Center</div>
<div class="guides_chapters_button" style="text-decoration: underline;" onclick="surround('[u]', '[/u]');">u</div>
</div>
<textarea class="guides_chapters_textarea" id="textareamatch" name="matchupm" rows="7" cols="25"></textarea>
JS:
window.surround = function surround(text2,text3){
$("#textareamatch").surroundSelectedText(text2, text3);
}
function surroundtest(text2,text3){
var c = $(this).parent().next('textarea');
c.surroundSelectedText(text2, text3);
}
JS FIDDLE: http://jsfiddle.net/qmpY8/1/
What I need working is surroundtest, the other is an example working but using the id. I would love to replace that one because Im usinc cloned objects.
The this statement in surroundtest applies to the window object and not the element. What you should do is to change the function definition as so:
function surroundtest(element, text2,text3){
var c = $(element).parent().next('textarea');
...
}
And the HTML accordingly:
<div class="guides_chapters_button" onclick="surroundtest(this, '[center]', '[/center]');">Center</div>
If this is the HTML you are going with, then .closest() can also be used to get the textarea element.Like below:
var c = $(element).parent().closest('textarea');

Categories