How much knowledge of the DOM should javascript code have? - javascript

I'm implementing OpenID and OAuth on my site, in C# and ASP.NET MVC 3. I'm basing off of DotNetOpenAuth for the back-end and openid-selector for the front-end.
I liked openid-selector but it doesn't have OAuth support out of the box so I started adapting it (with help of StackOverflow's implementation and jsbeautifier).
I found a lot of code that handles the DOM like this:
function highlight(boxId) {
// remove previous highlight.
var highlight = $('#openid_highlight');
if (highlight) {
highlight.replaceWith($('#openid_highlight a')[0]);
}
// add new highlight.
$('.' + boxId).wrap('<div id="openid_highlight"></div>');
};
or
function useInputBox(provider) {
var area = $('#openid_input_area');
var id = 'openid_username';
var html = '';
var value = '';
var style = '';
var label = provider.label;
if (label) {
html = '<p>' + label + '</p>';
}
if (provider.name == 'OpenID') {
id = this.input_id;
value = 'http://';
style = 'background: #FFF url(' + spritePath + ') no-repeat scroll 0 50%; padding-left:18px;';
}
html += '<input id="' + id + '" type="text" style="' + style + '" name="' + id + '" value="' + value + '" />'
+ '<input id="openid_submit" type="submit" value="' + this.signin_text + '"/>';
area.empty();
area.append(html);
$('#' + id).focus();
};
Which both sound to me like they're assuming too much about the DOM (too many ids, or the current state of the DOM).
Is it ok to have javascript so tightly coupled to the DOM? What's the best way to avoid code like this and follow a less intrusive approach?
I guess what perplexes me is the call:
openid.init('openid_identifier', '', 'http://cdn.sstatic.net/Img/openid/openid-logos.png?v=8', true);
When there's so much assuming already in the script file.

I'd argue, as you suspect that this is a bad thing.
There's a huge lack of, well, design patterns in Javascript UI development. I'm guessing a lot of people came straight from html, to learning some jQuery, to writing web applications.
A simple system (I find) that does handle this better, is backbone.js. The sourcecode is legible, and it separates view-concerns from business logic concerns quite nicely.
http://documentcloud.github.com/backbone/
http://martinfowler.com/eaaDev/uiArchs.html

Also for a more MVVM approach ( aka data binding ) knockoutjs is an option. They also have a nice interactive tutorial to get you started.

Related

How to pass a value from a Javascript generated button to a controller?

My code generates a table with a button at the end of each row. When the user clicks a button how can I pass a property u.userEmail to the controller via the button? Will the value being sent to the controller be a string?
My (non-working) attempt:
<script>
$(document.body).append("waiting on async table to load<br>");
$(document).ready(function () {
$.getJSON("/Account/LoadClaimsTable", function (crewResponse) {
//returns a List<UserClaims>
$(document.body).append("<table>")
crewResponse.forEach(function (u) {
var s = "";
s+="<tr><td>" + u.userEmail + "</td>";
u.userClaims.forEach(function (k) {
console.log("added claim"+k.value);
s += ("<td>" + k.type + "</td><td>" + k.value + "</td><td>" +
"<input type=\"hidden\" name=\"userEmail\" value=\"`${u.userEmail}`\" />"+
"<input type=\"button\" value=\"Create\" onclick=\"location.href='#Url.Action("EditClaims", "Account")'" />
+"</td>");
});
s += "</tr>";
$(document.body).append(s);
s = "";
});
$(document.body).append("</table>")
});
});
</script>
AccountController.cs contains:
public ActionResult EditClaims(string userEmail)
{
return View("StringView", userEmail);
}
You have to pass it on the url of the action. Not sure if you want to pass u.userEmail, but it could looks like this:
crewResponse.forEach(function (u) {
var s = "<tr><td>" + u.userEmail + "</td>";
u.userClaims.forEach(function (k) {
console.log("added claim"+k.value);
s += ("<td>" + k.type + "</td><td>" + k.value + "</td><td>" +
"<input type=\"hidden\" name=\"userEmail\" value=\"`${u.userEmail}`\" />"+
"<input type=\"button\" value=\"Create\" onclick=\"location.href='#Url.Action("EditClaims", "Account")?userEmail=" + u.userEmail + "'\"/></td>");
});
s += "</tr>";
$(document.body).append(s);
});
There are multiple ways to do it. One is mentioned in the answer above by Felipe. Here is another alternate approach using unobtrusive js
Add the email as html5 data attributes to the button along with another attribute which we will use bind the click behavior.
u.userClaims.forEach(function (k) {
// Add quotes as needed if you want multiline ( i just removed those)
s += "<td>" + k.type + "</td><td>" + k.value + "</td><td>
<input type='button'
clickablebutton data-email='" + u.email + "' value='Create'/></td>";
});
Now, in your document ready, bind a click event handler to those elements (with our custom attribute) and read the data attribute and build the url you need.
$(document).on("click", "input[clickablebutton]", function (e){
var url = '#Url.Action("EditClaims", "Accounts")?useremail=' + $(this).data("email");
window.location.href = url;
});
Some other suggestions
Use the appropriate element. Button is better than input (Consider accessibility)
If it is for navigation, Use an anchor tag instead of a button.
Inline javascript is not great. Let the browser parses your markup without any interruptions and you can add the behavior scripts later (that is the whole point of uobutrisive js approach)
The approach you appear to be taking would be Ajax, response, render a template. With that being said, you may want to rethink your approach.
Step 1.
Build a template
<template id="...">
<button type="button" value="[action]" onclick="[url]">[text]</button>
</template>
Step 2.
Create your request.
axios.get('...').then((response) => {
// Retrieve template.
// Replace bracket with response object model data.
html += template.replace('[action]', response.action);
});
Step 3.
Have the JavaScript render your template.
The above can create a clear concise codebase that is easier to maintain and scale as the scope changes, rather than an individual request performing a change with embedded markup. This approach has worked quite well for me, also I feel it'll make you troubleshooting and definition easier, as the controller is handing an object back to your JavaScript instead of a markup / view data. Which will be a better finite control for the frontend and clear modifications in future.

