Binding JSON Data Back to the MVC View to dynamically insert data to a table - asp.net-mvc-4

I am working on an MVC4 application. In the submit action of the button, i am making a call to a controller action, which will return data back to me as Json. Number of rows in the table is dependent on the number of rows returned by the service.
I have created a new View Model to support this.
public class DataResponse
{
public List<ALSResult> Result { get; set; }
public string ResponseText {get;set;}
}
public class ALSResult
{
public string Name {get;set;}
public string DataResult {get;set;}
}
Controller Action
[HttpPost]
public ActionResult Submit(CViewModel calcModel)
{
try
{
DataResponse response = cRepository.GetDetails(calcModel.SelectedPlan,calcModel.EquipmentPrice,calcModel.DownPayment);
return Json(response, JsonRequestBehavior.DenyGet);
}
}
js file
$("#Submit").click(function () {
other code here
Process("/DPPC/Submit", "POST", JSON.stringify(model), "json", "application/json;charset=utf-8", logError, InvokeSuccess);
}
function InvokeSuccess(result) {
i will get json result in result filed
}
index.cshtml
<table id="tblResults">
<tr>
<td id="tdResultEt"></td>
<td id="tdResultEtPrice"></td>
</tr>
<tr>
<td id="tdResultDt"></td>
<td id="tdResultDtPrice"></td>
</tr>
more rows depending on the number of items in the JSON response
</table>
How do i dynamically bind the response from the Service to create rows for the table and bind the results?

I don't test it, and it maybe contain syntax errors
function InvokeSuccess(result) {
var table = $("#tblResults");
for(var i = 0; i < result.Result.lenght; ++i)
{
var item = result.Result[i];
var newTr = $("<tr>");
var newTd1 = $("<td>");
var newTd2 = $("<td>");
newTd1.text(item.Name);
newTd2.text(item.DataResult);
newTr.append(newTd1);
newTr.append(newTd2);
table.append(newTr);
}
}

Related

Azure Storage FileShare with ASP.Net Core and Razor Page

Beginner question
I am using the latest ASP.NET Core SDK to build an MVC/Razor page to display user files. We have the files in Azure Storage FileShare. The difficulty I'm running into is getting the FILES to list out and there is very little documentation on how to do this out there. Once I finally get it figured out I would like to create a post on Medium or somewhere else to help any other beginners.
The file structure of the File Share is as such:
Azure File Share
MainShare
EmployeNumber
Folder1
files.pdf
Folder2
files.pdf
Folder3
files.pdf
I have been able to successfully get blob information to display since that's the bulk of information out there, but I'm having trouble FileShare to display anything.
At first, I was running into an invalid cast issue where it was trying to cast CloudFileDirectory on my CloudFile so I found a solution to help it determine what to cast where. Now the page will attempt to run but nothing is produced and the page just loads and loads.
FileController.cs
public async Task<IActionResult> Index()
{
string filestorageconnection = _configuration.GetValue<string>("filestorage");
CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(filestorageconnection);
//CloudFile cloudFile = null;
CloudFileShare fileShare = null;
CloudFileClient cloudFileClient = cloudStorageAccount.CreateCloudFileClient();
fileShare = cloudFileClient.GetShareReference("MainShare");
List<IListFileItem> shareData = new List<IListFileItem>();
List<FileShareData> fileData = new List<FileShareData>();
FileContinuationToken token = null;
do
{
FileResultSegment resultSegment =
await fileShare.GetRootDirectoryReference().ListFilesAndDirectoriesSegmentedAsync(token);
foreach (var fileItem in resultSegment.Results)
{
if (fileItem is CloudFile)
{
var cloudFile = (CloudFile) fileItem;
//await cloudFile.FetchAttributesAsync(); <--- Not sure this does what i'm looking for
// Add properties to FileShareData List
fileData.Add(new FileShareData()
{
FileName = cloudFile.Name,
LastModified = DateTime.Parse(cloudFile.Properties.LastModified.ToString()).ToLocalTime().ToString(),
Size = Math.Round((cloudFile.Properties.Length / 1024f) / 1024f, 2).ToString()
});
}
else if (fileItem is CloudFileDirectory)
{
var cloudFileDirectory = (CloudFileDirectory) fileItem;
await cloudFileDirectory.FetchAttributesAsync();
}
}
} while (token != null);
return View(fileData);
}
FileShareData.cs
namespace FileShareMVC.Models
{
public class FileShareData
{
public string FileName { get; set; }
public string LastModified { get; set; }
public string Size { get; set; }
}
}
ShowAllFiles.cshtml
#model List<FileShareData>
#{
ViewData["Title"] = "ShowAllFiles";
}
<h1>ShowAllBlobs</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>FileName</th>
<th>FileSize</th>
<th>ModifiedOn</th>
<th>Download</th>
</tr>
</thead>
<tbody>
#foreach (var data in Model)
{
<tr>
<td>#data.FileName</td>
<td>#data.Size</td>
<td>#data.LastModified</td>
<td> Download </td>
</tr>
}
</tbody>
</table>
I'm not sure where to set a breakpoint to see whats stalling where. I've looked at the Network files in chrome when loading the page but nothing populates either.
Suggestions?
Regarding how to list all files in one Azure file share with method ListFilesAndDirectoriesSegmentedAsync, please refer to the following code
The file structure of the File Share is as such:
Azure File Share
MainShare
mydirectory
logs
STATS.LOG
csv
test.csv
cert
examplecert.pfx
The SDK I use
<PackageReference Include="Microsoft.Azure.Storage.File" Version="11.1.7" />
Code
FileController.cs
public class FileController : Controller
{
public async Task<IActionResult> Index()
{
string accountName = "blobstorage0516";
string key = "eGier5YJBzr5z3xgOJUb+snTGDKhwPBJRFqb2nL5lcacmKZXHgY+LjmYapIHL7Csvgx75NwiOZE7kYLJfLqWBg==";
var storageAccount = new CloudStorageAccount(new StorageCredentials(accountName, key), true);
var share = storageAccount.CreateCloudFileClient().GetShareReference("mainshare");
var dir =share.GetRootDirectoryReference();
//list all files in the directory
var fileData = await list_subdir(dir);
return View(fileData);
}
private static async Task<List<FileShareData>> list_subdir(CloudFileDirectory fileDirectory)
{
var fileData = new List<FileShareData>();
FileContinuationToken token = null;
do
{
FileResultSegment resultSegment = await fileDirectory.ListFilesAndDirectoriesSegmentedAsync(token);
foreach (var fileItem in resultSegment.Results) {
if (fileItem is CloudFile) {
var cloudFile = (CloudFile)fileItem;
//get the cloudfile's propertities and metadata
await cloudFile.FetchAttributesAsync();
// Add properties to FileShareData List
fileData.Add(new FileShareData()
{
FileName = cloudFile.Name,
LastModified = DateTime.Parse(cloudFile.Properties.LastModified.ToString()).ToLocalTime().ToString(),
// get file size as kb
Size = Math.Round((cloudFile.Properties.Length / 1024f), 2).ToString()
});
}
if (fileItem is CloudFileDirectory)
{
var cloudFileDirectory = (CloudFileDirectory)fileItem;
await cloudFileDirectory.FetchAttributesAsync();
//list files in the directory
var result = await list_subdir(cloudFileDirectory);
fileData.AddRange(result);
}
}
// get the FileContinuationToken to check if we need to stop the loop
token = resultSegment.ContinuationToken;
}
while (token != null);
return fileData;
}
}
FileShareData.cs
public class FileShareData
{
public string FileName { get; set; }
public string LastModified { get; set; }
public string Size { get; set; }
}
ShowAllFiles.cshtml
#model List<FileShareData>
#{
ViewData["Title"] = "ShowAllFiles";
}
<h1>ShowAllBlobs</h1>
<table class="table table-bordered">
<thead>
<tr>
<th>FileName</th>
<th>FileSize</th>
<th>ModifiedOn</th>
<th>Download</th>
</tr>
</thead>
<tbody>
#foreach (var data in Model)
{
<tr>
<td>#data.FileName</td>
<td>#data.Size</td>
<td>#data.LastModified</td>
<td> Download </td>
</tr>
}
</tbody>
</table>
Result
For more details, please refer to here and here

