Confused on passing parameter to MVC Partial View? - c#

I inherited an MVC site and was asked to combine two pages into one. Since both Views have their own View Model, I thought instead of pushing the two VMs together that I could use a Partial View to display, in this case, the Clock Group in a View called Clock_Detail. However, I need to get the GroupId to populate the data for the Clock Group.
So, having rarely used Partial Views I’m confused on how to get this to work.
I created a new View called _ClockGroup.cshtml and just copied and pasted some basic code from the other page just to see if I could get anything to display.
In my ClockDetail controller I added the following.
public PartialViewResult ClockGroup(int groupId)
{
ClockGroupViewModel vm = DAL.GetClockGroupDetail(groupId);
return PartialView(vm);
}
Here is _ClockGroup.cshtml
#model site.Models.ViewModels.ClockGroupViewModel
<div class="form-group">
#Html.LabelFor(m => m.GroupId, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(m => m.GroupId, new { htmlAttributes = new { #class = "form-control", disabled = "disabled", #readonly = "readonly" } })
</div>
<div class="form-group">
#Html.LabelFor(m => m.GroupName, htmlAttributes: new { #class = "control-label col-md-2" })
#Html.EditorFor(m => m.GroupName, new { htmlAttributes = new { #class = "form-control", autofocus = "autofocus" } })
#Html.ValidationMessageFor(m => m.GroupName, "", new { #class = "text-danger" })
</div>
This is from the clock_detail.cshtml view. It uses the following Model and then I added the Partial to this view.
#model site.Models.ViewModels.ClockDetailViewModel
#Html.Partial("_ClockGroup")
Of course I get an error that the model passed a dictionary type “site.Models.ViewModels.ClockDetailViewModel” but the dictionary requires a model item of time “site.Models.ViewModels.ClockGroupViewModel".
Will I need to go ahead and combine the two ViewModels together and just use the single ViewModel?

When you do not specify an object as the second parameter:
#Html.Partial("_ClockGroup")
It automatically passes the current model which is of type ClockDetailViewModel
Your partial requires type ClockGroupViewModel
The fix would be when you call the partial to pass in the ClockGroupViewModel property of your ClockDetailViewModel object:
#Html.Partial("_ClockGroup", Model.ClockGroupViewModelProperty)
If your ClockDetailViewModel class does not have a ClockGroupViewModel property, you will need to add that to your ViewModel and populate the data.

Related

Added placeholder text to input element in CSHTML

I'm currently writing an ASP.Net MVC application
I've successfully made an input box in CSHTML.
#Html.EditorFor(model => model.Test, new { htmlAttributes = new { #class = "form-control"} })
I now want to take the content of a string from the controller and add is as placeholder text. I've tried the following
#Html.EditorFor(model => model.Test, new { htmlAttributes = new { #class = "form-control", placeholder = #Html.DisplayFor(model => model.Test) } })
But that set the placeholder like so: placeholder=""
If I instead use #Html.DisplayNameFor it sets the placeholder to the variable's name Test, but I want the actual string content. I'm aware that one can use the Viewbag to pass text to the view, but there must be a way of doing it this way.
Any help is greatly appreciated.
You can't use #Html.DisplayFor() as a placeholder but you can use Html.DisplayNameFor().
Example:
#Html.EditorFor(model => model.Test, new { htmlAttributes = new { #class = "form-control", #placeholder = "Your Place Holder" } })
OR
#Html.EditorFor(model => model.Test, new { htmlAttributes = new { #class = "form-control", placeholder = #Html.DisplayNameFor(model => model.Test) } })
I think you need to add the [Display (name="Your Text")] attribute in the propriety of the model like this:
[Display (Name="Name Of Client:")]
Public string Name {get; set;}
Make a Property in your Model which you are passing to view.
Assign the string into that property.
Model.SomeName= YourVariableName;
Then Try Like This :-
#Html.EditorFor(model => model.Test, new { htmlAttributes = new { #class = "form-control", placeholder = #Model.SomeName }

How to post form data from razor view to WebApi as dictionary?

I have a lot of forms created with helper
Html.BeginRouteForm
I want to post it to my web api controller and I can do it with predefined DTO.
But I want to post it as dictionary, because the forms is for getting parameters from user. In each case the set of parameters can be different.
How I can do it?
How I can do it better?
Thanks!
UPDATE
Here is my form:
#using (Html.BeginRouteForm("DefaultApi", new { controller = "Products", action = "Add", httproute = "true" }))
{
<div class="form-group">
Product:
#Html.TextBoxFor(m => m.Product, new { #class = "form-control" })
</div>
<div class="form-group">
Cost:
#Html.TextBoxFor(m => m.Cost, new { #class = "form-control" })
</div>
}
You can declare the parameter in the post action as a FormDataCollection which is derived from NameValueCollection and is very similar to a dictionary. This is the weakly typed method to post form data in MVC.
[HttpPost]
public ActionResult Edit(FormDataCollection formDataCollection)
{
var nvc = formDataCollection.ReadAsNameValueCollection();
foreach(var key in nvc)
{
var value = nvc[key];
}
}

Razor: How to dynamically assign a value to a read only TextBox field?

