In my web app I have a grid list. I select a row and then click the edit button to show a partial update view (which I use to add new data too) in a popup window. The view shows, but I don't have any values in the textboxes. I use devextreme components, but I think, my issue has nothing to do with it (maybe I'm wrong).
This is the onClick code:
function editrow_onClick() {
var key = $("#grid").dxDataGrid("instance").getKeyByRowIndex(selectedRowIndex);
$.ajax({
url: '/MasterData/Sender/UpdateSender/'+key,
}).done(function (response) {
var popup = $("#sender-popup").dxPopup("instance");
popup.option("contentTemplate", function (content) {
content.append(response);
});
popup.show();
});
}
If I click the edit button, I get the right url like /MasterData/Sender/UpdateSender/3.
The corresponding controller action looks like this:
[Route("{id}")]
public IActionResult UpdateSender(long SenderId)
{
return PartialView("NewSender", SenderRepository.GetSender(SenderId));
}
On top of the controller class I have the corresponging attribute: [Route("MasterData/[controller]/[action]")]
I testet id, the action is reached, but the SenderId is 0. I would expect f.e. 3. This should be causing the empty view, I think. Why is SenderId 0 (the default value)?
I post the update view too, maybe this is the source of the problem (don't bother the AddSender action, I plan to change it conditionally, if I get the update data working):
#model Sender
<form asp-action="AddSender" asp-controller="Sender" method="post">
#using(Html.DevExtreme().ValidationGroup()) {
#(Html.DevExtreme().Form<Sender>()
.ID("form")
.ColCount(1)
.Items(items => {
items.AddSimpleFor(m => Model.Name);
items.AddSimpleFor(m => Model.Address);
items.AddSimpleFor(m => Model.ContactPerson);
items.AddSimpleFor(m => Model.ContactEmail);
items.AddGroup().Items(groupItem => groupItem.AddSimple().Template(
#<text>
<div style="text-align: right">
#(Html.DevExtreme().Button().ID("save").Text("Mentés").Width(100).Type(ButtonType.Success).UseSubmitBehavior(true))
#(Html.DevExtreme().Button().ID("cancel").Text("Mégsem").Width(100).Type(ButtonType.Normal).OnClick("close_onClick"))
</div>
</text>));
})
.LabelLocation(FormLabelLocation.Top)
.FormData(Model)
)
}
</form>
<script>
function close_onClick() {
$("#sender-popup").dxPopup("hide");
}
</script>
[Route("{SenderId}")] public IActionResult UpdateSender(long SenderId) { return PartialView("NewSender", SenderRepository.GetSender(SenderId)); }
Try replacing id with SenderId.
Then action method will hit with the desired value.
Related
I'm playing around in an MVC application and am trying to figure out something that seems pretty straight forward.
I have a Index.cshtml file in my Views/Home/ folder that is pretty simple (below)
Index view
...
<div>
Search
#Html.DropDownList("selection", MyProject.Util.Lists.GetMyList(), "Select One")
#Html.ActionLink("Search", "Index", "Search", new { st = xxx }, null)
</div>
...
I also have a Search controller that needs to take a "st" value and looks like this
public class SearchController : Controller
{
// GET: Search
public ActionResult Index(string st)
{
ApplicationDbContext db = new ApplicationDbContext();
List<Report> filteredReports = db.Reports.Where(r => r.Tag == st).ToList();
return View(filteredReports);
}
}
What I'm not sure of how do is grab what ever value is selected from the drop down and add that to my Html.ActionLink where I just have 'xxx' now.
Clicking on the link usuallly does a new GET request to the href attribute value of the link. It will not send any data from your form.
You need to use javascript and hijack the click event on the link and append the selected option value from the SELECT element as querystring to that and send it.
So give an id to the link which you can use later to wireup the click event
#Html.ActionLink("Search", "Index", "Search", null, new {#id="search"})
And the javascript
$(function(){
$("#search").click(function(e){
e.preventDefault();
window.location.href=$(this).attr("href")+"?st="+$("#selection").val();
});
});
Another option is to use the form submit. Wrap your select element inside a form and have a submt button which sends the form to the action method. This method does not need javascript. But your select input element name should match with your parameter name.
#using(Html.BeginForm("Index","Search",FormMethod.Get))
{
<div>
Search
#Html.DropDownList("st", MyProject.Util.Lists.GetMyList(), "Select One")
<input type="submit" value="Search" />
</div>
}
If you do not prefer to have button, but need the link element, you can use javascript to submit the form (like we did in the first approach). With this approach you do not need to manually append the querystring.
You have to call a AJAX POST call to your controller which store selected value, and then when your action link event fire get stored value from there.
AJAX:
$.ajax(
{
url:your url,
data:{"st":dropdown selected value},
type:"post"
});
Controller:
public ActionResult Index()
{
string st=TempData["st"].ToString();
ApplicationDbContext db = new ApplicationDbContext();
List<Report> filteredReports = db.Reports.Where(r => r.Tag == st).ToList();
return View(filteredReports);
}
public void SetSt(string st)
{
TempData["st"] = st;
}
I have an MVC view, which is launched by a function in the controller. That view has a button that I want to use to submit data to a different function in that same controller, but it always go back to the function that launched it instead.
The controller is called, the ViewForPrepare view is launched from PrepareList, I hit the button on ViewForPrepare, and it submits to PrepareList again instead of RunList.
In the controller I have:
public ActionResult PrepareList(int Key)
{
return "ViewForPrepare";
}
public ActionResult RunList(int Key)
{
return "OtherView";
}
Then in the View:
<input type="button" value="Submit Report" id="submit">
<script type="text/javascript">
$(document).ready(function () {
$('#submit').click(function () { window.location ='#Url.RouteUrl("RunList", new { Key = #Model.caseNumber })' });
});
</script>
So I press the button to go to RunList, but it keeps going to PrepareList. I've checked the routing and it looks OK. What do I need to do to get the button to submit to RunList?
You're code right now basically says "When I click the submit button. Change the window's location to something else." If that is what you want, try using
#Url.Action("RunList", new { Key = Model.caseNumber })
instead of
#Url.RouteUrl("RunList", new { Key = #Model.caseNumber })
and try using a <button> element instead of an <input> element.
If what you want is to post the data from the form, you should wrap your button in a form tag (make sure to replace "ControllerName" below with your actual controller.)
#Html.BeginForm("RunList","ControllerName", new { Key = Model.caseNumber })
{
<input type="submit" value="Submit Report" id="submit">
}
and get rid of the javascript altogether as it isn't necessary in this case. Also you will have to mark your RunList action as HttpPost for this to work.
[HttpPost]
public ActionResult RunList(int Key)
{
return "OtherView";
}
Why dont you just use a RouteLink instead of the Input?
#Html.RouteLink("Submit Report", "RunList", new { Key = Model.caseNumber }, new {#class="btn" })
not sure if you're using bootstrap or jquery ui but there are css classes to make links look like buttons.
ActionLink works the same way.
#Html.ActionLink("Submit Report", "RunList", "ViewForPrepare ", new { Key = Model.caseNumber }, new { #class = "btn" })
Using VS2015 Pro I created a project using the MVC template.
HomeController.cs added:
public ActionResult PrepareList(int Key)
{
return View();
}
public ActionResult RunList(int Key)
{
return View(); ;
}
Index.cshtml added:
#Html.ActionLink("Submit Report", "RunList", new { Key = 4 }, new { #class = "btn" })
Put a break point in "RunList" and it worked!
Using
<input type="button" value="Submit Report" id="submit">
#section script{
<script type="text/javascript">
$(document).ready(function () {
$('#submit').click(function () { window.location ='#Url.RouteUrl("RunList", new { Key = 4 })' });
});
</script>
}
I got the meessage
A route named 'RunList' could not be found in the route collection.
when you need to send data, you must add the attribute HttpPost to this action:
[HttpPost]
public ActionResult RunList(int Key)
{
return "OtherView";
}
I'm using BeginCollectionItem with MVC 5 for adding and removing rows whenever.
One issue I'm having is with the delete function, I followed an online tutorial
which specified using #divId:first which seems to indicate deleting the first row whenever. This is no good for me, and wouldn't make sense to an end user.
As I'm using BCI I want to delete these from the html DOM so they won't have database Ids.
How do I delete by the Id of the model, this apparently (I think I read somewhere) is automatically generated by BCI?
Delete Function in the main view
$('#deleterow').live('click', function () {
$(this).parents('#newRow:first').remove();
return false;
});
Partial View with rows I want to delete by Id
#model Mvc.Models.Project
#using (Html.BeginCollectionItem("something"))
{
<div id="newRow">
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
Delete
</div>
}
Update 2
When viewing the rendered html the data-action attribute renders as 0 for all objects so the JQuery can't and won't delete a row/object from the view.
Update
Instead of the check box I want to use the Delete link button, I assume this is possible? Not very familiar with jQuery but it is something I intend to look at, fairly new to MVC too but this is what I have so far:
Main View
<h3>Students</h3>
<div id="newStudent">
#foreach(var Student in Model.students)
{
Html.RenderPartial("_Student");
}
</div>
<input type="button" id="addStudent" name="addStudent" value="Add Student"/>
<input type="submit" value="Submit"/>
#section Scripts
{
<script type="text/javascript">
$('#addStudent').on('click', function () {
$.ajax({
async: false,
url: 'School/AddNewStudent'
}).success(function (partialView) {
$('#newStudent').append(partialView);
});
});
$('#newStudent').on('click', '.deleteStudent', function () {
var id = $(this).data('id');
if (id === 0) { // assumes Id is integer
$(this).closest('.studentRow').remove();
}
else { // existing item - controller to delete from Db
var url = '#Url.Action("action", "controller")';
$.post(url, { ID: id }, function (response) {
if (response) {
$(this).closest('.studentRow').remove();
}
}).fail(function (response) {
// display error message
});
}
});
</script>
}
Partial View
#using (Html.BeginCollectionItem("students"))
{
<div id="studentRow">
#Html.HiddenFor(m => m.Id)
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
Delete
</div>
}
Controller
public class SchoolController : Controller
{
// GET: School
public ActionResult Index()
{
var newSchool = new School();
return View(newSchool);
}
public ActionResult AddNewStudent()
{
var student = new Student();
return PartialView("_Student", student);
}
[HttpPost, ActionName("DeleteStudent")]
public ActionResult DeleteStudent(School school)
{
foreach(var student in school.students.Where(s => !s.isDeleted))
{
return View(school.students);
}
return View();
}
}
What I have done is created a IsDeleted Property in Model/ViewModel, Put it in the Row as a Hidden Field, And also have a delete button against each Row
using (Html.BeginCollectionItem("Contacts"))
{
<div class="row mt-10">
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.isDeleted, new { data_is_deleted = "false" })
.......Removed HTML
<div class="col-md-1">
<span class="glyphicon glyphicon-trash" data-action="removeItem" title="remove" style="cursor:pointer"></span>
</div>
Then add this jQuery a JavaScript file. (Note: Don't add this to the Row Partial View, I add it in the View that calls the Row Partial View)
You might have to edit this jQuery to match your HTML structure, The goal in this jQuery is to update the IsDeleted field to either true or false and then Disable the other Input fields
$(document).on('click', '*[data-action="removeItem"]', function(e){
e.stopPropagation();
var btn = $(this);
var row = btn.closest('.row');
var parent = btn.parent();
var checkBox = parent.siblings('*[data-is-deleted]');
var checkBoxVal = checkBox.val();
if(checkBoxVal == 'False' || checkBox.val() == 'false'){
checkBox.val('true');
row.find('input, textarea, select').attr('readonly', 'readonly');
} else {
checkBox.val('false');
row.find('input, textarea, select').attr("readonly", false);
}
checkBoxVal = checkBox.val();
});
This is what your view will look like:
When post Back to Controller:
foreach (var contact in contacts.Where(s => !s.isDeleted))
{
// New and Updated Items
}
foreach (var contact in myModel.Where(s => s.isDeleted && s.Id!= 0))
{
// Deleted Items
// You don't have to delete Items where Id == 0, Bcz they are not in the DB.
// Just some Item added to the View and then deleted without Save
}
Deleted Items will be disabled: Note: You can Hide them by editing the above jQuery
EDIT A:
Actual controller code is something like this:
[HttpPost]
public ActionResult SaveStudent(Student model){
// Save model items
// Then Save the List of Items like this:
foreach (var contact in model.myListItems.Where(s => !s.isDeleted))
{
// New and Updated Items
}
foreach (var contact in model.myListItems.Where(s => s.isDeleted && s.Id!= 0))
{
// Deleted Items
// You don't have to delete Items where Id == 0, Bcz they are not in the DB.
// Just some Item added to the View and then deleted without Save
}
}
Firstly .live() was depreciated in jquery-1.7 and removed in 1.9. Use .on() instead. Next your generating invalid html by generating duplicate id attributes for the 'delete' link, which also means you will only ever be able to delete the first item and you never be able to delete newly added items because you are not using event delegation. Note also the BeginCollectionItem does not _ automatically generate the models ID_. All it does is add a prefix to the property name which includes an indexer value based on a guid so that the items can be bound to a collection on post back.
The link in your partial partial needs a class name and should store the Id value so it can be easily accessed in the script.
#using (Html.BeginCollectionItem("students"))
{
<div id="studentRow">
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.isDeleted) // not sure what the point of the data- attribute is
#Html.LabelFor(m => m.Name)
#Html.EditorFor(m => m.Name)
Delete
</div>
}
Then your script needs to be (note the id="newStudent" for the enclosing <div> is confusing since you foreach loop is generating the html for existing items)
$('#newStudent').on('click', '.deleteStudent', function() { // use event delegation
var id = $(this).data('id');
if (id == 0) { // assumes property Id is typeof int
// Its a new item so just remove from the DOM
$(this).closest('.studentRow').remove();
} else {
// Its an existing item so call controller to delete it from the database
var url = '#Url.Action(""DeleteStudent", "School")';
$.post(url, { ID: id }, function(response) {
if(response) {
// The student was successfully deleted
$(this).closest('.studentRow').remove();
}
}).fail(function (response) {
// Oops, something went wrong - display error message?
});
}
});
And the controller
[HttpPost]
public JsonResult DeleteStudent(int ID)
{
// delete the student from the database based on the ID and signal success
return Json(true);
}
I have an MVC 4 view that contains a partial view. The partial view is included in the main view as follows:
<div id="PartialView">
#Html.Partial("_PhotoList", #Model)
</div>
My partial view looks as follows:
#model ExchangeSite.Entities.EstateSaleSellerListing
<div style="width: 1300px; height: 300px; overflow: auto">
#foreach (var photo in #Model.ImageList)
{
<a href="javascript:DeletePhoto(#photo.ImageId);">
<img src="#Url.Action("GetPhoto", new { id = photo.ImageId })" alt="" title="Click on the image to remove it" width="250" height="190"/>
</a>
}
</div>
<script>
function DeletePhoto(imageId) {
var Url = "/EstateSaleSellerListing/DeletePhoto";
$.get(Url, { imageId: imageId }, function (data) {
$("#PartialView").html(data);
});
}
</script>
As you can see, when a user clicks on an image, the DeletePhoto() method is called. It makes a call to an action method named DeletePhoto on the named controller. The action method deletes the photo, generates a new list of photos and updates the partial view. Everything works except the partial view is never updated.
My controller code is as follows:
public ActionResult DeletePhoto(int imageId)
{
var photo = this._systemLogic.GetItem<Photo>(row => row.ImageId == imageId);
this._systemLogic.DeleteItem(photo);
EstateSaleSellerListing listing = new EstateSaleSellerListing();
GetPhotoList(listing);
return PartialView(listing);
}
The EstateSaleSellerListing entity has a list of photo objects that get displayed in the partial view.
I don't have enough experience to know why my partial view isn't updating when the action method returns.
Try to move your javascript to your main page and change
return PartialView(listing);
to
return PartialView("_PhotoList", listing);
Check your cache settings in jQuery (it looks like you're using jQuery by the syntax anyway). And I think your second parameter is a bit off (not assigning it to data)... Try this
<script>
function DeletePhoto(imageId) {
var Url = "/EstateSaleSellerListing/DeletePhoto";
$.get(Url, { cache: false, data: { imageId: imageId }}, function (data) {
$("#PartialView").html(data);
});
}
</script>
You could also have the controller method render the partial view to string if it is being loaded in a partial.
Controller...
model.PartialViewContent=PartialToString("Partials/SmallerPart",model);
return PartialView("Partials/LargerPart",model);
View
$("#PartialView").html(data.PartialViewContent);
Fyi, PartialToString is not built into mvc. I had to hack that together
Is there a way to submit a partial view form in asp.net mvc without reloading the parent page, but reloading the partial view only to its new state? Similar to how knockout.js updates using data-bind.
My data table renders with a variable number of columns/names so I don't think knockout.js is an option for this one, so I am trying to use a partial view instead.
Not without jQuery.
What you would have to do is put your Partial in a div, something like:
<div id="partial">
#Html.Partial("YourPartial")
</div>
Then, to update (for example clicking a button with the id button), you could do:
$("#button").click(function () {
$.ajax({
url: "YourController/GetData",
type: "get",
data: $("form").serialize(), //if you need to post Model data, use this
success: function (result) {
$("#partial").html(result);
}
});
})
Then your action would look something like:
public ActionResult GetData(YourModel model) //that's if you need the model
{
//do whatever
return View(model);
}
Actually, if your Partial has a child action method, you can post (or even use an anchor link) directly to the child action and get an Ajax-like affect. We do this in several Views.
The syntax is
#Html.Action("MyPartial")
The Child Action is
public ActionResult MyPartial()
{
return PartialView(Model);
}
If your form posts to the child action
#using (Html.BeginForm("MyPartial"))
{
...
}
The Partial View will be updated with the partial view returned from the child action.
Jquery is still a legitimate way to update a partial. But technically, the answer to your question is YES.
As normal what I find when looking for things like this is people give too limited information so I will attempt to help here. The key is to set up a div with an ID you can append the return html to. Also when hitting your controller make sure it returns the partial. There are some potential problems with this method but on a good day it should work.
<div id="CategoryList" class="widget">
#{
Html.RenderPartial("WidgetCategories.cshtml");
}
</div>
function DeleteCategory(CategoryID) {
$.get('/Dashboard/DeleteWidgetCategory?CategoryID=' + CategoryID,
function (data) {
if (data == "No") {
alert('The Category has report widgets assigned to it and cannot be deleted.');
}
else {
$('#CategoryList').html(data);
}
}
);
}
[HttpGet("DeleteWidgetCategory")]
[HttpPost("DeleteWidgetCategory")]
public IActionResult DeleteWidgetCategory(string CategoryID)
{
string Deleted = CategoryModel.DeleteCategory(CategoryID);
if (Deleted == "Yes")
{
return PartialView("WidgetCategories");
}
else
{
return this.Json("No");
}
}
I would use the Ajax Form helper for such scenarios using a partial view and #html.RenderPartial("partialName")
partial helpers
In your Main View
<div id=SearchResult>
#Html.Partial("_NameOfPartialView", Model)
</div>
<input type="button" id="btnSubmit" value="Submit">
In your Javascript file
$('#btnSubmit').click(function () {
GetData(Id);
});
function GetData(Id){
$.ajax({
url: "/Home/GetEmployee/",
type: "get",
data: { Id:Id },
success: function (result) {
$('#SearchResult').html(result);
}
});
}
In your Home Controller
public ActionResult GetEmployee(int Id)
{
var employee= context.Employee.Where(x=> x.EmployeeId == Id)
return this.PartialView("_NameOfPartialView", employee);
}