How do I set an attribute to a dynamically created button?

First and foremost, hello. I'm a college student and not a very experienced coder, so forgive me if I end up saying something really dumb.
Either way, I have a school project in which I have to create a functional website using Node.js, where people are able to log in and buy stuff.
Most of the stuff already works, but I am having trouble with a very specific thing which I simply cannot get to work, yet is essential to the project itself.
I import data from a mySql database into the website using AJAX, that data contains products that people can buy. Then, those products are appended to the HTML page itself, and dynamically create two buttons for each product that is appended, one which says "Buy Now" and the other which says "Learn More".
Now, those buttons are supposed to be somehow associated to the values of the database, but I have no idea how to do this.
$(document).ready(function () {
$(function () {
$.ajax({
type: 'POST',
url: 'http://localhost:3000/getPacotes',
success: function (data) {
console.log(data);
for (var i = 0; i < data.length; i++) {
var pacoteSet1 = "<div class='col-md-4 pacotes'>";
var pacoteSet2 = "<input id=btnPac"+
i + " type='button' name='comprar' value='Comprar Pacote'>";
var pacoteSet3 = "</div>";
var idPacote = data[i].idPacote;
var nomePacote = data[i].Nome_Pacote;
$("#pacotes").append(pacoteSet1 +
"<h1>" + nomePacote + "</h1>" +
"<h1>" + nomePacote + "</h1>" +
"<h3>" + precoPacote + "euros/mes </h3>" +
pacoteSet2 +
pacoteSet3);
}
}
});
});
});
My teacher told me to give an attr to the buttons I create, and that's what I was pretty much trying to do now, but I dont know how to do that, I mean, if the buttons were static it would be pretty easy, but since the buttons are not there when the document is created, I dont know if this will work
$("#btnPac" + i).attr({"id": idPacote,
"nome": nomePacote,
"preco": precoPacote,
});
Anyway, Hopefully, I didn't sound too dumb and thanks in advance
TL:DR how do I give an attribute to a dynamically created button
You just need to change your jquery function like below:
$(document).find("#btnPac" + i).attr({"id": idPacote,
"nome": nomePacote,
"preco": precoPacote
});
This should be your solution.
You can do that while creating the html for button element. By using string concatenation, which you are already using while setting the ID:
var pacoteSet2 = "<input id=btnPac"+ i + " nome='" + nomePacote + "' preco='" + precoPacote + "' type='button' name='comprar' value='Comprar Pacote'>";
Overloading the HTML with attributes is a bad idea in my opinion. You could create the Elements in JS, bind the data to it, and then append the button to the dom. Thats quite easy with jquery:
var button=$("<button>Show More</button>");//create
button.on("click",showMore.bind({name:"test"}));//add listener
$(document.body).append(button);//append
function showMore(){
alert(this.name);
}
You can create a new input tag and set these attributes then append to your div with this code:
$('<input/>', {
id: 'btnPac' + i,
nome: nomePacote ,
preco: precoPacote ,
type: 'button'
}).appendTo("#pacotes");
You can do the same for your others (h1, h3)

