I want to include a drop down list of years across all the pages in my website. I assumed a good place to put this logic was in the layout page (_layout.cshtml). If a user changes the year I want to change my year session (ModelBinder) to change as well. This was so easy to do with ASP.NET web forms, but seems near impossible to do in MVC. I tried a partial view with no luck. Anybody have any ideas?
As usual you could start by defining a view model:
public class YearsViewModel
{
public string Year { get; set; }
public IEnumerable<SelectListItem> Years
{
get
{
return new SelectList(
Enumerable.Range(1900, 112)
.OrderByDescending(year => year)
.Select(year => new SelectListItem
{
Value = year.ToString(),
Text = year.ToString()
}
), "Value", "Text");
}
}
}
Then a controller:
public class YearsController : Controller
{
public ActionResult Index()
{
return View(new YearsViewModel());
}
[HttpPost]
public ActionResult Index(int year)
{
// TODO: do something with the selected year
return new EmptyResult();
}
}
and a corresponding view for the index action:
#model SomeAppName.Models.YearsViewModel
#{
Layout = null;
}
#Html.DropDownListFor(x => x.Year, Model.Years)
And finally inside your _Layout.cshtml you could use this controller:
<div id="selectyear">#Html.Action("index", "years")</div>
and attach a corresponding script which would send an AJAX request when the value changes:
$(function () {
$('#selectyear select').change(function () {
$.post('#Url.Action("index", "years")', { year: $(this).val() }, function (result) {
});
});
});
Related
I'm pretty new regarding frontend, and couldn't find a clear solution to this simple problem.
In a Visual 2017 c# ASP.NET Framework MVC project, for a single page with ONLY ONE SUBMIT button which is already used for updating some string of my model totalInfo, I want to update the integer property ModelltypeForView of my model from the selected value of n radiobuttons.
I learnt I could update my controller model going with an AJAX call, but I can't find the way to update my view model, I must miss something simple but not obvious for a beginner.
Here are the main parts of my MVC.
I am aware of the problems due to the return type of my SettModelltype of my controller, as well as the ajax and foreach loop of my view, so basically: how do I finish this code? Is this problem even fixable without any partial view?
Thank you so much for your time.
// Model TotalInfoModell.cs
public class TotalInfoModell
{
public List<Modelltype> Modelltyper { get; set; }
public int ModelltypeForView { get; set; }
}
public class Modelltype
{
public int MTIndex { get; set; }
public string MTName { get; set; }
public bool MTSelected { get; set; } //? useless?
}
// Controller MainController.cs
static TotalInfoModell _totalInfo;
[HttpGet]
public ActionResult Main()
{
if (_totalInfo == null)
{
_totalInfo = new TotalInfoModell();
}
return View(_totalInfo);
}
[HttpPost]
public ActionResult SettModelltype(TotalInfoModell totalInfoFraView)
{
_totalInfo.ModelltypeForView = totalInfoFraView.ModelltypeForView;
for (int i = 0; i < _totalInfo.Modelltyper.Count; i++)
{
_totalInfo.Modelltyper[i].MTSelected = (i == _totalInfo.ModelltypeForView);
} /// Could be useless
return RedirectToAction("Main"); //????
}
// View Main.cshtml
#foreach (var modelltype in Model.Modelltyper)
{
#Html.RadioButtonFor(i => modelltype.MTIndex == Model.ModelltypeForView, modelltype.MTIndex, new { #class = "MTSelected" }) // ????
#modelltype.MTName<br />
}
...
<script>
$(function () {
$('.MTSelected').change(function () {
var viewModel = {
"ModelltypeForView": $('.MTSelected:checked').val(),
};
$.ajax({
url: '#Url.Action("SettModelltype", "Main")',
data: viewModel,
type: 'POST',
success: function (data) {
},
error: function (xhr) {
alert("It didn't work");
}
}).done(function (data) {
alert("Done");
$('#Id').val(data.ModelltypeForView); //??? Should return totalInfoModell
});
});
});
</script>
I'm not sure I understand. Do you want to pass your viewmodel back with the redirect?
If that's the case you can add it as a parameter. Here is a post that can help
I know there are a lot of similar question here but none seem quite the same as mine.
In my View:
#model LocalInformedMotionServer.Models.FeedData
#Html.DropDownList("Premise", Model.LoadUp("key"), new { #style = "width: 218px;height:35px;" })
In my controller:
public class CloudController : Controller
{
public IEnumerable<wsCommon.Premise> Premises { get; set; }
public ActionResult Feed(string key)
{
var feedData = new FeedData();
Premises= feedData.LoadUp(key);
return View(feedData);
}
}
In my Model:
public class FeedData
{
public IEnumerable<wsCommon.Premise> LoadUp(string saltKey)
{
Premises premises = new InformedBiz.Premises();
return premises.GetPremises(saltKey);
}
}
It errors because the variable:
"key"
in this call:
Model.LoadUp("key")
is being read in as'null' in my controller method.
Of course as this is all new to me I could be doing this all wrong..
ADDITIONAL:
In my CloudController Class I have this:
public class CloudController : Controller
{
public ActionResult Feed(string saltKey)
{
var feedData = new FeedData();
feedData.LoadUp(saltKey);
return View(feedData);
}
public ActionResult Index()
{
return View();
}
public ActionResult LogIn()
{
return View();
}
}
I'm not sure what your Premise class looks like, but I usually use an IEnumberable of SelectListItem for drop downs in my views. So you could do something like this:
public IEnumerable<SelectListItem> LoadUp(string saltKey)
{
Premises premises = new InformedBiz.Premises();
return premises.GetPremises(saltKey).Select(
p => new SelectListItem { Text = p.Name, Value = z.PremiseId.ToString() }
);
}
You'll also need to create a Post ActionResult method that accepts the model in your view (FeedData) as well as wrap your DropDownList control in a Html.BeginForm, to post results to the controller. Hope this makes a bit of sense.
You have not posted the properties of your FeedData model but assuming it contains a property which is typeof Premise and you want to be able to select a Premise from a collection, then using a view model that represents what you want to display/edit is the recommended approach (refer View Model Design And Use In Razor Views and What is ViewModel in MVC?)
You view model might look like
public class FeedDataVM
{
.....
[Display(Name = "Premise")]
[Required(ErrorMessage = "Please select a Premise")]
public int? SelectedPremise { get; set; }
....
public SelectList PremiseList { get; set; }
}
and in your controller (not sure what saltKey is for?)
public ActionResult Feed(string saltKey)
{
FeedDataVM model = new FeedDataVM();
IEnumerable<Premise> premises = // get the collection of premise objects from your repository
// assuming you want to display the name property of premise, but post back the key property
model.PremiseList = new SelectList(premises, "key", "name");
return View(model);
}
View
#model FeedDataVM
#using(Html.BeginForm())
{
....
#Html.LabelFor(m => m.SelectedPremise)
#Html.DropDownListFor(m => m.SelectedPremise, Model.PremiseList, "-Please select")
#Html.ValidationMessageFor(m => m.SelectedPremise)
....
<input type="submit" value="Save" />
}
and the POST method
public ActionResult Feed(FeedDataVM model)
{
// model.SelectedPremise contains the value of the selected option as defined by the key property of Premise
}
Side note: Your FeedData model contains a method to retrieve a collection of Premise which appears to be calling another service. You should avoid this type of design which makes it difficult to debug and unit test. Your controller is responsible for initializing/getting you data models and view models and for populating/mapping their properties.
I have a controller
public class JobSearchResultController : Controller {
public ActionResult Index() {
return View();
}
}
the View ~/Views/JobSearchResult/Index
#Html.Raw("<b> TEST VIEW </b>")
And then I register the route
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new RouteDescriptor {
Priority = 5,
Route = new Route(
"JobSearchResult",
new RouteValueDictionary {
{"area", "Module.Test"},
{"controller", "JobSearchResult"},
{"action", "Index"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "Module.Test"}
},
new MvcRouteHandler())
}
};
}
And I'm calling it in one of the other views
#Html.ActionLink("Click Me",
"Index",
new { controller = "JobSearchResult", area = "Module.Test"" })
Works good, it takes me to the Actual View/Index displaying the plain page with
TEST VIEW
in localhost/Orchard.Web/JobSearchResult URL
Now, my problem is I'm expecting this page to use the current theme / current layout (masterpage) just like a normal content type
How can I do this?
I know theres an [Admin] attribute where you can be able to display in Admin view using the Admin theme but how about if I want to display it in the Client. Is there something like
[Client] //display in Client
public class JobSearchResultController : Controller {
public ActionResult Index() {
return View();
}
}
Thanks in advance!
Yes. You can apply the Orchard.Themes.ThemedAttribute:
[Themed] //display in Client
public class JobSearchResultController : Controller {
public ActionResult Index() {
return View();
}
}
I want to display a string type as checkbox on MVC view, but returns it as string type on HTTP post. The problem is that it returns false on HTTP Post. Below is my code:
View:
#model List<Car>
foreach(var car in Model){
bool isFourWheel = false;
if(bool.TryParse(car.IsFourWheel, out isFourWheel){
#Html.CheckBox("IsFourWheel", isFourWheel); //need to be rendered as checkbox, but returns string type on HTTP POST
}
}
Model:
public class Car
{
public string IsFourWheel { get; set; } //bad naming, but it can contain any type, include boolean
}
Controller:
public ActionResult Index()
{
var cars = new List<Car>(){ new Car(){IsFourWheel = "true"},new Car(){IsFourWheel = "false"} };
return View(cars);
}
[HttpPost]
public ActionResult Index(List<Car> cars) **Problem IsFourWheel is false when true is selected **
{
return View(cars);
}
Any ideal would be very much appreciated.
You can try specifying a template name in your helper:
#Html.EditorFor(car => car.IsFourWheel, "CheckBox")
And defining the template to render the data the way you want, in either ~/Views/{YourControllerName}/EditorTemplates/CheckBox.cshtml or ~/Views/Shared/EditorTemplates/CheckBox.cshtml.
You can find a whole series of post by Brad Wilson on MVC templates here:
Brad Wilson: ASP.NET MVC 2 Templates, Part 1: Introduction
It is for MVC 2, but most concepts still apply to MVC 3 as well (save for the Razor syntax).
Update:
Actually you probably don't need a custom template for this. Try using #Html.CheckBoxFor(car => car.IsFourWheel) instead.
Update 2:
Drop the following template in ~/Views/Shared/EditorTemplates:
IsFourWheel.cshtml
#functions {
private bool IsChecked() {
if (ViewData.Model == null) return false;
return Convert.ToBoolean(ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
}
}
#Html.CheckBox("", IsChecked(), new { #class = "check-box" })
Then from your view, call it like so:
#Html.EditorFor(model => model.IsFourWheel, "IsFourWheel")
I tested it and binding works in both GET and POST scenarios.
You could alter your viewmodel like this:
public class Car
{
public string IsFourWheel { get; set; }
public bool IsFourWheelBool { get { return bool.Parse(IsFourWheel); } }
}
Your view would look like this:
#Html.EditFor(x => x.IsFourWheelBool);
I think it will be easier, if you add an Id to your model. Just like this
Model:
public class Car
{
public int CarID { get; set; }
public string IsFourWheel { get; set; }
}
View:
#model IEnumerable<Car>
foreach (var car in Model)
{
if(car.IsFourWheel == "true"){
<input type="checkbox" name="carID" value="#car.CarID" checked="checked" />
}
else
{
<input type="checkbox" name="carID" value="#car.CarID" />
}
}
Controller:
[HttpPost]
public ActionResult Index(List<int> carID)
{
//handle selected cars here
return View();
}
I have a view which has a partial view in it, which has a text box in it. The main view has a model of type person and the partial view a model of type person.other. When I do a ajax post back the other model is empty, I expected it to pick up the textbox data. This is the code;
Classes
public class Person
{
public string PersonID { get; set; }
public string Name { get; set; }
public Other Other { get; set; }
}
public class Other
{
public string OtherName { get; set; }
}
Controller
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
Person person = new Person();
person.Other = new Other();
person.Other.OtherName = "avbc";
return View(person);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Test(Other other)
{
if (Request.IsAjaxRequest())
{
return PartialView("Test");
}
return null;
}
View
#model PartialViewTest.Models.Person
<h2>Index</h2>
<div id="mydiv">
#Html.Partial("Test", Model.Other)
</div>
PartialView
#model PartialViewTest.Models.Other
<h1>Test</h1>
#using (Html.BeginForm("Test", "Home", FormMethod.Post, new { id = "testForm" })) {
#Html.TextBoxFor(m => m.OtherName)
<input type="submit"/>
}
Jquery submit
$(document).ready(function () {
$('#testForm').submit(function () {
$.post($(this).attr("action"),
$(this).serialize(),
function (result) {
$('#mydiv').html(result);
});
);
});
Make sure you cancel the default form submission by returning false from the submit callback. Also you seem to be missing a closing }:
$(document).ready(function () {
$('#testForm').submit(function () {
$.post($(this).attr("action"), $(this).serialize(), function (result) {
$('#mydiv').html(result);
});
return false;
});
});
Also you might need to modify your controller action like this because what is actually sent to the server is Other.OtherName=foo:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Test([Bind(Prefix="Other")]Other other)