I have an ASP.NET MVC view with a model binding of a List
Inside my view I have a partial view that should handle a result of a different action and should be updated after the Action finished.
But the partial view is always crash because it tries to consume the model of the main view.
#model List<planemOsIdConsumer.Models.CommonDtosRole>
#{
ViewBag.Title = "Rollen und Rechte";
}
<table>
<thead>
<tr>
<th>Your Column Header Name</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
<label>#Html.Display(item.Name)</label>
</td>
</tr>
}
</tbody>
</table>
#using (Ajax.BeginForm("Create", "Role", new AjaxOptions
{
InsertionMode = InsertionMode.Replace, //target element(#mydiv) will be replaced
UpdateTargetId = "mydiv"
}))
{
<table cellpadding="5">
<tr>
<td>Rollenname:</td>
<td><input type="text" name="Name" id="roleNameVal" placeholder="Neue Rolle" /></td>
</tr>
</table>
<br />
<label id="resultLabel"></label>
<input type="submit" value="Submit" id="btn_click" />
<div id="mydiv">
#{
Html.RenderPartial("_CreateResult");
}
</div>
}
The Partial View
#model planemosIdUi.Dto.Result
#{
ViewBag.Title = "_CreateResult";
}
#{
if (Model?.Success == true)
{
<label>Erstellt</label>
}
else if(Model?.Success == false)
{
<label>Fehler</label>
}
else
{
<label>Do something</label>
}
}
The Partial view should ignore the model from the main view if this is possible.
You want to remove the Model class, thats mentioned at the top. Then only it won't ask for the model and Don't throw an error.
But based on your code, it should be there.
Pass the nullable model in your main CSHTML page as like below
Html.RenderPartial("_CreateResult", new planemosIdUi.Dto.Result());
If you are trying to display the partial view after some action, you can use ajax for appending the partial view content to mydiv id .otherwise every time when you load the page, partial view will render.
$.ajax({
cache: false,
async: false,
type: "POST",
contentType: "application/json",
url: "/ControllerName/ActionName",
success: function (result) {
$("#mydiv").html(result);
}
});
From the controller you can return the partial view as
public ActionResult ActionName()
{
planemosIdUi.Dto.Result model=new planemosIdUi.Dto.Result();// model object to partial view
return PartialView("_CreateResult",model);
}
Your _CreateResult.cshtml requires model of type Result.cs, and you must pass the model inside Html.RenderPartial method which is the second parameter of it.
If you don't want to pass the model to partial view, you could do two things
Pass the null value and have check for null value inside partial view
Html.RenderPartial("_CreateResult", null);
You could create the empty instance of model and pass it to partial view
Html.RenderPartial("_CreateResult", new planemOsIdConsumer.Models.CommonDtosRole());
Related
I send from controller to view a list of objects, viewmodel is the object with some properties and pagedList, that need to be presented on page. And by pressing the button, this list need to be exported as file, that is, it need to go back to the controller and be processed there.
Model:
public class ProductsList : ListViewModel<Product>
{
public ProductsList(string prefix) : base(prefix){ }
public ProductsList(PagedList<Product> products)
{
List = products;
}
public int? ProductTypeFilter {get;set; }
public string ProductTypeFilterName {get; set;}
public string FilterBy { get; set; }
}
ListViewModel just contain PagedList.
My controller
[HttpPost]
public FileResult SaveAsFile(PagedList<Product> viewmodel)
{
...
}
And my view
#model MyProject.ViewModels.ProductsList
if (Model.List.Count > 0)
{
<table id="products_table">
<colgroup>
<col class="productType"/>
</colgroup>
<thead>
<tr>
<th >
Product type
</th>
</tr>
</thead>
<tbody>
#{ var i = 0; }
#foreach (var item in Model.List)
{
<tr>
<td onclick="window.location='#Url.Action("Details", new {id = item.Id})'">
<p>
#item.Type
</p>
</td>
}
</tr>
i++;
}
</tbody>
</table>
}
<form asp-action="SaveAsFile" enctype="multipart/form-data" method="post">
#Html.HiddenFor(m => list);
<input type="submit" value="Save as File"/>
</form>
I already have tried add to controller params tags [FromForm], [FromBody] (actually all available tags).
In view tried with hidden field in form, without it just with submit; put form on partial view; other forms: ajax, Html.ActionLink("Save as File", "SaveAsFile", new {Model}).
On debug mod Model.List has 21 items (but it can has more, like 2000 items), but when I press the button, viewmodel is creating newly.
Problem: viewmodel is creating newly and i cannot get back my full viewmodel to controller
I will be grateful for any help :)
You can set your ViewModel data in a Session variable when you send the data to your View from Controller method:
In order to setup your Session, you can follow this S.O answer
Once your Session is setup, then you can put your ViewModel in it like:
HttpContext.Session.SetObjectAsJson("ProductsList", productslist);
And then retrieve it in your POST method like this:
[HttpPost]
public FileResult SaveAsFile(PagedList<Product> viewmodel)
{
//Get your viewmodel here
var list = HttpContext.Session.GetObjectFromJson<ProductsList>("ProductsList");
}
You can also serialize your ViewModel and then send it your Controller method without using form:
Create an ActionLink:
#Html.ActionLink("Submit", "SaveAsFile", "Home", new { jsonModel= Json.Encode(Model.list) }, null)
And your Controller method:
public FileResult SaveAsFile(string jsonModel)
{
var serializer= new DataContractJsonSerializer(typeof(Model.Product));
var yourmodel= (Product)serializer.ReadObject(GenerateStreamFromString(jsonModel));
}
I am trying to create a view in my application that performs basic CRUD commands in ASP.NET Core to teach myself some new skills. I am however stuck and would appreciate some assistance please.
I would like to have each "component" of the application sitting in a partial view for maintenance going forward. I initially had my Index view use a declaration of type IEnumerable (for the for each loop):
#model IEnumerable<Project.Web.Models.Sample.SampleModel>
Which worked perfect for returning the list and rendering the page but then when trying to have my Modal window partially loaded into the page and insert data using the "CreateSample" function on the controller it was not picking up the function and failed the insert (no form action found). If I then try to add:
#model Project.Web.Models.Sample.SampleModel
to the CreateModal view page it throws an error and wont even let me render the page, I presume because its being partial loaded the app is seen as having two SampleModel declarations. If I create this page completely separate and not partially loaded with the normal #model declaration it works.
I have the basic setup going so far and have included my code for each below.
Model - SampleModel
public class SampleModel
{
public int Id { get; set; }
public string SampleText { get; set; }
}
Controller - SampleController
public class SampleController : Controller
{
public const string ControllerName = "Sample";
//Open Database Connection
private _DBContext DBDatabase = new _DBContext ();
public ActionResult Index()
{
var Model = DBDatabase.Sample.Select(s => new SampleModel
{
Id = s.Id,
SampleText = s.SampleText
}).ToList();
return PartialView(Model);
}
[ActionName("_CreateModal")]
public ActionResult InsertNewRecord()
{
var Model = DBDatabase.Sample.Select(s => new SampleModel
{
Id = s.Id,
SampleText = s.SampleText
}).ToList();
return PartialView("_CreateModal", Model);
}
Views - Index, View, Create
Index - Calls Partial Views for View and Create
#using Project.Web.Controllers
#model Project.Web.Models.Sample.SampleModel
<!--html stuff here -->
#await Html.PartialAsync("_CreateModal")
<!--more html stuff here -->
#await Html.PartialAsync("_ViewData")
View - Foreach to Loop Records
#model Project.Web.Models.Sample.SampleModel
<table style="width: 100%;" id="example">
<thead>
<tr>
<th>#</th>
<th>Sample Text</th>
<th class="text-center">Status</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
#foreach (var sample in Model)
{
<tr>
<th scope="row">#sample.Id</th>
<td>#sample.SampleText</td>
<td class="text-center">
<div class="badge badge-success">Active</div>
</td>
<td class="text-center">
<div role="group" class="btn-group-sm btn-group">
<button class="btn-shadow btn btn-primary">Edit</button>
<button class="btn-shadow btn btn-primary">Delete</button>
</div>
</td>
</tr>
}
</tbody>
</table>
Create - Insert New Record
#model Project.Web.Models.Sample.SampleModel
<form method="post" asp-action="/SampleModel/CreateSample">
<div class="form-group">
<label for="CreationTime">SampleText</label>
<div>
<input type="text" class="form-control" id="SampleText" name="SampleText" placeholder="SampleText">
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Sign up</button>
</div>
</form>
As per Ammar's comment, you've just copy-pasted the Index Controller's data access. When building a form allowing the user to create a single new item, then the pattern is to typically pre-instantiate an empty model and pass it to the view:
[ActionName("_CreateModal")]
public ActionResult InsertNewRecord()
{
var model = new SampleModel(); // If Id is a GUID, then you could assign one here
return PartialView("_CreateModal", model);
}
was trying to use Html.BeginCollectionItem to work in my application and am struggling with getting the data to post. I want to add items to a list and then post the entire list. I am using ajax and jquery to add and delete items to my list and that seems to be working. But when I post the model received in my controller is always null even though when I look at fiddler the Form Data contains all my information.
Does anyone see something simple in my code that I am doing wrong?
Main View:
#model Test.Models.TestList
#using (Html.BeginForm())
{
<div class="form-group">
<div class="row">
<label for="AssemblyText" class="col-sm-1 col-sm-offset-1 control-label">Assembly:</label>
<div class="col-sm-2">
<input type="text" id="assembly" />
</div>
<label for="QuantityText" class="col-sm-1 control-label">Quantity:</label>
<div class="col-sm-2">
<input type="text" id="Qty" />
</div>
<button type="button" id="AddAssembly">Add Button</button>
</div>
</div>
<table id="Assemblies" class="table table-striped">
<thead>
<tr>
<th>Assembly</th>
<th>Quantity</th>
<th>Action</th>
</tr>
</thead>
<tbody class="text-left">
#if (Model != null)
{
foreach (var assembly in Model.mylist)
{
#Html.Partial("AssemblyRow", assembly)
}
}
</tbody>
</table>
<div class="form-group">
<input type="submit" id="submitbtn" class="btn btn-success" value="Submit" />
</div>
}
Partial View (AssemblyRow)
#model Test.Models.Test
<tr class="editorRow">
#using (Html.BeginCollectionItem("Assembly"))
{
<td>
#Html.HiddenFor(m => m.assembly)
#Html.TextBoxFor(m => m.assembly)
</td>
<td>
#Html.HiddenFor(m => m.Qty)
#Html.TextBoxFor(m => m.Qty)
</td>
<td>
<span class="dltBtn">
Delete
</span>
</td>
}
My Models are simple and look like...
public class TestList
{
public List<Test> mylist { get; set; }
}
public class Test
{
public string assembly { get; set; }
public string Qty { get; set; }
}
My controller
[HttpPost]
public ActionResult PostMain(TestList model)
{
return View();
}
I can provide whatever other code you guys think is helpful but I tried to keep it simple with what I thought were the relevant pieces.
Thanks for any help!
Edit: Pic of fiddler
Your collection property is named mylist therefore you must pass that name to the BeginCollectionItem method
#using (Html.BeginCollectionItem("mylist"))
{
....
which will generate elements with name=mylist[xxxx].assembly" (where xxxx is a Guid) that are need to correctly bind to your model.
However, you have other issues with your code. The DefaultModelBinder binds the first name/value pair matching a model property and ignores any subsequent name/value pairs with the same name. Because you have a hidden input for each property before the textbox, only the initial values you sent to the view will be bound when you submit, not the edited values. You need to remove both hidden inputs s that the partial is
#model Test.Models.Test
<tr class="editorRow">
#using (Html.BeginCollectionItem("mylist"))
{
<td>#Html.TextBoxFor(m => m.assembly)</td>
<td>#Html.TextBoxFor(m => m.Qty)</td>
<td>
<span class="dltBtn">Delete</span>
</td>
}
</tr>
Side note: It is also unclear what the html in the initial <div class="form-group"> is for. You including 2 inputs and a button, but that will not bind to your model and will not correctly add items to your collection (your add button needs to use ajax to call a server method that returns another partial view and append it to the DOM)
I don't see a submit button, so can't really tell what you are submitting, but try changing ActionResult PostMain(TestList model) to:
ActionResult PostMain(List<Test> model)
You need to use Editor Template instead of partial views. Main controller context is not available in partial views.
There are so many posts on Stackoverflow discussing this. One of them is
ASP.NET MVC 3 - Partial vs Display Template vs Editor Template
Problem: We have a Main View and a couple of partial views. Everything uses the same model as the main view. On the submit of Main view, I am trying to render a partial view and trying to pass down the model but only the properties that are getting passed down are the editable fields on the page or view. How can I pass down the other properties?
Current workaround: The partial view needs the other data too to generate a e-mail body but as it is not passed down we are creating hidden fields to pass them down.
Main View
#using (Ajax.BeginForm("CommonSave", "Common", null, new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
//OnSuccess = "submitFormSuccess",
//OnFailure = "submitFormFailure"
}, new { id = "commonForm" }))
{
<div>
<table>
<tr>
<td>
#Html.EditorFor(m => m.Name)
</td>
</tr>
<tr>
<td>
<input type ="submit" name="Common Save" />
</td>
</tr>
</table>
</div>
Controller:
public class CommonController : Controller
{
public ActionResult Index()
{
CommonModel model = new CommonModel()
{
Id = 3,
Name = "Test Name"
};
return View("Common", model);
}
[HttpPost]
public ActionResult CommonSave(CommonModel model)
{
return PartialView("CommonPartial", model);
}
public bool BinderSave(CommonModel model)
{
return true;
}
}
On load of the main view(Common) Index is called.
Upon submitting the Ajaxform on the Main view, the actionmethod CommonSave is called but the model passed to CommonSave only contains the Name and not the Id.
How can we pass that down as well without creating hidden fields or doing anything?
My actual model has a lot of fields which needs to be passed down.
instead of using an ajax form I would use an ajax call and populate the fields on success. Change your button to a button instead of submit
<input type ="button" class="btnSubmit" name="Common Save" />
then in your script tag
$('.btnSubmit').click(function () {
$.ajax({
url: '#Url.Action("CommonSave", "CommonController")',
type: "POST",
data: { Id: $('.Id').val() }
cache: false,
async: true,
success: function (result) {
//put the partial into a div on the form
$(".commonForm").html(result);
//now set the fields on the partial from the model
$('#EmailName').val('#Model.EmailName');
}
});
});
I understand the need for partial views and will tell you how I've solved this however each view should have it's own model, it just should. It's a tenet in the MVVM world:
Anyway, the partials could have a model via a interface. Define your partial view using a interface as it's model. For example:
interface:
public interface IPhone
{
string PhoneNumber ( get; set; }
}
you model:
public class MainModel : IPhone
{
public string Name
{
get { ... }
set { ... }
}
public string PhoneNumber
{
get { ... }
set { ... }
}
}
The point is that as long as the model passed to the main view implements the interface (the interface could also define a property that is another model) that the partial view depends on then all you need to do is pass the model to the partial, you may need to cast the model to the interface but technically you shouldn't have to. Let me know if this helps.
I have 2 controllers that generate 2 index views.
What i would like to do is use these views as global shared partial views but cant seem to get this working.
Does anyone know if this is even possible?
My controller code is
public ActionResult Index()
{
var viewModel = (from P in db.Projects
join R in db.Reports on P.ProjectTitle equals R.ReportProjectID into ps
from R in ps.DefaultIfEmpty()
select new MyViewModel { Project = P, Report = R });
return View(viewModel);
}
My ViewModel code is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MiLife2.ViewModels
{
public class MyViewModel
{
public Project Project { get; set; }
public Report Report { get; set; }
}
}
and my view is
#model IQueryable<MiLife2.ViewModels.MyViewModel>
#{
ViewBag.Title = "Index";
}
enter code here
<h2>Index</h2>
<div>#Html.Partial("_Partial1")</div>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.Project.ProjectTitle </td>
<td>#item.Project.ProjectCreatedByID</td>
<td>#item.Project.ProjectCreatedDate</td>
<td>#if (item.Report == null)
{
<text>No Reports</text>
}
else
{
#item.Report.Title;
}
</td>
<td>#if (item.Report == null)
{
<text> </text>
}
else
{
#item.Report.Description;
}</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Project.ProjectID }) |
#Html.ActionLink("Details", "Details", new { id=item.Project.ProjectID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Project.ProjectID })
</td>
</tr>
}
</table>
If i create a partial page and paste the above view into it and then use #HTML.Partial("_ProjPartial") i get the error
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[MiLife2.Project]', but this dictionary requires a model item of type 'System.Linq.IQueryable1[MiLife2.ViewModels.MyViewModel]'.
This does not happen if i use #HTML.Partial("_ProjPartial") from within the Index cshtml page in the specific controller views folder.
From the error it looks like to me that your partial view is looking for the same model as you have on your view. Passing the model to your partial should fix that error
#Html.Partial("_Partial1", Model)
update:
since that didn't work for you I would try using an ajax call
$('.btnSubmit').on('click', function(){
$.ajax({
url: "#(Url.Action("Action", "Controller"))",
type: "POST",
cache: false,
async: true,
data: { id: id },
success: function (result) {
$(".Content").html(result);
}
});
});
then in your controller
public PartialViewResult GetPartial()
{
var viewModel = (from P in db.Projects
join R in db.Reports on P.ProjectTitle equals R.ReportProjectID into ps
from R in ps.DefaultIfEmpty()
select new MyViewModel { Project = P, Report = R });
return PartialView("_Partial1", viewModel);
}
Using this ajax call you can call the partial view from any view and you can pass different id's, on button clicks or as needed to refresh the view. Hopefully calling it this way will fix your error. let me know if you have any questions.
Recently ran into something similar, so I wanted to add my 2 cents. The answer for me was in what I was passing to the Partial View.
I was attempting to pass a string to a partial view, but when that string happened to be null, it was acting as if I had not passed anything into the Partial, which means it defaulted to passing the current view's model.
For example, I have a view which renders a partial and that partial takes in a string:
#model SomeModel
#{ Html.RenderPartial("_MyPartialView", SomeModel.StringProperty) }
If SomeModel.StringProperty happens to be null, then it is going to try and pass what ever the current view's model is (which, in this case is SomeModel). So instead, I simply wrote the following which will pass in an empty string if SomeModel.StringProperty happens to be null:
#model SomeModel
#{ Html.RenderPartial("_MyPartialView", SomeModel.StringProperty ?? string.Empty) }
Hope this helps someone.