Dynamic jQuery Dialog passing JSON object from within loop

I'm wondering if there is a better (cleaner?) method than my current implementation. I'm currently encoding a PHP SimpleXMLObject (USPS Tracking API) to JSON and looping through said JSON object via Javascript to operate the front-end.
Examples from my current implementation below:
Function to display dialog implemented anonymously outside of .ready() :
var moreInfo_popup = function(i) {
$('#moreinfo'+i).dialog({
modal:false,
autoOpen:false,
height:555,
title: 'Detailed View',
width:500,
draggable:false,
buttons: {
Ok: function(){
$(this).dialog("close");
}
}
});
$('#moreinfo'+i).dialog('open');
}
Main loop for displaying Tracking ID, Most Recent Event, and Mail Class for each API response- I'm currently generating a content div appended to #modal_container, then calling moreInfo_popup() inline via <input onClick="">:
for(var key in obj) {
if(obj.hasOwnProperty(key)) {
if(key % 2 === 0) {
$('#page-nav').append("<div id=\"results_table\"><table class=\"data_table\"id=\"data_table_id\"border=\"0\"width=\"60%\"align=\"center\"><tr><td align=center width=20%>"+obj[key].TrackInfo.Attributes.ID+"</td><td align=\"center\"width=\"35%\">"+obj[key].TrackInfo.StatusSummary+"</td><td align=\"center\"width=\"20%\">"+obj[key].TrackInfo.Class+"</td><td align=\"center\"><input type=\"button\"class=\"moreInfo\"value=\"Detail\"id=\"_buttonMoreInfo\"onClick=\"moreInfo_popup("+key+")\"></td></tr></table></div>");
$('#modal_container').append("<div id=\"moreinfo" + key + "\"><table><tr><td>" + obj[key].TrackInfo.Attributes.ID +"</td></tr></table>").hide();
}
else {
$('#page-nav').append("<div id=\"results_table\"><table class=\"data_table_even\" id=\"data_table_id\" border=0 width=60% align=center><tr><td align=center width=20%>"
+ obj[key].TrackInfo.Attributes.ID + "</td><td align=center width=35%>" + obj[key].TrackInfo.StatusSummary + "</td><td align=center width=20%>"
+ obj[key].TrackInfo.Class + "</td><td align=\"center\"><input type=\"button\" value=\"Detail\" class=\"moreInfo\" id=\"_buttonMoreInfo\"onClick=\"moreInfo_popup("+key+")\"></td></tr></table></div>");
$('#modal_container').append("<div id=\"moreinfo" + key + "\"><table><tr><td>" + obj[key].TrackInfo.Attributes.ID +"</td></tr><tr><td> <button>OK</button></td></tr></table>");
}
}
$("#page-nav td:contains('undefined')").html("Invalid");
}
As I'm sure you can see, this feels like an incredibly tedious way of accomplishing the desired outcome, to which there is surely a better alternative. As a newcomer to JavaScript/jQuery, I've done plenty of searching on this subject, but haven't really understood much of what I found- If indeed I was asking the right questions in the first place.
I think you can use angular and data bind:
So you can just use a directive and automatically bind your JSON object from the server side easily to the html elements.
However you should start studying angular.
you can start looking for your elegant way of doing stuffs here:
https://docs.angularjs.org/tutorial/step_04
I hope it was useful.

Adding page numbers to PDFs using PdfGenerator