I have a read-only text box which is supposed to be initialized to a value dynamically. This field is present on a page which is used to enter values to details corresponding to the player that was created in the previous page (I am using RedirectToAction() for this purpose, but was unable to access the passed "PlayerID" value from the controller of the current page. So decided on using TempData. ). Since the PlayerID changes each time a new player is added, this field should display that value while still remaining read-only.
Also, this is a 'required' field, so the form cannot be submitted until the value is set.
#Html.TextBoxFor(model => model.PlayerID, htmlAttributes: new { #readonly = "read-only", #class = "control-label col-md-2", value = #Html.Raw(TempData["PlayerID"]) })
How can I set the value of this field as it is in the TempData["PlayerID"]?
You can either have a JavaScript that runs on the page load, ex:
#section Scripts
{
<script>
$(function(){
$("#PlayerID").val('#TempData["PlayerID"]');
});
</script>
}
Or, you can initialize the Model for the view inside the action, so if your action name was 'PlayerDetails', then you should have something like this:
public ActionResult PlayerDetails()
{
return View(new Player{PlayerID=TempData["PlayerID"]}
}
This way, when the view get bound to the view model, it will be initialized to the passed value.
You can try below code:
#Html.TextBox("PlayerID", #TempData["PlayerID"] ,new { #readonly = "read-only", #class = "control-label col-md-2"})
Or in your case:
#Html.TextBoxFor(model => model.PlayerId, htmlAttributes: new { #readonly = "read-only", #class = "control-label col-md-2", Value = #Html.Raw(TempData["PlayerID"]) })

ASP.net use session data to add something in create view

I created this simple ASP.NET project. (Default template using MVC)
In there I generated models from my database using ADO.net
I also generated controller for my model. (The model generated functions for create, edit, delete...) I also got view for every function in the controller.
So what I am trying to do now is:
-I am in my create view. (that means I see my form for creating objects)
-I need to enter data for [title, content] but to post in database I also need an id (this id is a foreign key, not the id of the object i am creating)
I already have this id saved in my session. I can access the session data by doing:
var user = Session["user"] as Uporabniki; //returns session data
user.id //selects id from session
Now what I want is to use this id in the create form textbox.
As of now the rows for id in my view look like this (I have no idea why it's a dropdown list. When I open the site I see names of all users in database and I can select one. But this is not what I want. I want to see only one):
<div class="form-group">
#Html.LabelFor(model => model.id_avtorja, "id_avtorja", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("id_avtorja", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.id_avtorja, "", new { #class = "text-danger" })
</div>
</div>
The create methods in controller look like this
// GET: Vprasanjas/Create
public ActionResult Create()
{
ViewBag.id_avtorja = new SelectList(db.Uporabniki, "id", "uporabniskoIme");
return View();
}
// POST: Vprasanjas/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,naslov,vsebina,datum,id_avtorja")] Vprasanja vprasanja)
{
if (ModelState.IsValid)
{
db.Vprasanja.Add(vprasanja);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.id_avtorja = new SelectList(db.Uporabniki, "id", "uporabniskoIme", vprasanja.id_avtorja);
return View(vprasanja);
}
Why is it not working if I change the view to something like this:
<div class="form-group">
#Html.LabelFor(model => model.id_avtorja, "id_avtorja", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#var user = Session["user"] as Uporabniki;
#Html.Raw(user.id)
#Html.ValidationMessageFor(model => model.id_avtorja, "", new { #class = "text-danger" })
</div>
</div>
And how can I fix this?
Try rewrite to
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,naslov,vsebina,datum,id_avtorja")] Vprasanja vprasanja)
{
if (ModelState.IsValid)
{
vprasanja.id = (Session["user"] as Uporabniki).id;
db.Vprasanja.Add(vprasanja);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.id_avtorja = new SelectList(db.Uporabniki, "id", "uporabniskoIme", vprasanja.id_avtorja);
return View(vprasanja);
}
Main idea - assign your id on post.
Please post some info on the error. But my guess is you aren't creating any input HTML element so there's nothing posting. You need something like <input type="hidden" id="id_avtorja" value="#user.id"> in the form.
Also, I'd advise against using data out of a session variable. That's older technology and very un-MVC in philosophy.

ASP.NET mvc4 - form elements inside ICollection loop

I want to make an editing function where you can add metafields (= specific fields for a category) to a category.
What I want to do in the view is
foreach (App.Models.Metafield field in Model.Category.Metafields)
{
<div class="field">
#Html.TextBoxFor(model => field.Name, new { #class = "form-control title" })
#Html.DropDownListFor(model => field.Type, Model.MetaTypes, new { #class = "form-control type" })
</div>
}
The problem is that the Metafields are not added to the viewModel when I hit the save button. So I guess the field.Name and field.Type should be replaced by something else..
This can't work this way because the sent form can't contain these dynamically generated fields with the same name.
However, you can collect the data from these dynamic fields using js and serialize it into a hidden field just before submitting the form, then you can parse it in your server side.
Here is an example with jquery:
$('#save-btn').click(function(e) {
$hidden = $("#hidden");
$hidden.val(getDynamicData());
});
function getDyanmicData() {
var data;
$fields = $(".field");
// get children and calculate data
return data;
}
This may be a bit too 'manual work', but I find it useful to know what's happening. For other solutions you can search form dynamic forms in ASP.NET MVC.
Please try this code
#for(int i=0;i<=Model.Category.Metafields.count();i++)
{
<div class="field">
#Html.TextBoxFor(m=> m[i].Name, new { #class = "form-control title" })
#Html.DropDownListFor(m=> m[i].Type, Model.MetaTypes, new { #class = "form-control type" })
</div>
}

Categories