I would like to know if I can convert my div button group (btn-group) to razor syntax in my asp.net mvc app? I want the razor syntax so I can preselect and pre activate a button label when entering the page. If razor isn't needed, then can someone please show me how to make a button active and selected on the page being entered from my view model data? It seems that without razor I would have to pass my viewmodel data to javascript to perform, but that doesn't seem right. Here is my html
<div class="form-group">
#Html.LabelFor(model => model.Listing.SpaceType, htmlAttributes: new { #class = "control-label" })
<div class="btn-group form-control" data-toggle="buttons" id="SpaceType">
<label id="SpaceTypeLabel0" class="btn btn-primary">
<input type="radio" name="typeoptions" autocomplete="off" id="0"> House
</label>
<label id="SpaceTypeLabel1" class="btn btn-primary">
<input type="radio" name="typeoptions" autocomplete="off" id="1"> Apartment
</label>
<label id="SpaceTypeLabel2" class="btn btn-primary">
<input type="radio" name="typeoptions" autocomplete="off" id="2"> Studio
</label>
<label id="SpaceTypeLabel3" class="btn btn-primary">
<input type="radio" name="typeoptions" autocomplete="off" id="3"> Other
</label>
</div>
</div>
Here is my model
public class Space
{
public int SpaceId { get; set; }
public virtual SpaceOverview Overview { get; set; }
public virtual SpaceDetails Details { get; set; }
public virtual SpaceListing Listing { get; set; }
public virtual SpaceAddress Address { get; set; }
[Required]
public DateTime DateCreated { get; set; }
}
and spacelisting is
public class SpaceListing
{
[Key, ForeignKey("SpaceOf")]
public int SpaceListingId { get; set; }
public SpaceType SpaceType { get; set; }
public SpaceLocation SpaceLocation { get; set; }
public SpaceAccommodation Accommodation { get; set; }
public Space SpaceOf { get; set; } // one-to-one
}
and spacetype is
public enum SpaceType
{
Home,
Apartment,
Studio,
Park,
Beach,
Field,
Backyoard,
FrontYard,
Other
}
Currently you creating a group of radio buttons with name="typeoptions" which have no relationship to the model, and your not even giving the radio buttons a value attribute so nothing would post back anyway.
The syntax should be
#Html.RadioButtonFor(m => m.Listing.SpaceType, "House", new { id = "House" })
#Html.Label("House")
#Html.RadioButtonFor(m => m.Listing.SpaceType, "Apartment", new { id = "Apartment" })
#Html.Label("Apartment")
...
To make this easier, you can create an extension method
public static class RadioButtonHelper
{
public static MvcHtmlString EnumRadioButtonListFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
{
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
string name = ExpressionHelper.GetExpressionText(expression);
Type type = Nullable.GetUnderlyingType(metaData.ModelType);
if (type == null || !type.IsEnum)
{
throw new ArgumentException(string.Format("The property {0} is not an enum", name));
}
StringBuilder html = new StringBuilder();
foreach (Enum item in Enum.GetValues(type))
{
string id = string.Format("{0}_{1}", metaData.PropertyName, item);
StringBuilder innerHtml = new StringBuilder();
innerHtml.Append(helper.RadioButtonFor(expression, item, new { id = id }));
innerHtml.Append(helper.Label(id, item.ToDescription()));
TagBuilder div = new TagBuilder("div");
div.AddCssClass("radiobutton");
div.InnerHtml = innerHtml.ToString();
html.Append(div.ToString());
}
TagBuilder container = new TagBuilder("div");
container.AddCssClass("radiobutton-container");
container.InnerHtml = html.ToString();
return MvcHtmlString.Create(container.ToString());
}
}
Note, this uses the following extension method
public static string ToDescription(this Enum value)
{
FieldInfo field = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])field.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
return value.ToString();
}
which allows you to decorate the enum values with a 'friendly' name
public enum SpaceType
{
Home,
[Description("2 bed apartment")]
Apartment,
....
}
and in the view
#Html.EnumRadioButtonListFor(m => m.Listing.SpaceType)
Related
I have a DropDownListFor that doesn't seem to be setting the expected Model property. Here's the HTML:
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedItem,
new SelectList(Model.ItemList, "Id", "Desc"), null,
new { #class = "selected-list", #size = "3" });
<div>
#Model.SelectedItem
</div>
<input type="submit" value="Do Stuff"
asp-controller="My"
asp-action="DoStuff"
asp-route-itemId="#Model.SelectedItem" />
</div>
}
The div is just there for debugging purposes, and it either shows 0 or blank.
The underlying model has a property:
public int SelectedItem { get; set; }
I've also tried a string:
public string SelectedItem { get; set; } = String.Empty;
(Hence why 0 or blank)
The actual list itself displays fine, it just doesn't update the model on select. How can I get it to update the model?
If you use asp-route-itemId, your action need contains parameter named itemId to receive the value. But for your scenario, it seems to be useless to receive such value, because it will always receive the value you set instead of the dropdown changed value.
Here is a simple demo you could follow and check if any difference with yours:
Model
public class TestVM
{
public string SelectedItem { get; set; } = String.Empty;
public List<Item> ItemList { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Desc { get; set; }
}
View(Index.cshtml)
#model TestVM
#using (Html.BeginForm())
{
#Html.DropDownListFor(x => x.SelectedItem,
new SelectList(Model.ItemList, "Id", "Desc"), null,
new { #class = "selected-list", #size = "3" });
<div>
#Model.SelectedItem
</div>
<input type="submit" value="Do Stuff"
asp-controller="Home"
asp-action="DoStuff" />
}
Controller
[HttpGet]
public IActionResult Index()
{
//Note: hard-coded the model just for easy testing.....
var model = new TestVM()
{
SelectedItem = "1",
ItemList = new List<Item>()
{
new Item(){Id=1,Desc="aa"},
new Item(){Id=2,Desc="bb"},
new Item(){Id=3,Desc="cc"}
}
};
return View(model);
}
[HttpPost]
public IActionResult DoStuff(TestVM model)
{
return View("index", model);
}
Result
I am trying to make a login/signup system with ASP.NET. I saw a tutorial on how to make it and I started following him. But it was sometimes messy and I couldn't understand him. (For the tutorial).
I used the 'new scaffolded item' to create a new 'identity' for login & registration.
I added custom data of my own (at this moment it's only FirstName&LastName) and when I create A user everything looks excellent, in debug mode I could see the data saved in my class but when it transfers to the database it holds as "NULL".
I am still new to this technique so I am not sure if that's all the code that's needed for you to help spot the problem. If the's anything to add, comment down, please.
here's the HTML for the registration (at the moment the login isn't complete) [Directory: Areas/Identity/Pages/Register.cshtml]
#page
#model RegisterModel
#{ ViewData["Title"] = "Register"; }
#{ Layout = "~/Areas/Identity/Pages/_AuthLayout.cshtml"; }
<form asp-route-returnUrl="#Model.ReturnUrl" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Input.FirstName"></label>
<input asp-for="Input.FirstName" class="form-control" />
<span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="Input.LastName"></label>
<input asp-for="Input.LastName" class="form-control" />
<span asp-validation-for="Input.LastName" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label asp-for="Input.Email"></label>
<input asp-for="Input.Email" class="form-control" />
<span asp-validation-for="Input.Email" class="text-danger"></span>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Input.Password"></label>
<input asp-for="Input.Password" class="form-control" />
<span asp-validation-for="Input.Password" class="text-danger"></span>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label asp-for="Input.ConfirmPassword"></label>
<input asp-for="Input.ConfirmPassword" class="form-control" />
<span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span>
</div>
</div>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
#section Scripts {
<partial name="_ValidationScriptsPartial"></partial>
}
here's the C# (the backend) [Directory: Areas/Identity/Pages/Register.cshtml.cs]
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.Logging;
using SvivaTeam.Areas.Identity.Data;
namespace SvivaTeam.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class RegisterModel : PageModel
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
private readonly ILogger<RegisterModel> _logger;
private readonly IEmailSender _emailSender;
public RegisterModel(
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
ILogger<RegisterModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
_emailSender = emailSender;
}
[BindProperty]
public InputModel Input { get; set; }
public string ReturnUrl { get; set; }
public IList<AuthenticationScheme> ExternalLogins { get; set; }
public class InputModel
{
[Required]
[DataType(DataType.Text)]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Last Name")]
public string LastName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
public async Task OnGetAsync(string returnUrl = null)
{
if (User.Identity.IsAuthenticated)
Response.Redirect("/");
ReturnUrl = returnUrl;
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
}
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();
if (ModelState.IsValid)
{
var user = new ApplicationUser
{
UserName = Input.Email,
Email = Input.Email,
FirstName = Input.FirstName, //Custom Code
LastName = Input.LastName //Cusom Code
};
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
if (_userManager.Options.SignIn.RequireConfirmedAccount)
{
return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl });
}
else
{
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
// If we got this far, something failed, redisplay form
return Page();
}
}
}
Here's more C# code that holds the custom info (In the video he said to make it). [Directory: Areas/Identity/Data/ApplicationUser.cshtml]
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace SvivaTeam.Areas.Identity.Data
{
public class ApplicationUser : IdentityUser
{
[PersonalData]
[Column(TypeName = "nvarchar(100)")]
public string FirstName { get; set; }
[PersonalData]
[Column(TypeName = "nvarchar(100)")]
public string LastName { get; set; }
}
}
I've tried to pass a list of models to controller but with no luck. I have a problem with generating empty view and then pass values from filled forms to controller. What I have:
Models
public class PostsModel
{
[Required]
[DataType(DataType.DateTime)]
public DateTime PublishDate { get; set; }
[Required]
public List<PostModel> Posts { get; set; }
}
public class PostModel
{
public string Language {get;set;}
public string X {get;set;}
public string Y {get;set;}
// and other properties
}
Controller
public IActionResult New()
{
ViewData["ButtonName"] = "Add";
return View(new PostsModel { PublishDate = DateTime.UtcNow });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> New(PostsModel model)
{
if (ModelState.IsValid)
{
// some code
// never reaches this point
}
return View(model);
}
Form:
<form method="post">
<h4>XYZ</h4>
<hr />
#{
Model.Posts = new List<PostModel>(2);
for (var i = 0; i < 2; i++)
{
Model.Posts.Add(new PostModel());
}
foreach (var test in Model.Posts)
{
<h4>xyz</h4>
<div class="form-group">
<label asp-for="#test.Subject">Temat</label>
<input asp-for="#test.Subject" class="form-control" />
<span asp-validation-for="#test.Subject" class="text-danger"></span>
</div>
}
}
<button type="submit" class="btn btn-primary">#ViewData["ButtonName"]</button>
</form>
Of course model is never valid. I don't have an idea how to do such functionality.
As #StephenMuecke said, using for-loop is working approach. In this particular problem that I had it's enough to do:
Controller:
public IActionResult New()
{
ViewData["ButtonName"] = "Add";
// example:
return View(new PostsModel { PublishDate = DateTime.UtcNow, Posts = new List<PostModel>(2) { new PostModel(), new PostModel() } });
}
View:
for (var i = 0; i < Model.Posts.Count; i++)
{
<h4>xyz</h4>
<div class="form-group">
<label asp-for="#Model.Posts[i].Subject">Temat</label>
<input asp-for="#Model.Posts[i].Subject" class="form-control" />
<span asp-validation-for="#Model.Posts[i].Subject" class="text-danger"></span>
</div>
#*and so one*#
}
I also want to thank M. Kostrzewski and M. Kaletka who also helped me on some .net group
Im trying to pass values from view to controller using Vue.js, but Im getting a problem with decimal values only. When I input decimal values and post it to controller, it arrives NULL.
Note: Only for decimal values, to Integer or String values it's ok.
Here is my code:
salvarProdutos: function () {
load();
this.$http.post(urlInit + '/Quotation/updateInfoComercial/', {
RefCotacao: this.refCotacao,
//InformaçoesComerciais//
Qtd: this.quantidade,
UnidadeMedida: this.unidadeMedida,
ValorUnitario: this.valorUnitarioProduto,
LoteMinimo: this.loteMinimo,
PrazoEntrega: this.prazoEntrega,
PorcentagemMaxVariacao: this.porcentagemVariacao
//--------------------------//
}).then((response) => {
unload();
if (response.data.worked) {
alert("Dados salvos com sucesso!");
}
else {
alert("Erro - Tente novamente mais tarde.");
}
});
}
<div class="col-md-6">
<div class="form-group label-floating">
<label class="control-label" for="porcentagemVariacao">
Porcentagem máxima de variação
</label>
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text">%</span>
</div>
<input type="number" min="1" step="0.1" class="form-control" id="porcentagemVariacao" v-model="porcentagemVariacao" required>
<span class="material-input"></span>
</div>
</div>
</div>
Here is my Controller:
[HttpPost]
public ActionResult updateInfoComercial(string refCotacao, InformacoesComerciais infoCF, string form)
{
var partNumber = infoCF.PartNumber;
var porcentagem = infoCF.PorcentagemMaxVariacao;
var message = "";
message = "OK";
return Json(new { message = message }, JsonRequestBehavior.AllowGet);
}
Here is my model:
public partial class InformacoesComerciais
{
public InformacoesComerciais() { }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
//public int ID { get; set; }
public decimal? Qtd { get; set; }
[StringLength(8)]
public string UnidadeMedida { get; set; }
[Key]
[Column(Order = 1)]
[StringLength(20)]
public string RefCotacao { get; set; }
public decimal? ValorUnitario { get; set; }
public decimal? LoteMinimo { get; set; }
public decimal? PorcentagemMaxVariacao { get; set; }
//[Column(TypeName = "date")]
public string PrazoEntrega { get; set; }
}
I change the <globalization> tag in Web.config and works:
Before:
<globalization culture="pt-BR" uiCulture="pt-BR" />
After:
<globalization culture="en-US" uiCulture="en-US" />
Thanks.
this is my models:
public class Ressource
{
[Key]
public int RessourceId { get; set; }
public string TitreR { get; set; }
public string Link { get; set; }
public string Type { get; set; }
public string Desc { get; set; }
public int Position { get; set; }
public int Rating { get; set; }
public string Tags { get; set; }
public int SectionId { get; set; }
public int UserId { get; set; }
}
public class Section
{
[Key]
public int SectionId { get; set; }
public string Titre { get; set; }
public String Tags { get; set; }
public virtual ICollection<Ressource> Ressources { get; set; }
}
public class structure
{
public Ressource ress;
public List<string> liste;
}
In this view, I enter the resource's title (TitreR), a description (Desc) and I choose from a list of tags which ones I want to link with this resource as well as entering a tag:
#model Mocodis.Models.structure
#{
ViewBag.Title = "AjouterRessource";
}
<h2>AjouterRessource</h2>
#using (Html.BeginForm("AjouterRessource", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<input type="hidden" name="SectionId" value="#Model.ress.SectionId" />
<legend>Ressource</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ress.TitreR)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ress.TitreR)
#Html.ValidationMessageFor(model => model.ress.TitreR)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ress.Desc)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ress.Desc)
#Html.ValidationMessageFor(model => model.ress.Desc)
</div>
</fieldset>
}
<form id="orderProductForm219" name="orderProductForm219" method="post" >
<b>Choisir les mots clés:</b>
<br /><br />
#foreach (string s in #Model.liste)
{
<input type="checkbox" name="tags[]" value="#s"> #s<br />
}
<input type="text" name="tags" id="tags" value="" />
<p>
<input type="submit" value="Create" />
<input type="hidden" name="tag" value="tags" />
<input type="hidden" name="res" value="#Model.ress" />
</p></form>
(I didnt put the javascript for it)
Finally the controllers are:
public ActionResult AjouterRessource(int SectionId)
{
Ressource res = new Ressource();
res.SectionId = SectionId;
Section sec = _db.Sections.Where(r => r.SectionId == SectionId).FirstOrDefault();
char[] delimiterChars = { ' ', ',', '.', ':', '\t' };
List<string> l = new List<string>();
l = sec.Tags.Split(delimiterChars).ToList();
structure s = new structure();
s.ress = res;
s.liste = l;
return View(s);
}
public string Check ( string list, string s)
{
char[] delimiterChars = { ' ', ',', '.', ':', '\t' };
List<string> l = new List<string>();
List<string> liste = new List<string>();
l = s.Split(delimiterChars).ToList();
liste = list.Split(delimiterChars).ToList();
foreach (string item in l)
{
if (!liste.Contains(item))
liste.Add(item);
}
return (string.Join(" ", liste.ToArray()));
}
[Authorize]
[HttpPost]
[InitializeSimpleMembership]
public ActionResult AjouterRessource(Ressource res, int SectionId, string tag)
{
if (ModelState.IsValid)
{
res.SectionId = SectionId;
var model = _db.Sections.Where(c => c.SectionId == SectionId).FirstOrDefault();
res.Tags = tag;
model.Tags = Check(model.Tags, tag);
_db.Entry(model).State = EntityState.Modified;
_db.Entry(res).State = EntityState.Added;
_db.SaveChanges();
return RedirectToAction("Section", new { SectionId = SectionId });
}
return View();
}
I keep getting: Object reference not set to an instance of an object on the line: #Html.ValidationSummary(true) in my view. Can you tell me how to fix it please?
Thank you
Every time I have gotten that error it has been from not initializing something. Looking through your code the only spot I am seeing that might need it is when you are setting structure. You might try putting a constructor on that class to initialize the fields and see if that helps. something like
public structure()
{
ress = new Resource();
liste = new List<string>();
}
You probably need to add the { get; set; } to the resource and list under structure as well. Hopefully this helps.