Razor: Transfer input text + Model to a controller - c#

Hy everybody,
In my Razor View page, I have a text input. I want to call a controller by using the "onchange" event and add my input value to a list contained in my Razor Model.
This is my html page:
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MyForms</title>
</head>
<body>
<label>Code</label>
<form method="post" action="">
<input name="NPItem" maxlength="11" autofocus onkeypress="return isNumberKey(event)" onchange="location.href = '#Url.Action("addtoList", "MyController", new { formsData = Newtonsoft.Json.JsonConvert.SerializeObject(Model) })';"/>
</form>
<table border="1" id="listeNPAI">
<tr>
<th>Index</th>
<th>Value</th>
</tr>
#{
foreach (Tuple<string, string> item in Model.list) {
<tr>
<td>#item.Item1</td>
<td>#item.Item2</td>
</tr>
}
}
</table>
</body>
</html>
This is my called controller action :
public ActionResult addtoList(string formsData) {
formsData _form = Newtonsoft.Json.JsonConvert.DeserializeObject<ModelClass>(formsData);
string input = Request["NPItem"];
if (input == null) { input = ""; } else { input = input.Trim(); }
if (input.Length == 11) {
_form.list.Add(new Tuple<string, string>(input.Substring(0, 5), input.Substring(6)));
}
return View("FormulaireNPAI", _form);
}
I add the input text value to the list in my controller action. The problem is that input is always '==null' (because there is no submit). However, it works when I press the Enter keyboard button.
Help, please
Thks in advance

You could do something like this:
$("#element").on("click", function() {
$.ajax({
url: "action",
type: "GET",
data: { data }
})
.done(function(partialViewResult) {
$("#yourViewWrapper").html(partialViewResult);
});
});
i.e. made AJAX call when you need and then refresh your view.

Related

View component refresh on button click in Razor Pages

