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:
Related
I am using ASP.NET MVC, I have finished my project and I copied the .sln solution files to the server.
When I run it on the server, everything ok, but there is showing exceptions for the Popup forms.
I have this controller
-- The Get method
public ActionResult Remove(int id)
{
Person Person_to_remove = new Person() { Person_Id = id };
return View(Person);
}
-- The Post method
[ActionName("Remove"), HttpPost]
public ActionResult Remove_post(int id)
{
DB.Remove(id);
return RedirectToAction("Index");
}
And these views:
Index View
<table>
<tr>
<th>Name</th>
<th>Remove?</th>
</tr>
#foreach (var row in Model)
{
<tr>
<td>#row.Name</td>
<td>
Remove
<script type="text/javascript">
function Open() {
window.open('#Url.Action("Remove", new { id = row.Id })', 'popup', 'width=450,height=250');
return false;
}
</script>
</td>
</tr>
}
</table>
Remove View (As a popup window)
#using (Html.BeginForm())
{
<main>
<div>
<h3>Confirm removing:</h3>
<h1>#Model.Name</h1>
<br />
<div class="row">
<div>
<button type="submit" onclick="updateParent();">Confirm</button>
<a onclick="window.close()">Cancel</a>
</div>
</div>
</div>
</main>
}
The Index view use a default "Layout" layout and the Remove view use a "Popup" layout on the shared folder
This is the updateParent() script running on the "Popup" layout
<script type="text/javascript">
function updateParent() {
window.opener.location.reload();
window.close();
}
</script>
On my computer the app works fine, but when running on the server the popup views won't post the form, they just close without updating the data.
But when using the same URL from the Popup window into a new tab, it works. but is not desired behaviour.
I don't know what is going on, i only changed the IP in the connection string.
The problem was that the form was closing before submitting. Thanks to derloopkat for the tip.
To solve this I change Razor #Html.BeginForm() to name the form "PopUp".
Html.BeginForm(null, null, FormMethod.Post, new { name = "PopUp", id = "PopUp" })
then modify the updateParent method, to submit the form.
<script type="text/javascript">
function updateParent() {
document.forms["PopUp"].submit(); <-- Added
window.opener.location.reload();
window.close();
}
</script>
Don't know if it's the best way, but it works.
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);
});
});
I have a very huge form in my application with a lot of different inputs and a lot of lists in my model. So i will try to add/delete the lists without sending the complete model to the server.
I tried several ways now but i don´t find a clean way. You can imagine my model like:
public class EditSomething
{
public string name { get; set;}
public List<something> somethingList { get; set;}
// a lot other fields...
public EditSomething(EditSomethingFromDatabase editSomethingFromDatabase)
{
name = editSomethingFromDatabase.Name;
somethingList = new List<SomethingModel>();
foreach(var something in editSomethingFromDatabase.Something)
{
somethingList.Add(new SomethingModel(editSomethingFromDatabase.Something));
}
}
}
The other model looks similar but without lists.
In the view i have a table for the model:
<h2>Something</h2>
<div id="SomethingDiv">
<table id="SomethingTable">
<thead>
<tr>
<th>#Html.Label("SomethingName")</th>
<th>#Html.Label("SomethingID")</th>
<th></th>
</tr>
</thead>
<tbody id="SomethingTableBody">
#Html.EditorFor(x => x.somethingList)
</tbody>
</table>
<p>
<input type="button" name="addSomething" value="Add Something" id="AddSomething">
</p>
</div>
the jquery of the addSomething is:
$('#AddSomething').click(function () {
$.ajax({
url: '#Url.Action("AddSomething", "SomethingModels")',
data: { tableSize: $('#SomethingTable tr').length },
cache: false,
success: function (html) { $('#SomethingTable tr:last').after(html); }
});
The controller method AddSomething is:
public ActionResult AddSomething (int tableSize)
{
SomethingModel something= new SomethingModel(null, (-2) * (tableSize + 1));
return PartialView(""~/Views/EditorTemplates/EditSomethingModel.cshtml"", something);
}
And at least i have a editor template in EditorTemplates as for editorfor and partialview. This have the important informations i want to send to the server:
#model SomethingModel
<tr>#TextBoxFor(m=>m.SomethingName)<td>
#TextBoxFor(m=>m.SomethingID)
So the problem now is, that the submit of the first view only post the SomethingModel to the server who already existed while opening the view but the new SomethingModel from the AddMutation method aren´t in the post. Someone an idea to fix this?
Edit: Changed the path to the editor template so i only need one view for the EditorFor and PartialView.
Edit2: To solve the main problem i created a view as following and use it as partial view. Now the data is send to the server correctlly. Only the validation on client side is still not working:
#model SomethingModel
<tr>#TextBoxFor(m=>m.SomethingName, new{Name="somethingList["+ViewBag.ListId+"].SomethingName")<span class="field-validation-valid" data-valmsg-for="somethingList[#ViewBag.ListId].SomethingName" data-valmsg-replace="true"></span><td>
<tr>#TextBoxFor(m=>m.SomethingID, new{Name="somethingList["+ViewBag.ListId+"].SomethingID")<span class="field-validation-valid" data-valmsg-for="somethingList[#ViewBag.ListId].SomethingID" data-valmsg-replace="true"></span><td>
</tr>
In the AddSomething method i added the ViewBag.ListId with the id of the next element in the list.
It seems a reasonable enough approach, but You've not shown your EditorTemplate, so I'm going to assume its something like:
#model List<something>
#for(int i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.DisplayFor(m => m[i].Id) #Html.HiddenFor(m => m[i].Id)</td>
<td>#Html.EditorFor(m => m[i].Name)</td>
</tr>
}
Your ajax method should return the HTML of a row - and this is important... the form fields need to be named 1 above the last one in the table.
So when you view the rendered source of your table (before adding any new fields it might look like:
...
<tbody>
<tr>
<td>1 <input type="hidden" name="something[0].Id" value="1"/></td>
<td><input type="text" name="something[0].Name" value="somename" /></td>
</tr>
</tbody>
You need to ensure the html returned by the ajax method for your new row is:
<tr>
<td>2 <input type="hidden" name="something[1].Id" value="2"/></td>
<td><input type="text" name="something[1].Name" value="somenewname" /></td>
</tr>
ie. the number inside the brackets is the next index for the items in something. If there is a gap in the indexes (or they overlap) then the new items will not get parsed.
EDIT - to get client side validation to work for the new fields alter your jquery ajax success callback as follows:
$('#AddSomething').click(function () {
$.ajax({
url: '#Url.Action("AddSomething", "SomethingModels")',
data: { tableSize: $('#SomethingTable tr').length },
cache: false,
success: function (html) {
$('#SomethingTable tr:last').after(html);
$.validator.unobtrusive.parse('#SomethingTable');
}
});
I'm building an MVC app and right now my view generates a pack of items. The user needs to check a checkbox if he wants to send the data.
Here's my view and how it is builded:
<script type="text/javascript">
$(document).ready(function() {
//alert("The document is ready");
$("#selectAll").click(function() {
//alert("The case has been clicked");
var chkValue = $(this).is(":checked");
$(".divChckBox").prop("checked", chkValue);
});
});
</script>
<p>
#using (Html.BeginForm("SendObj", "Manager"))
{
<p>
Select / UnSelet All Items #Html.CheckBox("selectAll", true)
</p>
<table>
<tr>
<th>Card Name</th>
<th>Number In Stock</th>
(...)
</tr>
#for (int i = 0; i < Model.Count(); i++)
{
<tr>
<td>#Html.DisplayFor(x => x[i].m_OthObj.m_ObjName)</td>
<td>#Html.DisplayFor(x => x[i].m_NbInStock)#Html.HiddenFor(x => x[i].m_NbInStock)</td>
(...)
<td>
<input type="checkbox" name="itdoesnotmatter" class="divChckBox" checked="true"/>
</td>
</tr>
}
</table>
<input type="submit" value="Send"/>
}
</p>
So you understand why I cannot use "CheckboxFor". Now what I want to do is send only the items which checkbox status is "checked". I know how to do this via model binding (checkboxfor), but I'm clueless as to how to build this.
I need to return a list of items. So how could I do this? Thank you very much!
Your form will return the values based on name, so shoot whoever told you such a stupid name :)
Use
<input type="checkbox" name="InStock" class="divChckBox" checked="true" value="#Model[i].ID" />
Or something more representative. Note that it is CRITICAL that you supply a unique identifier as the value of your checkbox. The value is how you will identify what was checked!
In your controller, there's several ways you can capture it. I do it like this:
public ActionResult Create(List<int> InStock)
{
foreach(var inStockItem in InStock)
{
//do what you need to do
}
}
The important points:
List<int> InStock
This must match the NAME attribute on your checkbox. The actual values will be the Value of your checkboxes.
Here I just randomly selected Create for your Action, but you need to make it match whatever action you are in (Edit, Index, etc..)
Good Luck!
try using the attr method to change the property checked.
$(document).ready(function() {
$("#selectAll").click(function() {
var chkValue = $(this).is(":checked");
$(".divChckBox").attr("checked", chkValue);
});
});
View code:
<!-- note "x[i].m_id"; Use the entity's id property is here
...maybe this should be m_NbInStock? -->
<input type="checkbox" name="selectedItems" value="#x[i].m_id" class="divChckBox" checked="true"/>
Controller code:
public class Manager : Controller
{
/* ... */
[HttpPost]
public ActionResult SendObj(IList<Int32> selectedItems)
{
// Grab those items by their IDs found within `selectedItems` and perform
// any processing necessary
// ...
//return View();
}
/* ... */
}
I'm stuck trying to wrap my head around how to make a page like this work. My model contains a list of objects which have text fields for different languages. There's a spot in the view that needs to be dynamically changed to display the text fields associated to the language selected in a drop-down list. The text fields also need to be editable such that when the parent form is submitted, the text fields need to save correctly in the parent model after being edited in the partial view/model. I was hinted in another page that partial views is what I should try to do, maybe something about programmatically generating partial views for each language in the list, and doing something to update/render the correct partial view based on the selection in the drop down list...?
My initial attempt led me to try using ajax calls to the controller to return the correct partial view based on the value of the drop down menu, but I can't bind it to the model, and hence can't update/save it when the main view form is submitted. Here's what I've been working with so far:
Model:
public class EditModel
{
public List<TextField> TextFieldList;
public List<string> DisplayList
{
get
{
List<string> tempList = new List<string>();
foreach (TextField a in TextFieldList)
{
tempList.Add(a.Language.ToString() + "/" + a.Currency.ToString());
}
return tempList;
}
}
}
Controller:
public ActionResult EditTextFields(int adId, int index)
{
Ad ad = this.Repository.GetById(adId);
return PartialView("EditTextFields", ad.TextFieldsList[index]);
}
Main View:
#model Models.EditModel
<script type="text/javascript">
$(function () {
$(href('DisplayList').change(function () {
var value = $(this).find(':selected').val();
var adId = GetId();
$.get('/Controllers/EditTextFields', "{\"adId\": " + String(adId) + ", \"index\":\"" + value + "\"}", function (data) {
$('#TextFieldDiv').html(data);
});
}));
});
</script>
#using (Html.BeginForm("Edit", "Widget", FormMethod.Post, new { id = "WidgetEditForm", enctype = "multipart/form-data" }))
{
<div>
#Html.DisplayFor(m => m.DisplayList)
</div>
<div id="TextFieldDiv">
#{Html.RenderPartial("EditTextFields", (TextField)#Model.TextFieldList[0]);}
</div>
}
Partial View:
#model Objects.TextField
#using (Html.BeginForm())
{
<table>
<tr>
<td class="fieldName" style="vertical-align: top;">
Headline Text:
</td>
<td>
#Html.EditorFor(m => m.Headline)
#Html.ValidationMessageFor(m => m.Headline)
</td>
</tr>
<tr>
<td class="fieldName">
Sub-Headline Text:
</td>
<td>
#Html.EditorFor(m => m.SubHeadline)
#Html.ValidationMessageFor(m => m.SubHeadline)
</td>
</tr>
</table>
}
Ideas to point me in a good direction?