I have some complication with service removing. I have function that removes service on the server but I have to reload page to update table. I found way how to remove row by click-binding but there is the issue beacuse I can only remove row or get ID for delete service from server NOT both. :/
This is example of code that removes service on the server but doesn't remove table row.
HTML:
<table id="serviceView" class="fixed_header" border: 1>
<thead>
<tr>
<th>Name</th>
<th>Adress</th>
<th>Notification</th>
</tr>
</thead>
<tbody data-bind="foreach: services">
<tr>
<td data-bind="text: name"></td>
<td data-bind="text: address"></td>
<td data-bind="text: serviceId"></td>
<td ><button data-bind="click: $parent.DeleteService.bind(this, serviceId)">Remove</button></td>
</tr>
</tbody>
</table>
JS:
self.services = ko.observableArray([]);
self.lastCheck = ko.observable();
$.getJSON("http://localhost:55972/api/status", function (data) {
self.services(data.services);
self.lastCheck = data.lastCheck;
}); //////This is loading data to the table from server
self.DeleteService = function (serviceId) {
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
self.services.remove(serviceId)
})
};
This is example of code that removes table row
When I use click-binding like this:
<button data-bind="click: $parent.DeleteService">Remove</button>
And change delete function to this:
self.DeleteService = function (serviceId) {
self.services.remove(serviceId)
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
// here I want to remove row but i doesnt goes here without service ID.
})
};
It removes row but instead serviceId I got [object, object] in the URL.
Can you help me with it ? I got idea to use jquery to just update the table but it's seems unnecessarily complicated for me when I can use knockout.
I know the solution is not that hard but I'am just unable to solve it..... -_-
I'am sorry for taking time with this bullshit but this is my first real project and I'am so desperate at this point beacuse I have lot of things to do and I'am stucked on this.
In your Js code, you can try this:
self.services = ko.observableArray([]);
self.lastCheck = ko.observable();
$.getJSON("http://localhost:55972/api/status", function (data) {
self.services(data.services);
self.lastCheck = data.lastCheck;
}); //////This is loading data to the table from server
var serviceIdRemoved;
self.DeleteService = function (serviceId) {
serviceIdRemoved = serviceId; // now you can do whatever you need more with this value
$.ajax({
type: "GET",
url: "http://localhost:55972/api/services/remove/" + serviceId,
}).done(function () {
self.services.remove(serviceId)
})
};
With this way of work you can user the content of the variable and donĀ“t loose it. Also if you get [Object, Object], you can:
console.log(serviceId) // to see the content in the console.
JSON.stringify(data) //to see the content in html
This source could help you to understand it better.
The [object, object] you are seeing is actually the data and event objects which are secretly added to the JS function parameters by Knockout. If you want to add your own parameter to the click binding then you should do it like this:
<button data-bind="click: function(data, event) { $parent.DeleteService(serviceId, data, event) }">Remove</button>
You can then define your JS function as follows:
self.DeleteService = function (serviceId, data, event) {
[code here...]
}
You can read up on the exact details of it in the excellent Knockout documentation here:
http://knockoutjs.com/documentation/click-binding.html
It's about half-way down under the heading that reads Note 2: Accessing the event object, or passing more parameters
Related
I'm trying to display a table on a new page by calling an API and loading the data in the table. This page is loaded on click of a menuItem.
But the issue I'm facing is that the table is displaying, but not the data I'm intending to. I know that I'm able to fetch the data from the API since i can see that in the console log.
Here is the code:
In this first html file im clickling the menu and calling my next html page i want to load
and also im giving my id="covidLink" which im calling in my JS FILE.
pan.html
<div class="navbar">
<a class="covidText" id="covidLink" href="covidStatusUpdate.html">Covid-19</a>
</div>
In the below js file im making a call to the api and appending the data in tbody.
Fetchdata.js
$(document).ready(function () {
$("#covidLink").click(function () {
console.log("Link clicked...");
requestVirusData();
});
});
function requestVirusData() {
$.getJSON('https://api.covid19api.com/summary',
function(data){
var countries_list = data.Countries;
//console.log(countries_list);
$(countries_list).each(function(i, country_dtls){
$('#totalbody').append($("<tr>")
.append($("<td>").append(country_dtls.country))
.append($("<td>").append(country_dtls.TotalConfirmed))
.append($("<td>").append(country_dtls.TotalDeaths))
.append($("<td>").append(country_dtls.TotalRecovered)));
});
})
}
and lastly
statusUpdate.html
<table class="table table-striped table-bordered table-sm" cellspacing="0" width=80%>
<thead>
<tr>
<th>Country</th>
<th>TotalConfirmed</th>
<th>TotalDeaths</th>
<th>TotalRecovered</th>
</tr>
</thead>
<tbody id="totalbody">
</tbody>
</table>
What am I supposed to do ? I have to admit that I'm lost here.
I don't think you quite understand how AJAX works. You're handling a click on "covidLink". This does two things simultaneously.
it tells the browser to navigate away from the current page and go to statusUpdate.html instead.
it runs the requestVirusData() function. This gets the data from the API and returns it to the page.
But the problem is: the API call returns the data to the page where the script was called from - i.e. it returns it to pan.html. And you've just told the browser to move away from that page. Also, pan.html doesn't contain a table to put the returned data into.
The logical solution here is to link to fetchdata.js from statusUpdate.html instead, and tweak the code slightly so it runs when that page loads, rather than on the click of a button:
$(document).ready(function () {
console.log("page loaded...");
requestVirusData();
});
As suggested by ADyson i did changes in my code and now im able to display the table with data.
Here are my code changes:
statusUpdate.html
<tbody id="tbody">
<script>
var datatable;
fetch('https://api.covid19api.com/summary')
.then(function (response) {
return response.json();
})
.then(function (data) {
appendData(data);
})
.catch(function (err) {
console.log('error: ' + err);
});
function appendData(data) {
var countries_list = data.Countries;
var tbody = document.getElementById("tbody");
// clear the table for updating
$('table tbody').empty();
// hide the table for hidden initialize
$('table').hide();
// loop over every country
for (var i in countries_list) {
var country_dtls = countries_list[i];
// replace -1 with unknown
for (var o in country_dtls) {
if (country_dtls[o] == -1) country_dtls[o] = 'Unknown';
}
$('table tbody').append(`
<tr>
<td>${country_dtls.Country}</td>
<td>${country_dtls.TotalConfirmed}</td>
<td>${country_dtls.TotalDeaths}</td>
<td>${country_dtls.TotalRecovered}</td>
</tr>`);
}
}
// }
</script>
</tbody>
pan.html
<a class="covid" href="statusUpdate.html">Covid-19</a>
and now i do not need fetchdata.js obviously.
Hope this helps someone stuck like me :)
I'm having issues refreshing data that has been POSTed using Ajax. The POST is successfully being executed, but the data on the VIEW does not get refreshed with the new data. When I debug, the values from the Ajax POST are successfully being passed to my Search controller. When the controller returns the view model return View(model);, my VIEW is not refreshing the new data. How do I get the new data to show in my VIEW?
Ajax/jQuery
$(document).ready(function () {
$("#typeId, #scorecardId, #dateId").on('change', function () {
$.ajax({
url: "/StaffChange/Search",
type: "POST",
dataType: "json",
data: {
typeSelected: $('#typeId').val(),
scorecardSelected: $('#scorecardId').val(),
effectiveDateSelected: $('#dateId').val()
}
})
});
});
View
<table class="table table-condensed table-hover table-responsive table-striped">
<tr>
<th>
<a href="#Html.GetUrlAndRouteObject(Model.Sort, "change_date")">
Change Date
#Html.AddSortArrow(Model.Sort, "change_date")
</a>
</th>
<th>
#Html.EditorFor(model => model.effectiveDate, new { htmlAttributes = new { #class = "datepicker", #placeholder = "Effective Date", #id = "dateId" } })
</th>
<th>
#Html.DropDownListFor(model => model.type, new SelectList(Model.type), "-Type-", new { #id = "typeId" })
</th>
<th>
#Html.DropDownListFor(model => model.type, new SelectList(Model.scorecard), "-Scorecard-", new { #id = "scorecardId" })
</th>
</tr>
#foreach (var item in Model.get_staff_changelog_results)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Change_Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Effective_Date)
</td>
<td>
#Html.DisplayFor(modelItem => item.Type)
</td>
<td>
#Html.DisplayFor(modelItem => item.Scorecard)
</td>
</tr>
}
Controller
public ActionResult Search(string sort, string typeSelected, string scorecardSelected, DateTime? effectiveDateSelected)
{
//TO DO: Implement MVC way of permissions...
if (System.Web.HttpContext.Current.Session["ConnectelligenceAdmin"].ToString() != "true")
{
return View("AccessDenied");
}
//Get sort order for Change Date using BLL
var staffChangeSort = (String.IsNullOrEmpty(sort)) ? SortOptions.change_date_desc : (SortOptions)Enum.Parse(typeof(SortOptions), sort);
//Execute sort order for Change Date using BLL
var sortResults = _changeLogSort.SortStaffChangeLog(staffChangeSort,typeSelected, scorecardSelected, effectiveDateSelected);
//Get list of dropdown results which is used for filtering
var dropdownResults = _staffChangeFilter.StaffChangeFilter();
var model = new Hierarchy_AdjustmentViewModel { get_staff_changelog_results = sortResults.get_staff_changelog_results, Sort = staffChangeSort, type = dropdownResults.type, scorecard = dropdownResults.scorecard};
return View(model);
}
You need to use the response you receive from your ajax call. Currently your Search method is returning a view result. What you can do is, if the method is invoked from an xhr call ,you may return a partial view result which has only the markup for table rows (with the new set of data) and in your ajax call's done event, update the DOM (replace the existing table rows with this new markup).
So first create a partial view called _List.cshtml and have the code to render the table rows there
#model Hierarchy_AdjustmentViewModel
#if(Model.get_staff_changelog_results!=null)
{
foreach (var item in Model.get_staff_changelog_results)
{
<tr>
<td> #item.Change_Date </td>
<td> #item.Effective_Date </td>
<td> #item.Type </td>
<td> #item.Scorecard </td>
</tr>
}
}
You can use the same partial view in your main view as well to reduce duplicate code
Now update your action method to return this partial view when the request was made from ajax code
public ActionResult Search(string sort, string typeSelected)
{
// Your existing code to get data goes here
var model = new Hierarchy_AdjustmentViewModel();
mode.get_staff_changelog_results = sortResults.get_staff_changelog_result;
if (Request.IsAjaxRequest())
{
return PartialView("_List", model);
}
return View(model);
}
Now all you have to do is, use this response to update the table. Since you are returning view result (HTML markup), you do not need to specify dataType as json.
$("#typeId, #scorecardId, #dateId").on('change', function () {
var $tbl = $(this).closest("table");
$tbl.find("tbody").find("tr:gt(0)").remove();
$.ajax({
url: "#Url.Action("Search","StaffChange")",
type: "POST",
data: {
typeSelected: $('#typeId').val(),
scorecardSelected: $('#scorecardId').val(),
effectiveDateSelected: $('#dateId').val()
}
}).done(function(res) {
$tbl.find("tbody").append(res);
}).fail(function(x, a, e) {
alert(e);
});
});
Another option is returning the data as a JSON array, and the done handler has to parse it (loop through it) and create markup for each row and append to the table (after clearing existing rows).
Because you're not responding to the AJAX call in any way. Add a .done() callback handler to the AJAX call:
$.ajax({
/*...*/
}).done(function (response) {
// update the page in here
});
At that point the question becomes... What sort of updates do you plan to do? It looks like you're returning a view from the controller:
return View(model);
So the data you're getting back in the AJAX response is a bunch of raw HTML. If it's a subset of a full page (that is, no additional <head>, <body>, etc. tags) then you could replace an existing container element with the contents of the response:
$('#someContainer').html(response);
However, this tends to be a bit sloppy and can cause other problems. (For example, replacing DOM elements which have handlers attached to them or plugins initialized on them, requiring you to re-think how you approach some things.) Instead, for AJAX calls it's common to return JSON data:
return Json(model);
This returns just the data instead of all the HTML surrounding this. This is useful for a couple of reasons:
It's easier to manipulate if you only want to do small and specific things.
It uses less network bandwidth.
It's just plain silly to return a bunch of HTML that the page already has.
With that data you can then update the specific elements on the page. For example, maybe you want to update the value of an input:
$('#someInput').val(response.SomeProperty);
Or the text of a display element:
$('#someElement').text(response.AnotherProperty);
How you handle the response and what you need to do to your page to "update it" is up to you. The point is that the system doesn't automatically do this for you. You need to handle the AJAX response and write your logic accordingly.
I am trying to fetch the data from files using Ajax by clicking row of table (passing row values to button on clicking rows) or by entering the variables in text box and pressing button. But it does not seem to be working.(Pls don't downvote as i am C++ programmer and learning web development.)
<!DOCTYPE html>
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"> </script>
<table bodrder=1 class='list'>
<thead>
<tr>
<th class='A'>ID</th>
<th class='B'>Value</th>
<th class='C'>Name</th>
<th class='D'>Cell #</th>
<th class='E'>Nickname</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>54235</td>
<td>Benjamin Lloyd</td>
<td>(801) 123-456</td>
<td>Ben</td>
</tr>
<tr>
<td>2</td>
<td>44235</td>
<td>XXXXXX</td>
<td>642363673</td>
<td>TRE</td>
</tr>
</tbody>
</table>
<div id="tabs" class="plots-tabs" style="padding-top: 10px; padding-bottom: 10px">
<table>
<tr><td>ID:<input id="id" type="text" class="inputbox" /></td></tr>
<tr><td>Value:<input id="value" type="text" class="inputbox" /></td></tr>
</table>
This is DIV element which will be filled by div element on clicking button or by clicking table row which also generate the event and click the button by passing values to ajax and fetchign data.
<p style="width: 100%; text-align: right;"><button type="button" id="button">Submit</button></p>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
//here ID and value are parsed through table click event or from text box on clicking button
$.ajax({
url:filename,
data: {
ID: $("input#id").val(),
Value: $("input#value").val()
},
success:function(result){
$("#tabs").html(result);
}});
var filename= "Data_"+ID+"_"+Value+".txt";
$("#tabs").load(filename);
});
});
var table = document.getElementsByTagName("table")[0];
var tbody = table.getElementsByTagName("tbody")[0];
tbody.onclick = function (e) {
e = e || window.event;
var data = [];
var target = e.srcElement || e.target;
while (target && target.nodeName !== "TR") {
target = target.parentNode;
}
if (target) {
var cells = target.getElementsByTagName("td");
for (var i = 0; i < 2; i++) {
data.push(cells[i].innerHTML);
}
}
alert(data);
};
</script>
</body>
</html>
cat Data_2_54235.txt
Nice Work! Your code is working with first file.
cat Data_2_44235.txt
Nice Work! Your code is working with second file.
how can i implement the above code.
I see you generate a filename based on input values. That means that the ajax call will be made upon that filename, which is odd, becouse you have to create a file with that name.
Anyway, i don't see nowhere in your code that by clicking table rows you make an ajax call, you only save the innerHTML text to a variable data = [] and then alert it. But the problem is not here (if you don't expect to make ajax call when clicking table-rows), but it is inside the ajax call you are making when clicking the button.
first
url:filename
var filename= "Data_"+ID+"_"+Value+".txt";
I strongly suggest you don't do that. It will work if you make an ajax call to a php script which creates that txt file with filename name, and then make another ajax call to that file and fetch it.
second
data: {
ID: $("input#id").val(),
Value: $("input#value").val()
}
look here at data, the doc explains it. the code above means that to filename it will pass parameters (GET parameters, i.e. x?=...), but becouse your file is .txt, this doesn't make sense.
third
$("#tabs").load("demo_test.txt");
This will add the text inside demo_test.txt to $("#tabs") , like innerHTML does or .html() does. Do you have demo_test.txt on your host? i suppose this should work.
just change you ajax call and load call with this. this should work :
$("button").click(function() {
$.ajax({
url : "demo_test.txt",
dataType: "text",
success : function (data) {
$("#tabs").html(data);
}
});
});
For clicking the table-rows, just add an event listener to table-rows, and make an ajax call. read the link i send you, as they are important to understand better what is ajax.
You can see no unnecessary data parameter is thrown to ajax call, and i put there an dataType, meaning that we expect text data to be recieved. If this doesn't work, you have to be sure that you are working on localhost server(for ajax to work...) and you have demo_test.txt , and the url is passed correctly
example using input values to fetch from ajax:
$("button").click(function() {
var id = $("input#id").val();
var value = $("input#value").val();
$.ajax({
url : "Data_" + id + "_" + value + ".txt",
dataType: "text",
success : function (data) {
$("#tabs").html(data);
},
error: function (data) {
#("#tabs").html('No such file found on server');
}
});
});
example of event handler click <tr>
$("table tbody").on("click", "tr", function() {
var id = $(this).find("td")[0].text(); // gets the first td of the clicked tr (this is the ID i suppose)
var value = $(this).find("td")[1].text(); // gets the second td of the clicked tr (this is the VALUE i suppose)
$.ajax({
url : "Data_" + id + "_" + value + ".txt",
dataType: "text",
success : function (data) {
$("#tabs").html(data);
},
error: function (data) {
#("#tabs").html('No such file found on server');
}
});
});
I have a knockout observableArray bound to a table below.
<table id="Users">
<thead>
<tr>
<td>User Name</td><td>Primary Email</td><td>Product Role</td><td>Active</td><td>EditUser?</td>
</tr>
</thead>
<tbody data-bind="foreach: CustomerUsers">
<tr>
<td data-bind="text: UserName"></td>
<td data-bind="text: PrimaryEmail().EmailAddress"></td>
<td><select></select></td>
<td data-bind="text: StaticActiveText"></td>
<td>Edit User</td>
</tr>
</tbody>
</table>
And the following view model:
function CustomerAdminVm() {
var vm = this
this.CustomerUsers = ko.observableArray(GetCustomerUsers());
}
My problem is that when the view model initially loads GetCustomerUsers it correctly gets the values it needs, and inserts them into the observable array. However, the elements are not displayed in the table.
The strange thing is that if I call the following function:
this.AddUserToCustomer = function () {
if (vm.NewUser) {
vm.CustomerUsers.push(vm.ActiveUser());
}
vm.CloseUserModalDialog();
}
The user is added to the array, and correctly displayed in the table. Even more confusing is that the latest value of the array in the push shows the initial values that are not displayers are in the array.
Does anyone have any idea what could be causing this behavior?
The code for GetCustomerUsers is:
function GetCustomerUsers() {
var users = [];
$.ajax({
type: 'Get',
url: ControllerBase + 'Actions/GetAllUsersForCustomer',
async: false,
success: function (data) { users = $.map(data, function(item) { return new ObservableUser(item); }); }
});
return users;
}
And ActiveUser is also an ObservableUser.
What you should do is to create the ko variable in the constructor and then update it with the ajax values when the data arrives.
function CustomerAdminVm() {
var vm = this
this.CustomerUsers = ko.observableArray();
GetCustomerUsers(this.CustomerUsers);
}
function GetCustomerUsers(getData) {
$.ajax({
type: 'Get',
url: ControllerBase + 'Actions/GetAllUsersForCustomer',
async: false,
success: function (data) { getData($.map(data, function(item) { return new ObservableUser(item); })); }
});
}
Ok I actually found a work around for this. Instead of populating the data as part of the constructor I add it after the document is ready
function CustomerAdminVm() {
var vm = this
this.CustomerUsers = ko.observableArray();
$(document).read(function(){vm.CustomerUsers(GetCustomerUsers());});
}
I'm going to throw a theory out as to what is actually happening, feel free to tell me I am wrong, but the solution does work.
What I believe to be happening is the data is being populated in the viewmodel constructor. But because the call is a synchronous call the constructor is not completing before the data is being populated. then the apply bindings function is being called, but because the data is prepoulated before the apply bindings function comes back the data already in the object is ignored because it has no observable change. During a normal async operation the call the constructor completes, then the apply bindings function is applied, and then the data comes back and the observable is changed causing an event flag to update the view.
I'm not sure if this is a knockout issue, a JSON issue or some other issue so I'll just explain it all.
I have a Web grid displaying a number of users. I can edit these members using a AJAX pop-up window. As part of this window I can assign roles to a particular user. As seen in the following image:
These roles are chosen from dropdown menus and if I want to apply multiple roles to a user I can add extra dropdowns. I do this using knockout's foreach binding.
Now after I hit save the webgrid columns update automatically in ALL browsers. However if I edit a user and give them extra roles, then hit save and then try and edit them again their newly added roles are not showing up in IE. Chrome and Firefox work perfectly however.
When I click on the 'save' button on this popup window the data is sent to the server as JSON using the following code:
$.ajax({
type: 'POST',
dataType: 'json',
url: '/Person/Save',
dataType: 'html',
data: JSON.stringify(jsonObj),
contentType: 'application/json; charset=utf-8',
success: function(data) {
dlg.dialog("close");
$('#myUserGrid').html(data);
},
error: function(data) {
console.debug(data);
}
});
The relevant html and knockout code from the popup window view is as follows:
<tr>
<td class="roleList" colspan="2">
<table data-bind="foreach: RoleDdList">
<tr>
<td class="label">
* Role:
</td>
<td>
<select data-bind="options: $root.Roles, value: $data.role, optionsValue:'Id', optionsText:'Name'"> <!--,optionsCaption: 'No Role' -->
</select>
</td>
</tr>
<tr>
<td></td>
<td>Delete Role</td>
</tr>
</table>
</td>
<tr></tr>
There is a lot of code in the ViewModel but the relevant pieces are
var Role = function(roleId, roleName) {
this.Id = roleId;
this.Name = roleName;
};
// Class to represent a row in the Roles DropDown List
function RoleDropDown(initialRole) {
var self = this;
self.role = ko.observable(initialRole);
}
$(function () {
function myViewModel() {
var self = this;
//code relating to other fields
// Role Code
self.Roles = new ko.observableArray();
#{
foreach (var pair in Model.RolesList)
{
#:self.Roles.push(new Role(#pair.Key,"#pair.Value"));
}
}
// List of Role DropDowns
self.RoleDdList = ko.observableArray([
new RoleDropDown(self.Roles()[0])
]);
// Add a Role (code for link on form)
self.addRole = function() {
self.RoleDdList.push(new RoleDropDown(self.Roles()[0]));
};
// Delete a Role
self.deleteRole = function(num) {
self.RoleDdList.remove(num);
};
//
#{
if(#Model.Roles!=null)
{
#:self.RoleDdList.pop();
foreach (var selectedRole in Model.RoleNumbers)
{
#:self.RoleDdList.push(new RoleDropDown(#selectedRole));
}
}
}
}
vm = new myViewModel();
ko.applyBindings(vm);
$.validator.unobtrusive.parse('#formUser');
});
Any ideas for what the root of this problem is?