I'm using view components to render a list of strings in HTML. The component has a button that when clicked, should toggle which list of strings is shown. When the button is clicked, only the view component should be reloaded, not the entire page.
I have the component list showing properly but I'm not sure how to get the button hooked up so that it only refreshes the view component
In ~ViewComponents I have ShowWords:
public class ShowWords : ViewComponent
{
public IViewComponentResult Invoke(bool ShowAll)
{
if (ShowAll)
{
return View(new List<string> { "showing", "all", "of", "the", "strings" });
}
else
{
return View(new List<string> { "not", "showing", "all", "strings" });
}
}
}
In ~Pages/Shared/Components/ShowWords I have Default.cshtml:
#model List<string>
<table>
<thead>
<tr>
<th>STRINGS</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Model[i]</td>
</tr>
}
</tbody>
</table>
The view component is called with this:
<vc:show-words show-all="true" />
#*How do I get this button to refresh the view component?*#
<form method="post">
<input type="submit" value="Toggle"/>
</form>
I'm able to get the button working if I bind it to a property and then post it but that reloads the entire screen not just the view component. What's the best way get just the view componenet to refresh when the button's clicked? Any help is appreciated.
Try to use ajax to call action which returns a view component.Here is a demo:
View(change input's type to button,so that the form will not be submitted when clicking the button):
<div id="component">
<vc:show-words show-all="true" />
</div>
#*How do I get this button to refresh the view component?*#
<form method="post">
<input type="button" value="Toggle" onclick="Refresh()"/>
</form>
#section scripts
{
<script>
function Refresh() {
$.ajax({
type: "GET",
url: "RefreshViewComponent",
success: function (data) {
document.getElementById("component").innerHTML = data;
}
});
}
</script>
}
action:
public IActionResult RefreshViewComponent()
{
return ViewComponent("ShowWords", new { ShowAll = false });
}
result:

Adding an entry to the table and page using ajax

I study javascript. Got to ajax requests. Everything works fine in mvc. I decided to try on web forms. Trying to post a new entry on the page, please tell me what I'm doing wrong. Here is my code. The page code acts as the main view:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Tables.aspx.cs" Inherits="WebApplication1.Tables" %>
<!DOCTYPE html>
<script src="../../Scripts/jquery-1.8.0.min.js"></script>
<script src="../../Scripts/jquery.unobtrusive-ajax.js"></script>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" >
</head>
<body>
<div>
<table id="tab" class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Author</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>1408</td>
<td>Stiven King</td>
<td>500</td>
</tr>
</tbody>
</table>
</div>
<form id="form1" runat="server">
<asp:TextBox ID="txbName" runat="server"></asp:TextBox>
<asp:TextBox ID="txbAuthor" runat="server"></asp:TextBox>
<asp:TextBox ID="txbPrice" runat="server"></asp:TextBox>
<input id="btnAdd" type="submit" value="Добавить" />
</form>
</body>
</html>
<script>
$('#btnAdd').on('click', function () {
$.ajax({
type: "POST",
url: "Tables.aspx/AddBook",
data: JSON.stringify({
"Name": $('#txbName').val(),
"Author": $('#txbAuthor').val(),
"Price": $('#txbPrice').val()
}),
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (MyDT) {
$('#tab tbody').append(MyDT);
},
error: function (xhr) {
alert(xhr.statusCode)
}
});
});
</script>
Web method code, controller:
[WebMethod]
public static string AddBook(string Name, string Author, int Price)
{
db = new Context();
string html = "";
Book book = new Book() { Name = Name, Author = Author, Price = Price };
db.Books.Add(book);
db.SaveChanges();
html = GetHTMLRow(book);
return html;
}
And another method of obtaining html code for further adding an entry to the page, something like a partial view on which the entry is going to:
public static string GetHTMLRow(Book book)
{
string htmlRow = $"<tr><td>{book.Name}</td><td>{book.Author}</td><td>{book.Price}</td></tr>";
return htmlRow;
}
My code is completely working, but for some reason the page is restarted. But shouldn't ajax request work asynchronously without touching the page? In MVC everything works fine. And then why not? What can be wrong?
when you click the button it submits the form(because the type is set to "submit"). change it to "button"
<input id="btnAdd" type="button" value="Добавить" />

Value cannot be null or empty. Parameter name linkText

I have a project that similar on site in "asp.net MVC for professional" book,
so i have a problem with navigation bar, problem in view.
the text of error is:
Value cannot be null or empty.
Имя параметра: linkText
#foreach (var link in Model)
{
#Html.RouteLink(link, new
{
controller = "Profile"
,
I understand that problem in link, but I have no idea how to fix it.
below code of controllers and view.
Menu.cshtml
#model IEnumerable<string>
#Html.ActionLink("List", "Profile")
#foreach (var link in Model)
{
#Html.RouteLink(link, new
{
controller = "Profile",
action = "List",
category = link,
page = 1
})
}
NavController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using HoboAnimal.Domain.Abstract;
namespace HoboAnimal.WebUI.Controllers
{
public class NavController : Controller
{
private IProfileRepository repository;
public NavController(IProfileRepository repo)
{
repository= repo;
}
public PartialViewResult Menu(){
IEnumerable<string> categories = repository.Profiles.
Select(x => x.Category).
Distinct().
OrderBy(x => x);
return PartialView(categories);
}
}
}
Layout
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
<link href="~/Content/Site.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div id="header">
</div>
<div id="categories">
#{Html.Action("Menu","Nav");}
</div>
<div id="content">
#RenderBody()
</div>
</body>
</html>
Thank you
Since linkText is first argument of ActionLink and RouteLink, this mean that 1 or more of yours "link" in Model is empty string. Check it before create link:
#foreach (var link in Model)
{
if(!String.IsNullOrEmpty(link.toString())
{
#Html.RouteLink(link, new
{
controller = "Profile",
action = "List",
category = link,
page = 1
})
}
}
or remove empty rows from selection:
IEnumerable<string> categories = repository.Profiles.
Select(x => x.Category).
Distinct().
Where(x => !String.IsNullOrEmpty(x)).
OrderBy(x => x);
This error:
Value cannot be null or empty. Имя параметра: linkText
only says that the linkText parameter is needed (he can't be null or empty) in the ActionLink method.
for example you could write like this:
#Html.ActionLink(" ","List", "Profile")
it should help.

How to pass data from view to controller in ASP.NET MVC? [duplicate]

I'm developing an ASP.NET MVC 5 web with C# and .NET Framework 4.5.1.
I have this form in a cshtml file:
#model MyProduct.Web.API.Models.ConnectBatchProductViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Create</title>
</head>
<body>
#if (#Model != null)
{
<h4>Producto: #Model.Product.ProductCode, Cantidad: #Model.ExternalCodesForThisProduct</h4>
using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
{
#Html.HiddenFor(model => model.Product.Id, new { #id = "productId", #Name = "productId" });
<div>
<table id ="batchTable" class="order-list">
<thead>
<tr>
<td>Cantidad</td>
<td>Lote</td>
</tr>
</thead>
<tbody>
<tr>
<td>#Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].Quantity")</td>
<td>#Html.TextBox("ConnectBatchProductViewModel.BatchProducts[0].BatchName")</td>
<td><a class="deleteRow"></a></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" style="text-align: left;">
<input type="button" id="addrow" value="Add Row" />
</td>
</tr>
</tfoot>
</table>
</div>
<p><input type="submit" value="Seleccionar" /></p>
}
}
else
{
<div>Error.</div>
}
<script src="~/Scripts/jquery-2.1.3.min.js"></script>
<script src="~/js/createBatches.js"></script> <!-- Resource jQuery -->
</body>
</html>
And this is the action method:
[HttpPost]
public ActionResult Save(FormCollection form)
{
return null;
}
And the two ViewModel:
public class BatchProductViewModel
{
public int Quantity { get; set; }
public string BatchName { get; set; }
}
public class ConnectBatchProductViewModel
{
public Models.Products Product { get; set; }
public int ExternalCodesForThisProduct { get; set; }
public IEnumerable<BatchProductViewModel> BatchProducts { get; set; }
}
But I get this in FormCollection form var:
But I want to get an IEnumerable<BatchProductViewModel> model:
public ActionResult Save(int productId, IEnumerable<BatchProductViewModel> model);
If I use the above method signature both parameters are null.
I want an IEnumerable because user is going to add more rows dynamically using jQuery.
This is jQuery script:
jQuery(document).ready(function ($) {
var counter = 0;
$("#addrow").on("click", function () {
counter = $('#batchTable tr').length - 2;
var newRow = $("<tr>");
var cols = "";
var quantity = 'ConnectBatchProductViewModel.BatchProducts[0].Quantity'.replace(/\[.{1}\]/, '[' + counter + ']');
var batchName = 'ConnectBatchProductViewModel.BatchProducts[0].BatchName'.replace(/\[.{1}\]/, '[' + counter + ']');
cols += '<td><input type="text" name="' + quantity + '"/></td>';
cols += '<td><input type="text" name="' + batchName + '"/></td>';
cols += '<td><input type="button" class="ibtnDel" value="Delete"></td>';
newRow.append(cols);
$("table.order-list").append(newRow);
counter++;
});
$("table.order-list").on("click", ".ibtnDel", function (event) {
$(this).closest("tr").remove();
counter -= 1
$('#addrow').attr('disabled', false).prop('value', "Add Row");
});
});
Any idea?
I have checked this SO answer, and this article but I don't get my code working.
You need to generate the controls for the collection in a for loop so they are correctly named with indexers (note that property BatchProducts needs to be IList<BatchProductViewModel>
#using (Html.BeginForm("Save", "ConnectBatchProduct", FormMethod.Post))
{
....
<table>
....
#for(int i = 0; i < Model.BatchProducts.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(m => m.BatchProducts[i].Quantity)</td>
<td>#Html.TextBoxFor(m => m.BatchProducts[i].BatchName)</td>
<td>
// add the following to allow for dynamically deleting items in the view
<input type="hidden" name="BatchProducts.Index" value="#i" />
<a class="deleteRow"></a>
</td>
</tr>
}
....
</table>
....
}
Then the POST method needs to be
public ActionResult Save(ConnectBatchProductViewModel model)
{
....
}
Edit
Note: Further to your edit, if you want to dynamically add and remove BatchProductViewModel items in he view, you will need to use the BeginCollectionItem helper or a html template as discussed in this answer
The template to dynamically add new items would be
<div id="NewBatchProduct" style="display:none">
<tr>
<td><input type="text" name="BatchProducts[#].Quantity" value /></td>
<td><input type="text" name="BatchProducts[#].BatchName" value /></td>
<td>
<input type="hidden" name="BatchProducts.Index" value ="%"/>
<a class="deleteRow"></a>
</td>
</tr>
</div>
Note the dummy indexers and the non-matching value for the hidden input prevents this template posting back.
Then the script to add a new BatchProducts would be
$("#addrow").click(function() {
var index = (new Date()).getTime(); // unique indexer
var clone = $('#NewBatchProduct').clone(); // clone the BatchProducts item
// Update the index of the clone
clone.html($(clone).html().replace(/\[#\]/g, '[' + index + ']'));
clone.html($(clone).html().replace(/"%"/g, '"' + index + '"'));
$("table.order-list").append(clone.html());
});
In your Post Methode you receive "MyProduct.Web.API.Models.ConnectBatchProductViewModel" as Parameter.
Use the existing model for the Post methode.
Why do you want a IEnumerable from your model? there is only one available including the id in the model.
you can visit this article for complete source code with a video tutorial.
you have to create an action first, from where we can pass the list of object
[HttpGet]
public ActionResult Index()
{
List<Contact> model = new List<Contact>();
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
model = dc.Contacts.ToList();
}
return View(model);
}
then we need to create a view for that action
#model List<UpdateMultiRecord.Contact>
#{
ViewBag.Title = "Update multiple row at once Using MVC 4 and EF ";
}
#using (#Html.BeginForm("Index","Home", FormMethod.Post))
{
<table>
<tr>
<th></th>
<th>Contact Person</th>
<th>Contact No</th>
<th>Email ID</th>
</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
<td> #Html.HiddenFor(model => model[i].ContactID)</td>
<td>#Html.EditorFor(model => model[i].ContactPerson)</td>
<td>#Html.EditorFor(model => model[i].Contactno)</td>
<td>#Html.EditorFor(model => model[i].EmailID)</td>
</tr>
}
</table>
<p><input type="submit" value="Save" /></p>
<p style="color:green; font-size:12px;">
#ViewBag.Message
</p>
}
#section Scripts{
#Scripts.Render("~/bundles/jqueryval")
}
and then we have to write code for save the list of object to the database
[HttpPost]
public ActionResult Index(List<Contact> list)
{
if (ModelState.IsValid)
{
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
foreach (var i in list)
{
var c = dc.Contacts.Where(a =>a.ContactID.Equals(i.ContactID)).FirstOrDefault();
if (c != null)
{
c.ContactPerson = i.ContactPerson;
c.Contactno = i.Contactno;
c.EmailID = i.EmailID;
}
}
dc.SaveChanges();
}
ViewBag.Message = "Successfully Updated.";
return View(list);
}
else
{
ViewBag.Message = "Failed ! Please try again.";
return View(list);
}
}
using(Html.BeginForm())
{
// code here
}
While to Post form Data all tags must be included form tag.
Following the principle of DRY, you can create one EditorTemplate for that purpose.
Steps:
1- In Views > Shared > Create new folder named (EditorTemplates)
2- Create a view inside your newly created EditorTemplates folder , the view's model should be BatchProductViewModel according to the OP example. Place your code inside the Editor view. No loop or index is required.
An EditorTemplate will act similar to a PartialView for every child entity but in a more generic way.
3- In your parent entity's view, call your Editor :
#Html.EditorFor(m => m.BatchProducts)
Not only this provides a more organized views, but also let's you re-use the same editor in other views as well.

Rendering a Partial View on button click in a div C# MVC 5

I have been following the answers on here but can't seem to get it to work. I think it's firing my function and calling my controller but it isn't rendering my partial view. Any help would be awesome.
Controller
public ActionResult Detail(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
User_Accounts user_accounts = db.User_Accounts.Find(id);
if (user_accounts == null)
{
return HttpNotFound();
}
return PartialView("_Detail", user_accounts);
}
HTML
<h2>Index</h2>
<div class="container left">
<div class="panel-default panelbox" style="position:static">
#*<p>
#Html.ActionLink("Create New", "Create")*#
#using (Html.BeginForm("Index", "Users", FormMethod.Get))
{
<p>
Type: #Html.DropDownList("userType", "All")
</p>
<p>
Last Name: #Html.TextBox("SearchString")
</p>
}
</div>
<div class="panel panel-default left">
<div class="panel-heading">
<label style="text-align:center">
User
</label>
</div>
<div class="table-responsive">
<table id="UserTable" class="table-bordered table leftPanel table-condensed">
#foreach (var item in Model)
{
<tr>
<td>
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' id="js-reload-details">#Html.DisplayFor(modelItem => item.DisplayName)</button>
#*#Html.ActionLink(item.DisplayName, "Detail", new { id = item.user_id_IN }, new { onclick = "renderPartial();" })*#
</td>
</tr>
}
</table>
</div>
</div>
</div>
<div>
<label>Details</label>
<div id="detailsDiv"></div>
</div>
Script
<script>
$('.js-reload-details').click(function (evt) {
var $detailDiv = $('#detailsDiv'),
url = $(this).data('url');
$.get(url, function (data) {
$detailsDiv.replaceWith(data);
});
});
</script>
Let me know if you need anything else.
You cant use data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' in your button to generate a url. #Html.Action() is a method which calls you controller. What would be happening is that for each item in your model you would be hitting the Detail method of UsersController (performance must of been awful if you had a lot of items :) ).
Since you appear to need only the one url (/Users/Detail) I suggest you just store the ID in data to minimize the html generated. As noted in the other answers you also need to use a class name for the button to prevent invalid html, and I also suggest using type="button" because the default (depending on the browser) may be "submit" (you don't have a form so does not matter in this case, but its good practice to get into). There is also no real need to use #Html.DisplayFor() unless your using a custom DisplayTemplate or have a [DisplayFormat] attribute on the property.
Change the html to
<button type="button" data-id="#item.user_id_IN" class="js-reload-details">#item.DisplayName</button>
and the script to
var url = '#Url.Action("Detail", "Users");
$('.js-reload-details').click(function() {
$.get(url, { id: $(this).data('id') }, function (data) {
$('#detailsDiv').html(data);
});
});
Note you do not want to use replaceWith() in your case. .replaceWith() would replace the actual div <div id="detailsDiv"></div> with the html your method returned, so the next time a user clicked on this or any other button, the method would be called, but <div id="detailsDiv"></div> no longer exists and nothing would happen.
$('#detailsDiv').html('Hello world');
renders
<div id="detailsDiv">Hello world</div>
but
$('#detailsDiv').replaceWith('Hello world');
renders
Hello world
The id of your button id="js-reload-details"
Mistake this code is repeated in a foreach loop. which will cause multiple id's of the same name on your HTML page.
Your click event is on : '.js-reload-details'. which is a class:
so make your code like this:
#foreach (var item in Model)
{
<tr>
<td>
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' class="js-reload-details">
#Html.DisplayFor(modelItem => item.DisplayName)
</button>
</td>
</tr>
}
One error I noticed in your jQuery is that you have $detailsDiv.replaceWith(data);
It should be $detailDiv according to your code: var detailDiv = $('#detailsDiv'); instead of $detailsDiv
<script>
$(document).ready(function(){
$('.js-reload-details').click(function (evt) {
evt.stopPropagation();
var detailDiv = $('#detailsDiv');
// TRY using the attr function:
var url = $(this).attr("data-url");
$.get(url, function (data) {
detailDiv.html(data);
});
});
});
</script>
UPDATE:
<script>
$(document).ready(function(){
$('.js-reload-details').click(function (evt) {
evt.stopPropagation();
var detailDiv = $('#detailsDiv');
// TRY using the attr function:
var url = $(this).attr("data-url");
$.get(url).success(function(result) {
detailDiv.html(result);
});
});
</script>
It's a good practice we use unique id's for our HTML elements. Since the following statement is going to be executed mulitple times
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' id="js-reload-details">#Html.DisplayFor(modelItem => item.DisplayName)</button>
You will have multiple buttons with the same id. Instead of doing so, you could use a class.
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' #class="js-reload-details">#Html.DisplayFor(modelItem => item.DisplayName)</button>
Then you have to correct your script:
// Now we bound the click event in all the elements that contain
// the .js-reload-details class
$('.js-reload-details').click(function (evt) {
var $detailDiv = $('#detailsDiv');
// Here was your the error
var url = $(this).attr("data-url");
$.get(url, function (data) {
$detailsDiv.replaceWith(data);
});
});

Categories

Resources