This question pertains to NReco's PdfGenerator component.
I use this product to convert dynamically generated HTML string to a Pdf document within the .NET MVC framework using C#.
While looking for ways to add page numbers (e.g., 1 of 5) to the footer of the Pdf, I came across this and this on SO. And not surprisingly, both options seem to offer a similar approach to accomplishing the same goal.
While the code itself makes sense, what I'm having a hard time understanding is this - My document content (or the HTML string) is generated inside a View. The HTML string is then passed to a Controller (.cs) for the actual conversion process. With my very limited knowledge on MVC framework, I think there's no way you can add JavaScript code to the Controller (or is there?).
So, I don't quite understand how the above two JavaScript based methods can be incorporated inside my C# function that handles the document conversion. Or is this something that should be done inside the View?
Controller:
[HttpPost]
public ActionResult Html2Pdf(FormCollection form) {
var docTitle = form["doctitle"].ToString();
var headerHtml =
"<div style='width:100%; margin-top:1em; display:block;'>" +
"<img src='" + System.Web.HttpContext.Current.Server.MapPath("~") + "/media/images/document_banner.png' />" +
"</div>" +
"<div style='width:100%; bottom:110px; left:0; position:relative; display:block;'>" +
"<span style='color:#fff; font-size:2.5em; font-family:georgia; margin-left:1em;'>" + docTitle + "</span>" +
"</div>";
var footerHtml =
"<div style='width:100%; text-align:center; border-top:1px solid #abc; margin-top:2em;'>Page 0 of 0</div>;
var htmlToPdf = new HtmlToPdfConverter();
// various parameters get set here
htmlToPdf.PageHeaderHtml = headerHtml;
htmlToPdf.PageFooterHtml = footerHtml;
....
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;filename=MyTestDocument.pdf");
htmlToPdf.GeneratedPdf(form["htmlcontent"].ToString(), null, Response.OutputStream); // form["htmlcontent"] holds the document body
Response.End();
Return new EmptyResult();
}
You don't need to append javascript code (from wkhtmltopdf help) for rendering page number because PdfGenerator will do that for you if you set PageHeaderHtml or PageFooterHtml properties. All you need is just mark with "page" class element where you want to render page number:
htmlToPdf.PageHeaderHtml = "<div>Page: <span class="page"></span></div>";
that's all.
You can use this code if you want to display also the total number page (topage class):
var generator = new NReco.PdfGenerator.HtmlToPdfConverter();
generator.PageFooterHtml = $#"page <span class=""page""></span> of <span class=""topage""></span>";
Just below code, that's all
var htmlToPdf = new NReco.PdfGenerator.HtmlToPdfConverter();
htmlToPdf.PageFooterHtml = "<div style='padding:5px !important; font-size:12px;text-align:right;'>page <span class='page'></span> of <span class='topage'></span></div>";

How to get the total of the list ordered, with Javascript

I'm making a code of a online delivery webpage, and I having a hard time trying to figure out how to output the total of the list ordered by the user.
function ListOrder(){
document.getElementById('order').innerHTML += "<div id=\"YourOrders\">" + + document.getElementById('FoodName').value + document.getElementById('quantity').value + document.getElementById('Totality').value + "</div><br>";}
Edited: I want to know how I can get the sum of the total price. So, I placed a parseInt between the document.getElementById('Totality').value . It looks like this now,
function ListOrder(){
document.getElementById('order').innerHTML += "<div id=\"YourOrders\">" + + document.getElementById('FoodName').value + document.getElementById('quantity').value + parseInt(document.getElementById('Totality').value) + "</div><br>";}
Can someone help me make a function or something for that? Javascript only, please. I'm still kinda new at it.
function ListOrder(){
document.getElementById('order').innerHTML +=
"<div id=\"YourOrders\">" +
parseInt(document.getElementById('FoodName').value) +
parseInt(document.getElementById('quantity').value) +
parseInt(document.getElementById('Totality').value) +
"</div><br>";
}
the kernel of your code should look like the following (double + operator deleted, reformatted):
function ListOrder(){
document.getElementById('order').innerHTML +=
"<div id=\"YourOrders\">" + (
document.getElementById('FoodName').value
+ document.getElementById('quantity').value
+ document.getElementById('Totality').value
)
+ "</div><br>"
;
}
You've phrased your question in a way that suggests you wish to output an order list assembled from the content of all (html) elements with certain ids.
this won't work reliably:
Ids should be document unique.
The Js functions you use do not iterate over lists.
instead, proceed along the following lines (which assume that you import jquery, a cross-browser dom-handling and ajax library (which you should use anyway :)):
function ListOrder(){
var e_orders = $("<div id=\"YourOrders\">");
$("#order").append(e_orders);
$(".FoodName").each ( function ( idx_fn, e_fn ) {
$(e_orders).append(
$("<div/>").append(
$(e_fn).val()
+ $(e_fn).nextAll('.quantity').val()
+ $(e_fn).nextAll('.Totality').val()
);
);
$(e_orders).append("<br>");
});
return e_orders;
}
The code template assumes that the source data are elements with value attributes being marked with css classes quantity, Totality and 'FoodName``, that these elements are siblings and unique within a container element for each item incl. quantity information. It should be flexible enough to be tailored to your actual needs and html structure.

Categories

Resources