Why is my controller return wrong response data?

I`m using Ajax call to remove items from list. When user clicks on remove button (each button for row with id) it sends Ajax call (Post) to Order/RemoveFromCart. When backend is reached i remove the item from list. Im sure when returning data from controller the item i want to remove is truly removed. The problem is when i take my div and call html(data) from response it removes always the last item in the list.
Thank you for help.
I have tried update dotnet to latest version.
I have tried to remove Ajax call and reload whole view - that fixes the problem but i want to use an Ajax call.
#for (int i = 0; i < Model.CartItems.Count; i++)
{
<input asp-for="CartItems[i].ProductId" type="hidden">
<tr>
<td>
<input type="text" class="inputTextWithoutBorder" readonly="readonly" asp-for="CartItems[i].Product">
</td>
<td>
<select class="select-table" asp-for="#Model.CartItems[i].Packaging" asp-items="#Model.CartItems[i].PackageTypes"></select>
</td>
<td>
<input type="text" class="inputTextWithoutBorder" asp-for="#Model.CartItems[i].Amount" placeholder="Vyberte množství...">
</td>
<td>
<button id="removeFromCartId" onclick="removeFromCart(this);" type="button" value="#i" class="removeButton"></button>
</td>
</tr>
}
[HttpPost]
public IActionResult RemoveFromCart(OrderModel model, int itemToRemove)
{
if (model.CartItems[itemToRemove] != null)
{
model.CartItems.RemoveAt(itemToRemove);
}
return PartialView("_OrderCartWithCalendarPartial", model);
}
Model:
public class CartModel
{
public int ProductId { get; set; }
public int Amount { get; set; }
public string Packaging { get; set; }
public string Product { get; set; }
public List<SelectListItem> PackageTypes { get; } =
new List<SelectListItem>{
new SelectListItem {Value = "", Text = ""},
};
}
Javascript:
function removeFromCart(button) {
var index = button.value;
var placeholderElement = $('#orderFormId');
var myformdata = placeholderElement.serialize()
+ "&itemToRemove=" + index;
var result = $.post("/Order/RemoveFromCart", myformdata);
result.done(function (data) {
$("#cartWithCalendarDiv").html(data);
setDateFieldToZero();
});
}
Problem scenario:
Sending via Ajax list with two items (item0, item1). The controller receives exact list with two items. Remove item0 in controller and return partial view with that updated model. The page reloads only with item0. Why?

How to pass all text of input selected row into action?

i have this view in my project.
I want to get the text of input in each row that is selected.
How to pass all text of input selected row into action
<table width="100%" class="table table-striped table-bordered table-hover" id="dataTables-example">
<thead>
<tr>
<th width="45%">Select</th>
<th width="45%">User Name</th>
<th width="5%">Description</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model.TypeList)
{
<tr>
<td>
<input type="checkbox" name=checklist" id="checklist"/>
</td>
<td>
#Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
<input type="text" name="Extradecription"/>
</td>
</tr>
}
</tbody>
my Actions. How can I have the corresponding values of text and checkbox for the Selected rows
public IActionResult Index()
{
return View(repository.GetUser());
}
public IActionResult Save(int[] checklist,string[] Extradecription)
{
repository.Save(checklist,Extradecription);
return View(repository.GetUser());
}
If you try to get two different arrays as you have showed in you controller-action code, there will be a trouble with text for non selected items, the array for check boxes will bind as expected but for descriptions will be different, just to be clear, check the following example:
Assuming We have a list with tree options:
100 - Foo
200 - Bar
300 - Zaz
If We set the following selection for items:
Foo, a
Zaz, c
If We take a look on the request, this is the raw request:
checklist = 100,300
Extradecription = a,null,c
So, the trouble is avoid to bind null descriptions for non selected options, this is complicated, in that case I recommend to you a clear solution:
Create a model to create entity process
Create a model for option
Add a list of option model in create entity model
Initialize the model to create a new entity
Render inputs in view using asp-for tag
Retrieve the request to create a new entity
I'll assume the name of models and properties to show how to bind a typed array in your request, change the names according to your scenario.
Create entity model:
public class CreateEntity
{
public CreateEntity()
{
Items = new List<SelectedItem>();
}
// Step 3
[BindProperty]
public List<SelectedItem> Items { get; set; }
// Another properties
}
Model for option:
public class SelectedItem
{
public bool IsSelected { get; set; }
public int Code { get; set; }
public string Name { get; set; }
public string Desc { get; set; }
}
Rendering the options list:
#for (var i = 0; i < Model.Items.Count; i++)
{
<input asp-for="#Model.Items[i].IsSelected" />#Model.Items[i].Name
<input asp-for="#Model.Items[i].Desc" />
<br/>
}
The GET and POST actions in controller:
[HttpGet]
public IActionResult CreateOption()
{
// Set the items list
var model = new CreateEntity
{
Items = new List<SelectedItem>
{
new SelectedItem{ Code = 100, Name = "Foo" },
new SelectedItem{ Code = 200, Name = "Bar" },
new SelectedItem{ Code = 300, Name = "Zaz" }
}
};
return View(model);
}
[HttpPost]
public IActionResult CreateOption(CreateEntity form)
{
// Retrieve only selected items
var query = form.Items.Where(item => item.IsSelected == true).ToList();
return View();
}
If you want to know more about check boxes in Razor pages, please check this link: Checkboxes in a Razor Pages Form
Please let me know if this answer is useful.

Unable to send parameters in #html.actionlink to partialview

I have one actionlink inside the foreach loop. Depending on the model items I am generating link buttons with values dynamically.
This is how it looks
#foreach (var group in Model.records)
{
<tr>
<td>#Html.ActionLink(#group.clientId.ToString(), "detailsbyClientId", "DocumentVerification", new { id = #group.clientId.ToString()},null)</td>
<td>#group.clientName</td>
<td>#group.Count</td>
</tr>
}
On clicking link button i am calling partial view as below.
[HttpGet]
public PartialViewResult detailsbyClientId(int? clientId)
{
return PartialView();
}
I am receiving a null value for clientId but #group.clientId.ToString() contains a valid value.
The parameter in your method is named clientId but your generating a query string value named id. Change one or the other to match
public PartialViewResult detailsbyClientId(int? ID)
{
....
}
and in the view
#Html.ActionLink(#group.clientId.ToString(), "detailsbyClientId", "DocumentVerification", new { id = group.clientId },null)
Note you do not need the # nor .ToString() in the 4th parameter - just new { id = group.clientId }
Try this:
The point is the name of the variable you are passing and the name of the variable that is expecting the value in the controller should be the same.
#foreach (var group in Model.records)
{
<tr>
<td>#Html.ActionLink(#group.clientId.ToString(), "detailsbyClientId", "DocumentVerification", new { clientId = group.clientId},null)</td>
<td>#group.clientName</td>
<td>#group.Count</td>
</tr>
}
[HttpGet]
public PartialViewResult detailsbyClientId(int? clientId)
{
return PartialView();
}

Why won't a List of complex types bound to TextBoxes in a table show changes to the model in MVC 4?

I have run into an issue that seems pretty simple, but I have not been able to find a solution. I have created a ReportModel object that is the model in the view. The ReportModel contains a list of FinancialHistory objects. I populate the objects and display them in a table of textboxes within a form in the view using default binding (This works correctly). The user can then submit the form to refresh the FinancialHistory objects from a different datasource, replacing what was previously in the list with the new results. When the new results are returned, I can see that the model contains the expected new values, but when the HTML is rendered, the original amounts still appear. If the new results contains more objects than the original list (as shown in the example code), the added rows do appear with the correct values. So, if the original had 2 objects and the refreshed list has 3, the resulting HTML shows the first 2 rows with the old values and a 3rd row with the new values.
Here are the models:
public class ReportModel
{
public string AccountNumber { get; set; }
public IList<FinancialHistory> FinancialHistories { get; set; }
}
public class FinancialHistory
{
public FinancialHistory()
{
Id = Guid.Empty;
}
public Guid Id { get; set; }
public DateTime TransactionDate { get; set; }
public decimal TotalAmount { get; set; }
}
In the Home/Index view, I use HTML.TextBoxFor() to bind the properties of each FianancialHistory object in the list to textboxes in a table. Here is the Index view:
#model SimpleExample.Models.ReportModel
<form id="FormSave" method="post" name="FormSave" action="/Home/Refresh">
#Html.LabelFor(model => model.AccountNumber) #Html.TextBoxFor(model => model.AccountNumber)
<table class="table" style="width: 95%">
<tr>
<td >Date</td>
<td >Amount</td>
</tr>
#{
if (Model.FinancialHistories != null)
{
for (int index = 0; index <= Model.FinancialHistories.Count - 1; index++)
{
<tr>
<td>#Html.TextBoxFor(model => model.FinancialHistories [index].TransactionDate, "{0:MM/dd/yyyy}", new { #readonly = "true" })</td>
<td>#Html.TextBoxFor(model => model.FinancialHistories[index].TotalAmount, "{0:#,#.00}", new { #readonly = "true" })</td>
<td>#Html.HiddenFor(model => model.FinancialHistories[index].Id)</td>
</tr>
}
}
}
</table>
<input type="submit" id="submit" value="Refresh" class="submit" />
</form>
For this example, my action methods in the controller are very simple. Initially, the Index method populates the list with 2 FinancialHistory Objects. The Refresh method replaces the original 2 objects with 3 new objects, with different amounts.
public class HomeController : Controller
{
public ActionResult Index()
{
ReportModel reportModel = new ReportModel();
reportModel.AccountNumber = "123456789";
IList<FinancialHistory> financialHistories = new List<FinancialHistory>();
financialHistories.Add(new FinancialHistory
{
Id = Guid.NewGuid(),
TransactionDate = DateTime.Parse("3/1/2010"),
TotalAmount = 1000.00M
});
financialHistories.Add(new FinancialHistory
{
Id = Guid.NewGuid(),
TransactionDate = DateTime.Parse("4/1/2011"),
TotalAmount = 2000.00M
});
reportModel.FinancialHistories = financialHistories;
return View(reportModel);
}
public ActionResult Refresh(ReportModel reportModel)
{
FinancialHistoryRepository financialHistoryRepository = new FinancialHistoryRepository();
IList<FinancialHistory> financialHistories = new List<FinancialHistory>();
financialHistories.Add(new FinancialHistory
{
Id = Guid.Empty,
TransactionDate = DateTime.Parse("3/1/2010"),
TotalAmount = 1111.11M
});
financialHistories.Add(new FinancialHistory
{
Id = Guid.Empty,
TransactionDate = DateTime.Parse("4/1/2011"),
TotalAmount = 2222.22M
});
financialHistories.Add(new FinancialHistory
{
Id = Guid.Empty,
TransactionDate = DateTime.Parse("5/1/2012"),
TotalAmount = 3333.33M
});
reportModel.FinancialHistories = financialHistories;
return View("Index",reportModel);
}
}
That's how HTML helpers work and is by design. When rendering they are first looking in the ModelState for values and after that in the model. You are modifying the values of your model in the POST controller action, but the ModelState values still contain the old values which will be used. If you want to modify values of your model in a POST action you should remove the original values from the ModelState if you intend to redisplay the same view:
public ActionResult Refresh(ReportModel reportModel)
{
// clear the original posted values so that they don't get picked up
// by the helpers
ModelState.Clear();
FinancialHistoryRepository financialHistoryRepository = new FinancialHistoryRepository();
...
return View("Index",reportModel);
}