I am creating a web application in MVC 4. How do we create a different view and render it based on if user is logged in or not? I am not sure if I am asking the right question since I am new to programming. I have looked this online but haven't found any resource that guides a beginner to do it. I'm pretty sure this is done in the controller but my question is how to tell the controller to render a different view after checking that the user is logged in. However, if there is some other obvious and efficient way of doing this then I would greatly appreciate if someone took the time to answer this question.
there's an override when you do return View()
return View("myView", myModel");
for example
public ActionResult Index()
{
if(User.Identity.IsAuthenticated)
return View("GoodUser");
return View("BadUser");
}
But if you're new, I would suggest 2 things:
see the hole online curse that is available for free in http://www.asp.net/mvc (right side where it says "Essential Videos")
create an MVC3 project and see how they do it as it's comes already with Membership
You can use Forms Authentication.
ViewModel:
public class LogIN
{
[Required]
[DisplayName("User name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[DisplayName("Password")]
public string Password { get; set; }
}
Repository:
public class LogInRepository
{
public bool CheckUser(string UserName, string Password)
{
//Query the database here and return true or false depending on the username and the password
}
}
Controller:
[HttpPost]
public ActionResult LogOn(LogIN model, string returnUrl)
{
if (ModelState.IsValid)
{
LogInRepository rep = new LogInRepository();
string UserRole = string.Empty;
if (rep.CheckUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName);
if (!string.IsNullOrEmpty(returnUrl))
return Redirect(returnUrl);
else
return Redirect(FormsAuthentication.DefaultUrl);
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
View:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Account LogIn</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Password)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Password)
#Html.ValidationMessageFor(model => model.Password)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
Web.config settings:
<authentication mode="Forms">
<forms loginUrl="~/Test/LogOn" timeout="2880" defaultUrl="~/Test/Index" />
</authentication>
<authorization>
<deny users="?" />
<allow users="*" />
</authorization>
If the User is not logged on user will be redirected to LogOn view and if logged in will redirect to Index view.
Related
I want to access to another page(View) by click a button. I try to write that in HTML code of my View using but it didn't work.
Shall I do it in Controller ? if yes how ? adding that I want to restrict the access only to the registred members.
this is what I tried :
<p>Sell your Car »</p>
If using a button is a alternative, you can also use this (though the suggestion provided by Ubiquitous Developers is also good):
Method 1
View
<form method="POST">
<input type="submit" name="sellCar" value="Sell your car" />
</form>
MVC Controller
public ActionResult Overview(string sellCar) {
if(!string.IsNullOrWhiteSpace(sellCar))
return RedirectToAction("SellYourCar");
return View();
}
public ActionResult SellYourCar() {
return View();
}
Method 2
View
#model CarMarketplace.Models.FormContent
<form method="POST">
<input type="submit" asp-for="Continue" value="Sell your car" />
</form>
MVC Controller
public ActionResult Overview(FormContent content) {
if(!string.IsNullOrWhiteSpace(content.Continue))
return RedirectToAction("SellYourCar");
return View();
}
public ActionResult SellYourCar() {
return View();
}
Model
public class FormContent {
public string Continue { get; set; }
}
Try this :
Sell your Car »
or
Sell your Car »
Ok, so now I'm trying to learn .net core mcv and I'm having a problem mapping data from MySQL back to my form. When I make the form as a single text box with a single button, and the other fields outside the form (but on the same page), the mapping works fine. If I include all the fields within the form, the data is obtained but not displayed on the page. I have even gone so far as to code one of the multiple submit buttons as an update of the data. I use the first text box to get the item from the database, which it does (but does not map to the text-boxes), then in the second text box (which should have the existing data, but is empty) I put the information to update in the database, click on the submit button for that text box, and the database is updated (but the text boxes in the view remain blank).
My model:
using System;
namespace DbTest.Models
{
public class ProductInventory
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public int Field4 { get; set; }
}
}
my controller:
using System;
using Microsoft.AspNetCore.Mvc;
using MySql.Data.MySqlClient;
using Microsoft.AspNetCore.Authorization;
using DbTest.Models;
namespace DbTest.Controllers
{
public class InventoryController : Controller
{
// [Authorize]
public IActionResult Index()
{
return View();
}
[HttpPost]
public IActionResult ProcessForm(string button, ProductInventory p)
{
IActionResult toDo = null;
if (button == "Button1")
{
toDo = GetItem(p);
}
if (button == "Button2")
{
toDo = UpdateField2(p);
}
if (button == "Button3")
{
toDo = UpdateField3(p);
}
if (button == "Button4")
{
toDo = UpdateField4(p);
}
return toDo;
}
// [HttpPost]
public IActionResult GetItem(ProductInventory p)
{
//CODE SNIP - DATABASE QUERY, IT ALL WORKS, SO WHY BOTHER YOU WITH THE DETAILS?
return View("Index", p);
}
public IActionResult UpdateField2(ProductInventory p)
{
//CODE SNIP - DATABASE UPDATE, ALL WORKS, NOTHING TO SEE HERE
return View("Index", p);
}
}
}
And finally, my view:
#model DbTest.Models.ProductInventory
#{
ViewData["Title"] = "Inventory Page";
}
#using (Html.BeginForm("ProcessForm", "Inventory", FormMethod.Post))
{
<div>
Search Item (Field 1):
#Html.TextBoxFor(model => model.Field1)
<input type="submit" name="button" value="Button1" />
</div>
<div>
Field 2:
#Html.TextBoxFor(model => model.Field2)
<input type="submit" name="button" value="Button2" />
</div>
<div>
Field 3:
#Html.TextBoxFor(model => model.Field3)
<input type="submit" name="button" value="Button3" />
</div>
<div>
Field 4:
#Html.TextBoxFor(model => model.Field4)
<input type="submit" name="button" value="Button4" />
</div>
}
To reiterate, if I close the form after Button1:
#using (Html.BeginForm("ProcessForm", "Inventory", FormMethod.Post))
{
<div>
Search Item (Field 1):
#Html.TextBoxFor(model => model.Field1)
<input type="submit" name="button" value="Button1" />
</div>
}
<div>
Field 2:
//etc.
the mapping works, but only the first field and button of the form work. With the form around all four fields and buttons, the mapping doesn't work, but the coding of the second button DOES update the database on clicking Button2.
Can someone explain what I've done wrong here?
Thanks!
At first, don't use html helpers in ASP.NET Core.They work but it is not best practice. Instead use tag helpers wherever possible. Furthermore, don't use your db models as view models.
Regarding your Index action: You forgot to pass a view model to your view.
In your ProcessForm action you instantiate IActionResult and then assign it with a (action) function. Don't do that. Instead use return RedirectToAction("ActionName");.
In your case I would handle the DB updates inside the ProcessForm action or in a function, which doesn't return IActionResult.
In conclusion, I can only recommend you to read the ASP.NET Core documentation and then ask again if you still don't get it to work. I recommend you to start with reading this.
I am new to Entity Framework/MVC and need to know how to modify a dropdown menu autogenerated by Visual Studio. I imagine it should be pretty simple, but I have not been able to figure it out. I have used the database-first approach and have the two following tables in my database:
public partial class RestaurantRating
{
public int RestaurantRatingId { get; set; }
public int RestaurantRatingScore { get; set; }
}
public partial class RestaurantType
{
public int RestaurantTypeId { get; set; }
public string RestaurantTypeDesc { get; set; }
}
I removed the extra details, but basically one of them will store restaurant ratings (the rating being an integer) and the other one will store restaurant types (what type of food they serve). The only really difference between the two is that the rating is an integer and they type description is a string.
Visual Studio autogenerated code for the CRUD operations for these and other tables. The HTML code in Create.cshtml for these two tables is as follows:
<div class="form-group">
#Html.LabelFor(model => model.RetaurantTypeId, "RetaurantTypeId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RetaurantTypeId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.RestaurantTypeId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RestaurantRatingId, "RestaurantRatingId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RestaurantRatingId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Id, "", new { #class = "text-danger" })
</div>
</div>
And the ViewBag information for these two tables in the Create Action result in the controller is the following:
ViewBag.RestaurantRatingId = new SelectList(db.RestaurantRating, "RestaurantRatingId", "RestaurantRatingId");
ViewBag.RestaurantTypeId = new SelectList(db.RestaurantType, "RestaurantTypeId", "RestaurantTypeDesc");
The problems and expected results are the following:
The dropdown menu for RestaurantType works as expected. It simply loads the different types into a dropdown menu and allows the user to select one of them. However, the RestaurantRating will load the RatingIds instead of the descriptions, which is what I need. I have tried changing the viewbag without success.
The HTML code automatically selects the first value for the dropdown menus, but it is possible to save NULL values to the database for these fields. How can I add an empty default value for the dropdown menus above, so that if the user selects the empty value (or does not touch the dropdown menu) a NULL value will be pushed to the database?
Any help is greatly appreciated. I will be happy to provide any additional code/information. Thank you so much!
You Just need to add an option label to your dropdown like this:-
#Html.DropDownList("RetaurantTypeId", null,"optionLable goes Here", htmlAttributes: new { #class = "form-control" })
and make sure on the other side that you are binding it to a nullable model property so the model binder will be able to set the model property value to Null.
hope this answer your question.
I'm trying to generate a selectList with a default selected item in MVC. I assign a selectlist to the viewbag and try to render it in my view and keep getting the below error.
Does anyone know what I might be doing wrong?
Create the list in my controller:
ViewBag.MasterAccountSelect = new SelectList(masterAccountsList, "MasterAccount", "MasterAccount", userSettingsViewModel.MasterAccountName);
Render the SelectList in my view:
<div class="editor-label">
#Html.LabelFor(model => model.MasterAccountSelect, "MasterAccountSelect")
</div>
<div class="editor-field">
#Html.DropDownList("MasterAccountSelect")
#Html.ValidationMessageFor(model => model.MasterAccountSelect)
</div>
I get this error:
Compiler Error Message: CS1061: 'UserSettingsViewModel' does not
contain a definition for 'MasterAccountSelect' and no extension method
'MasterAccountSelect' accepting a first argument of type
'UserSettingsViewModel' could be found (are you missing a using
directive or an assembly reference?)
I also get this error when I
You view model does not have a property named MasterAccountSelect hence the error. From the code you are assigning to ViewBag, you appear to be wanting to bind to a property in your model named MasterAccountName in which case your model should be
public class UserSettingsViewModel
{
[Display(Name = "Master Account Name")]
[Required(ErrorMessage = "Please select a master account name")]
public string MasterAccountName { get; set; }
public SelectList MasterAccountNameList { get; set; }
....
}
Then in the GET method
UserSettingsViewModel model = new UserSettingsViewModel
{
MasterAccountName = ..... ,
MasterAccountNameList = new SelectList(masterAccountsList, "MasterAccount", "MasterAccount")
}
return View(model);
And in the view
<div class="editor-label">
#Html.LabelFor(m => m.MasterAccountName)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.MasterAccountName, Model.MasterAccountNameList, "-Please select-")
#Html.ValidationMessageFor(m => m.MasterAccountName)
</div>
Side note: Since you wanting the options value and display text to be the same (using property MasterAccount), you can also use
MasterAccountNameList = new SelectList(masterAccountsList.Select(x => x.MasterAccount))
try Label instead of LabelFor, You can not use LabelFor strictly type html helper if you don't have property in your model.
<div class="editor-label">
#Html.Label("MasterAccountSelect")
</div>
<div class="editor-field">
#Html.DropDownList("MasterAccountSelect")
#Html.ValidationMessageFor(model => model.MasterAccountSelect)
</div>
One good Suggestion
Whenever you are using Viewbag for filling dropdown, Keep your Viewbag and property name different because it will not auto select your selected value.
Is it possible to make a HtmlHelper for parts where another htmlherlper is already used.
like in this case:
<div class="control-group">
#Html.LabelFor(model => model.cLinks.Link2Privates)
<div class="controls">
#Html.TextBoxFor(model => model.cLinks.Link2Privates, new { #class = " span7"})
#Html.ValidationMessageFor(model => model.cLinks.Link2Privates)
</div>
</div>
I'm assuming you simply want a helper that will generate all this information for you. While it's technically possible to create a custom helper that will do just that, it's actually better to use editor templates:
Views\Shared\EditorTemplates\BootstrapControlGroup.cshtml
<div class="control-group">
#Html.Label("")
<div class="controls">
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = " span7"})
#Html.ValidationMessage("")
</div>
</div>
Form
#Html.EditorFor(m => m.cLinks.Link2Privates, "BootstrapControlGroup")
That basically says to use this template to render the "editor" for this property. If you don't want to have to specify the template name, there's other ways. You can decorate your property with the UIHint attribute:
[UIHint("BoostrapControlGroup")]
public string Link2Privates { get; set; }
Or you can rely on a particular C# type or DataType. For example, if you wanted all strings to be handled this way, you could name that template String.cshtml instead and then just do:
#Html.EditorFor(m => m.cLinks.Link2Privates)
Razor would see that it was a string and use the String.cshtml editor template automatically if it existed. You can also use the DataType attribute, for example:
[DataType(DataType.EmailAddress)]
public string Email { get; set; }
And, then create a Views\Shared\EditorTemplates\EmailAddress.cshtml template. Razor will use this template, then, any time you call Html.EditorFor for this property.
I have created a HtmlHelper extension library that works well with Twitter Bootstrap scenarios.
http://buildmvc.codeplex.com
There's even a HtmlHelper add-on for Twitter Bootstrap Form Groups:
https://www.nuget.org/packages/Build.Mvc.TwitterBootstrap
#using ( Html.BuildForm().Begin(FormRenderStyle.Horizontal) )
{
Html.UpdateFormBuilderContext(ctx =>
{
ctx.RenderValidationMessages = true;
});
<fieldset>
<legend>User Information</legend>
#Html.BuildTextBoxGroupFor(m => m.FirstName, b=> b.TextBox(t=> t.Autofocus()))
#Html.BuildTextBoxGroupFor(m => m.Nickname)
#Html.BuildTextBoxGroupFor(m => m.LastName)
</fieldset>
}
Note: The library expects you to be using TwitterBootstrap > v3. The example above has version 2.3.2 class names.