DOM manipulation inside a callback - javascript

I am building a client that is connected to a socket.io server. I have this code:
$(document).ready(function () {
Desktops.onData(function (desktops) {
var desktopsList = $("#desktops");
$.each(desktops, function (id, desktop) {
console.log(desktop.name);
desktopsList.append('<option value="' + id + '">' + desktop.name + '</option>');
});
});
Sockets.connect();
});
The callback inside Desktops.onData gets called when the data comes back from the server. I then take this data(desktops) and try to append it into a select element, which is used with a Bootstrap plugin. In the HTML side I have:
<select id="desktops" class="selectpicker" data-live-search="true">
</select>
I know that the problem is not in the data because console.log works. I also know that the problem is not with my HTML or jQuery code because when I try to append to the select outside of the callback, it works. So it must be something about doing jQuery inside callbacks. What can I do to make that jQuery work?
Thanks.

Given the plugin you've stated that you're using (Bootstrap Select), you need to call the refresh method on it for it to recognise that new option elements have been added to the underlying select. Try this:
Desktops.onData(function (desktops) {
var desktopsList = $("#desktops");
$.each(desktops, function (id, desktop) {
console.log(desktop.name);
desktopsList.append('<option value="' + id + '">' + desktop.name + '</option>');
});
desktopsList.selectpicker('refresh'); // update the bootstrap select here
});

Related

Cannot show TrustPilot HTML when inserted with JavaScript (jQuery)

If I added the TrustPilot html code directly on the HTML page, it works fine but I needed to insert it with jQuery. I see the HTML code when inserted but it's not displaying.
$(window).on('load', function () {
var el = $('#some-element');
el.html( trustPilotHtml() );
function trustPilotHtml() {
var str = "<div " +
"class='trustpilot-widget' " +
"data-locale='en-GB' " +
"data-template-id='123456' "+
"data-businessunit-id='123456' " +
"data-style-height='500px' " +
"data-style-width='100%' " +
"data-theme='light' " +
"data-stars='4,5' " +
"data-schema-type='Organization'>" +
"<a " +
"href='https://some-url.com' target='_blank'>Trustpilot</a> " +
"</div>";
return $(str);
}
});
Is the only way of getting the element to display properly is to directly inserted into the HTML without javascript?
No it's not the only way.
You should have a bootstrap script in your inside the HEAD part of your HTML (or close to it).
This script takes care of initializing all the widgets (TrustBoxes in Trustpilot lingo) that you have in your HTML.
Of cause that doesn't work if you are injecting the HTML dynmically, so it's also possible to call window.Trustpilot.loadFromElement(trustbox); yourself, when if you need to.
Here trustbox is a HTMLElement, you could get by using document.getElementById("some-element") or similar.
Reference: https://support.trustpilot.com/hc/articles/115011421468
The following worked for me on a product list page which returned filtered list via ajax
var element = document.getElementsByClassName("trustpilot-widget");
for(var i=0; i<element.length; i++) {
window.Trustpilot.loadFromElement(element[i]);
}
On the first page load all product reviews are displayed as expected, but if the page is updated via ajax call (using filters for example) the reviews are lost. Running the above code after ajax, reloads the reviews

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 to call JS function in newTextBoxDiv.after().html?

I document.write("<option></option>") 500 options for select in a function named laborlist(). When user click on select it just print out all those 500plus s on page nothing else. I don't know how to call javascript or php function in newTextBoxDiv.after().html
newTextBoxDiv.after().html('<select class="select2_single form-control" name="jobrequest' + counter + '"><option></option>' + laborlist() + '</select>');
Another question is which method is faster to make database table of 500 laborlist and call it through AJAX or method i am using? Thank you
function laborlist(){
var labourList = document.getElementById("labourList");
// use innerHTML instead of document.write()
labourList.innerHTML +="<option>1</option><option>2</option><option>3</option>and so on";
}
I don't think that would be possible.
Instead you can have a id for the select tag and use that id in laborlist() funtion to append option to it.
So it would be something like this:
newTextBoxDiv.after().html('<select id="labourList" class="select2_single form-control" name="jobrequest' + counter + '"><option></option>' + '</select>');
function laborlist(){
var labourList = document.getElementById("laborList");
// use innerHTML instead of document.write()
labourList.innerHTML += "<option>...</option>"
}

How to pass multiple parameter to a function

I am using onclick event to perform some acction, but for som reason the second ID is not being passed what am I doing wrong here:
row += '<td>' + data[staff].Naame + '(' + data[staff].place1 + 'fID="' + data[staff].id+ '"' +')</td>'
$(document).on("click", ".name", function (e) {
var code = ($(this).attr("code"))
var fID = ($(this).attr("fID"))
function(code, fID);
});
For some reason fID is not being passed from 'fID="' + data[staff].id+ '"' to function(code, fID); why is that?
Avoid using loads of string concatenation in jQuery, to create elements, as it is generally unreadable and leads to typing mistakes (like not putting the fId inside the tag attributes):
Instead build the element with jQuery. I am not 100% sure of what your link should look like from the code, but something like this (tweak to suit):
var $td = $('<td>').html(data[staff].Naame);
$td.append($('<a>', {class: 'name', code: data[staff].place, fId: data[staff].id}).html(data[staff].place1));
row.append($td);
I think you need to define fID within the <a ... > tag - like you are doing for code.
ie:
...
Try this.
row += '<td>' + data[staff].Naame + ''+data[staff].place1+'</td>'

cascading dropdown list on mvc4 not updating nor loading javascript file

I'm currently trying to create a new set of cascading dropdown lists by following this tutorial :
http://blogs.msdn.com/b/rickandy/archive/2012/01/09/cascasding-dropdownlist-in-asp-net-mvc.aspx
problem is it doesnt work and I'm basically illiterate on javascript (currently I dont have the time to sit and learn it), somehow the dropdown lists are not working, only the first one shows the first hierarchy info.
This is what I've done:
First the controller:
Here's the index method:
public ActionResult Index()
{
var list = repo.GetParentEstablishments();
ViewBag.Parent = (new SelectList(list.ToArray(),"EstablishmentId","Name"));
return View();
}
Here's the method that's supposed to return the list of children for the selected father:
[HttpPost]
public ActionResult Children(int parentId)
{
var parents = repo.GetChildrenEstablishments(parentId);
return Json(new SelectList(parents, "EstablishmentId", "Name"));
}
Here's the view for the index method:
#using (Html.BeginForm("Index", "Ticket", FormMethod.Post, new { id = "ParentChildrenFormID", data_childrenListAction = #Url.Action("ChildrenList") }))
{
<fieldset>
<legend> Parent/Children</legend>
#Html.DropDownList("Parents", ViewBag.Parent as SelectList, "Select a Parent", new {id="ParentsID"})
<div id="ChildrenDivId">
<label for="Children">Children </label>
<select id="ChildrenID" name="Children"></select>
</div>
<p>
<input type ="submit" value="Submit" id="SubmitID" />
</p>
</fieldset>
}
<script src ="#Url.Content("~/Scripts/parentChildren.js")"></script>
And finally here's the script file:
$(function () {
$('#ChildrenDivId').hide();
$('#SubmitID').hide();
$('#ParentsID').change(function () {
var URL = $('#ParentChildrenFormID').data('childrenListAction');
$.getJSON(URL + '/' + $('#ParentsID').val(), function (data) {
var items = '<option>Select a Children</option>';
$.each(data, function (i, child) {
items += "<option value='" + child.value+ "'>" + child.Name + "</option>";
// state.Value cannot contain ' character. We are OK because state.Value = cnt++;
});
$('#ChildrenID').html(items);
$('#StatesDivID').show();
});
});
$('#ChildrenID').change(function () {
$('#SubmitID').show();
});
});
For what I've managed to understand from the javascript function, the div that has the label and dropdown for the children should appear hidden once the page is loaded and appear once the user selects a parent from the first list, currently this is not happening and instead everything shows up once the page is loaded. Also once I select a parent nothing happens on the second list, I can infer from this that the javascrip file is not being executed on the users browser, why isn't it being executed? what am I doing wrong?
Thank in advance, any help will be appreciated.
You have an error on the following line
items += "<option value='" + child.value + "'>" + child.Name + "</option>";
Which should be:
items += "<option value='" + child.Value + "'>" + child.Text + "</option>";
Since your controller action is returning a SelectList, this class is an IEnumerable<SelectListItem> where SelectListItem has 2 properties called Value and Text.
There's also another issue with your code. You have used the folllowing to construct the url to your controller action:
URL + '/' + $('#ParentsID').val()
which will result in an url of the form:
/SomeController/Children/123
But the action argument of your Children action is called parentId. If you are using the default route setup, only an {id} parameter will be sent as part og of the uri path (that's the default route pattern: {controller}/{action}/{id} and not {controller}/{action}/{parentid}). So you should either change your route setup or pass the parentId parameter like this:
$.getJSON(URL, { parentId: $('#ParentsID').val() }, function (data) {
...
});
Yet another issue with your code is that your Children conrtoller action is decorated with the [HttpPost] attribute but the $.getJSON method sends a GET request. So it won't work. You could use the $.post method instead:
$.post(URL, { parentId: $('#ParentsID').val() }, function (data) {
...
});
Another issue is that you are showing the StatesDivID but your actual div that was hidden is ChildrenDivId. Make sure you are showing the proper element:
$('#ChildrenDivId').show();
Also make sure you have the jQuery script referenced before your parentChildren.js script. Right now you have included your script inside the view but cannot see jQuery being included. Maybe you have it in your _Layout.
I would recommend you using a javascript debugging tool such as FireBug or Chrome developer toolbar to analyze your javascript code.

Categories

Resources