Dropdownlistfor in mvc razor with font awesome icons - font-awesome

I have a strongly typed view with dropdownlistfor for rating
public class EmployeeRating
{
public int RatingId { get; set; }
public IEnumerable<RatingList> RatingList { get; set; }
}
public class RatingList
{
public int Value { get; set; }
public string Text { get; set; }
}
In my view
#Html.DropDownListFor(m =>EmployeeRatings.RatingId,new SelectList(ViewBag.RateList, "Value", "Text", EmployeeRatings.RatingId),
"Select Rating", new { #class = "form-control"})
I have ratings like GOOD,POOR,AVERAGE.
To this I need to add related font awesome icons and display in my view .But it is not rendering icons,instead showing <i class="fa fa-smile-o"></i> Good.
Then I tried with UNICODE of FA icons.But not displaying icons(showing UNICODE in dropdownlist).
I tried like this
RatingList = new List<RatingList>();
RatingList.Add(new RatingList { Value = 1, Text = " Good" });
RatingList.Add(new RatingList { Value = 2, Text = " Average" });
I could not even find any helper tags which does this work.Also I need to give color to each item something like smiley Good-Green ,frown Poor-Red , meh Average -Orange.
I am doing this from past two days and still no luck.
Please help.Thanks in advance.

Use the following to render FA icons in dropdownlist:
#Html.DropDownListFor(m => m.SelectedSiteID, Model.Sites, null, new { #id = "siteSelection", #onchange = "siteChanged(this)", #class = "siteSelector" })
<i style="position:absolute;top:10px;font-size:24px;right:15px;color:white;pointer-events:none;" class="fa fa-angle-down" aria-hidden="true"></i>

Related

Input type number strange behaviour

I'm building a fruit and vegetables e-commerce app. I have made a system for users to add items to cart by entering values in an <input type="number"> field, within a form.
The problem I'm having is that when I enter "1.2" for example, my server receives the value "12". But when I enter the value "1", my server just receives the value "1". This really bugs me. Does anyone know why this happens?
Thanks in advance.
Some data for context: my backend is build in Asp.Net Core 3.x. The backend endpoint looks like this:
[HttpPost]
public ActionResult UpdateShoppingCart(int productId, float amount)
{
Console.WriteLine(amount);
var selectedProduct = _productRepository.Products.SingleOrDefault(p => p.Id == productId);
float productCount = 0;
if(selectedProduct != null)
{
productCount = _shoppingCart.UpdateCart(selectedProduct, realAmount);
}
return Redirect(Request.Headers["Referer"].ToString());
}
The html part looks like this
<div class="text-center">
<div class="float-left">
<p>Cantidad:</p>
</div>
<div class="float-right">
<form method="post" class="form-inline" asp-controller="ShoppingCart" asp-action="UpdateShoppingCart">
<input type="hidden" name="productId" value="#Model.Product.Id"/>
<input name="amount" type="number" class="form-control h-auto input-add-to-cart" data-id="#Model.Product.Id" min=0 step="0.1" value="#(Model.ShoppingCartItem != null ? (Model.ShoppingCartItem.Amount).ToString(new CultureInfo("en-US")) : (0).ToString())"/>
<button type="submit" id="update-button-#Model.Product.Id" disabled class="ml-1 btn btn-sm btn-outline-warning"><i class="material-icons material-icons-actual-font" disabled>shopping_cart</i>Actualizar</button>
</form>
</div>
</div>
UPDATE:
The code for the ShoppingCartItem model is the following:
public class ShoppingCartItem
{
public int Id { get; set; }
public Product Product { get; set; }
public int ProductId { get; set; }
public float Amount { get; set; }
public string ShoppingCartId { get; set; }
}
SECOND UPDATE:
Could it be that i'm setting a culture in the thread that uses the comma as a dot?
In the Configure method of Startup, i'm setting
var cultureInfo = new CultureInfo("es-CL");
cultureInfo.NumberFormat.CurrencySymbol = "$";
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
Ok, that was it.
The problem was that my Browser was setting the decimal separator as a dot (for example, 4.7) and my backend was expecting a comma (for example, 4,7) so that confused my application.
The culprit was what was posted on my second update, that was
var cultureInfo = new CultureInfo("es-CL");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;

Set MVC SelectList text to two fileds [duplicate]

How would I generate a select list, where the text field, is made up of two or more text columns, eg: Where I have a Description and Rate field in my database, I want to combine these to show:
Large--£200
Medium--£150
Small--£100
Controller code is:
var stands = db.Stands.Where(s => s.ExhibitorID == null).ToList();
ViewBag.StandID = new SelectList(stands,"StandID", "Description" + "-- £" + "Rate");
...and my view is (currently):
<div class="editor-field">
#Html.DropDownList("StandID", "--Select--")
</div>
...but the "Description" + "-- £" + "Rate"); won't run:
DataBinding:
'System.Data.Entity.DynamicProxies.Stand_63F8C9F623B3C0E57D3008A57081AFCD9C39E1A6B79B0380B60840F1EFAE9DB4'
does not contain a property with the name 'Description--£Rate'.
Thanks for any help,
Mark
You could create a new anonymous class using a simple LINQ projection, and then use the SelectList(IEnumerable, string, string) constructor overload to specify the value and text fields to be used for the <option> elements i.e.:
var stands =
db.Stands
.Where(s => s.ExhibitorID == null)
.Select(s => new
{
StandID = s.StandID,
Description = string.Format("{0}-- £{1}", s.Description, s.Rate)
})
.ToList();
ViewBag.StandID = new SelectList(stands, "StandID", "Description")
Edit
In C#6 and later, string interpolation makes for better reading than string.Format
...
Description = $"{s.Description}-- £{s.Rate}"
If you project to a strong ViewModel class name (instead of to an anonymous class), you will undoubtedly want to replace the magic strings with the safety of the nameof operator:
ViewBag.StandID = new SelectList(stands, nameof(Stand.StandID), nameof(Stand.Description));
var stands = db.Stands.Where(s => s.ExhibitorID == null).ToList();
IEnumerable<SelectListItem> selectList = from s in stands
select new SelectListItem
{
Value = s.StandID,
Text = s.Description + "-- £" + s.Rate.ToString()
};
ViewBag.StandID = new SelectList(selectList, "Value", "Text");
You can create a partial Model class
public partial class Stand
{
public string DisplayName
{
get
{
return this.Description + "-- £" + this.Rate.ToString();
}
}
}
Then in your View
var stands = db.Stands.Where(s => s.ExhibitorID == null).ToList();
ViewBag.StandID = new SelectList(stands,"StandID", "DisplayName");
The Format of the constructor that you are using is
SelectList(IEnumerable items, string dataValueField, string dataTextField).
So when you use it the way you have you are actually telling it to bind to the TextField called "Description-- £Rate" and if this is not what the field is called coming in the from the DB it won't know what you are indicating.
Either of the two methods described above will work as long as the value you have in your dataValueField matches the name of the property you put the Value in and the dataTextField matches the property name of where you put the Text, perhaps a mix of the two solutions above. (Only because I prefer lambda expressions over linq.) and using a selectlist item prevents it from have to do a ToList on the collection after the transform. you are actually creating the objects that naturally bind to a select list.
You also may want to put in checks on the description or rate to make sure they aren't empty before putting them into the list
var stands = db.Stands.Where(s => s.ExhibitorID == null)
.Select(s => new SelectListItem
{
Value = s.StandID.ToString(),
Text = s.Description + "-- £" + s.Rate.ToString()
});
ViewBag.StandID = new SelectList(stands, "Value", "Text");
I did this by modifying my View Model, here are my code:
The View Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MvcEsosNew.Models;
using System.Web.Mvc;
namespace MvcEsosNew.ViewModels
{
public class EntitlementViewModel
{
public int EntitlementCount { get; set; }
public Entitlement Entitlement { get; set; }
public SelectList Member { get; set; }
public SelectList Job_Grade { get; set; }
public SelectList Department { get; set; }
public SelectList Esos_Batch { get; set; }
}
public class department_FullName
{
public int deptID { get; set; }
public string deptCode { get; set; }
public string deptName { get; set; }
public string fullName { get { return deptCode + " - " + deptName; } }
}
}
The Controller
public void getAllDepartment(EntitlementViewModel entitlementVM)
{
var department = from Department in db.Departments.Where(D => D.Status == "ACTIVE").ToList()
select new department_FullName
{
deptID = Department.id,
deptCode = Department.department_code,
deptName = Department.department_name
};
entitlementVM.Department = new SelectList(department, "deptID", "fullName");
}
The View
<div class="form-group row">
<div class="col-sm-2">
#Html.LabelFor(model => model.Entitlement.department_id)
</div>
<div class="col-sm-10">
#Html.DropDownListFor(model => model.Entitlement.department_id, Model.Department, new { #class="form-control" })
#Html.ValidationMessageFor(model => model.Entitlement.department_id)
</div>
</div>
The result:

Post html select to MVC Controller. Finding a better way

I'm just looking for a better way to do the following :
I've got an html select :
<form method="post" action="/Account/ChangeUserRole">
<select name="Val" onchange="this.form.submit();" class="span2">
#foreach (var r in ViewBag.UserRoles)
{
#if (u.UserRole.ID == r.ID)
{
<option selected="selected" value="#u.ID/#r.ID">#r.Name</option>
}
else
{
<option value="#u.ID/#r.ID">#r.Name</option> // <-- better way?
}
}
</select>
</form>
I'm posting it as "userid/roleid" and on the controller side doing a string.Split on / to split u.ID and r.ID
I would like to know if it's possible to post it so my controller get's them in this way :
[HttpPost]
public IActionResult ChangeUserRole(int UserID, int RoleID)
Instead of this witchcraft:
[HttpPost]
public IActionResult ChangeUserRole(string Val)
{
char[] splitChar = new char[] { '/' };
string[] s = Val.Split(splitChar);
int UserID = Convert.ToInt32(s[0]);
int RoleID = Convert.ToInt32(s[1]);
}
Sorry for the long post. Hope my question makes sense.
I'm not such a big fan of html helpers.
Side note:
I'm using MVC 6, ASP 5 - RC1
Appreciate the help
Cheers!
The best solution is to use the TagHelpers to build your dropdown. Let's start by creating a view model specific to this view.
public class UserRoleEditVm
{
public List<SelectListItem> Roles { set; get; }
public int RoleId { set; get; }
public int UserId { set; get; }
}
In your get action, create an object of this, load the property values and send it to the view.
public IActionResult Create()
{
// User Id and Role list is hard coded for demo. You may replace it with real data.
var v = new UserRoleEditVm {UserId = 45};
v.Roles = new List<SelectListItem>
{
new SelectListItem {Value = "1", Text = "Admin"},
new SelectListItem {Value = "2", Text = "Editor"},
new SelectListItem {Value = "3", Text = "Reader"}
};
return View(v);
}
And in your view, which is strongly typed to our view model, we will like Tag helpers to for creating the HTML markup.
#model UserRoleEditVm
<form asp-action="ChangeUserRole" asp-controller="Account">
<select asp-for="RoleId" asp-items="#Model.Roles">
<option>Please select one role</option>
</select>
<input type="hidden"asp-for="UserId"/>
<input type="submit"/>
</form>
And in your HttpPost action method, you can use an object of our view model as the parameter and the Model binder will map the posted form values to property values of that object.
[HttpPost]
public ActionResult ChangeUserRole(UserRoleEditVm model)
{
var userId = model.UserId;
var roleId = model.RoleId;
// to do : Do something with the above 2 values
// to do :Save and redirect (PRG pattern)
// return RedirectToAction("Success");
}

MVC 4 HTML is never decoded on POST

I am using a Kendo editor to create email templates and on POST, once a change to the template has been submitted, always renders in encoded HTML.
This is my razor code on the page:
#model Business.Models.Administration.EmailSetupViewModel
#using Kendo.Mvc.UI;
<h2>Application Stages Portal</h2>
<h4>Email Setup</h4>
#using (Html.BeginForm())
{
if (Model.EmailSaved)
{
<h2>
Email template saved</h2>
}
else
{
#* #Html.DisplayFor(m => m.EmailSavedMsg)*#
}
#Html.DropDownListFor(m => m.EmailTemplateToEdit, Model.EmailTemplatesList)
<input type="submit" name="setup" value="setup" />
if (Model.ShowEmailForm)
{
<div id="email-edit">
#Html.Label("Title")
#Html.TextBoxFor(m => m.EmailTitle, new { style = "width:200px" })
<br />
#(Html.Kendo().Editor()
.Name("editor")
.HtmlAttributes(new { style = "width: 600px;height:440px" })
.Value(#<text>
#Html.Raw(Model.EmailBody)
</text>))
</div>
<input type="submit" id="btnSaveTemplate" name="update" value="update" />
<h2>
Please note</h2>
<p>
The following items are <i>reserved and should not be changed, you may move them
to a different place within the message. </i>
<ul>
<li><*name*> e.g. Fred Flinstone </li>
<li><*membernumber*> e.g. 12345678 </li>
</ul>
</p>
}
}
And this is where the actual editor markup is on the page
#(Html.Kendo().Editor()
.Name("editor")
.HtmlAttributes(new { style = "width: 600px;height:440px" })
.Value(#<text>
#Html.Raw(Model.EmailBody)
</text>))
Model.EmailBody contains the actual string.
When I GET the page, it renders fine. But when I do POST it never decodes so the rendering is wrong. I don't want to see all the HTML tags but the actual formatting.
This is my Controller code:
#region Email template
[HttpGet]
public ActionResult EmailSetup()
{
ViewBag.DisplayName = StaticFunctions.GetDisplayName(this.User.Identity.Name);
EmailSetupViewModel model = new EmailSetupViewModel();
Business.Administration.Email Email = new Business.Administration.Email();
var list = Email.GetTemplateList();
model.EmailTemplatesList = list.OrderBy(o => o.Text).ToList();
return View(model);
}
[HttpPost]
public ActionResult EmailSetup(EmailSetupViewModel model, string value, string editor)
{
ViewBag.DisplayName = StaticFunctions.GetDisplayName(this.User.Identity.Name);
string body = HttpUtility.HtmlDecode(editor); //encode to db
if (Request["update"] != null)
{
Business.Administration.Email Email = new Business.Administration.Email();
model.EmailSaved = Email.SaveTemplate(model, body);
//ModelState.Clear(); // when doing POST - clearing the ModelState will prevent encode of HTML (Default behaviour). This isn't good long term solution.
if (model.EmailSaved)
{
model.EmailSavedMsg = "Template saved";
}
else
{
model.EmailSavedMsg = "Template couldn't be saved";
}
model.EmailTemplatesList = Email.GetTemplateList();
model = Email.GetTemplate(model);
model.EmailBody = HttpUtility.HtmlDecode(model.EmailBody);
return View(model);
}
else
{
Business.Administration.Email Email = new Business.Administration.Email();
model.EmailTemplatesList = Email.GetTemplateList();
model = Email.GetTemplate(model);
model.EmailBody = HttpUtility.HtmlDecode(model.EmailBody);
return View(model);
}
}
#endregion
This is my model, I am using [AllowHtml] attribute on the property.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
namespace Business.Models.Administration
{
public class EmailSetupViewModel
{
public EmailSetupViewModel()
{
this.EmailTemplatesList = new List<SelectListItem>();
}
public string EmailTemplateToEdit { get; set; }
public List<SelectListItem> EmailTemplatesList { get; set; }
public string EmailTitle { get; set; }
[AllowHtml]
public string EmailBody { get; set; }
public bool ShowEmailForm { get; set; }
public bool EmailSaved { get; set; }
public string EmailSavedMsg { get; set; }
}
}
Finally two screenshots, one on GET and one on POST.
I was using ModelState.Clear() as well but when I clicked back on the browser, it wouldn't decode.
So basically I want help rendering the HTML in my editor on post so it renders properly and doesn't show HTML tags in the editor.

How can I override the name attribute of a RadioButtonFor?

I'm trying to group together radiobuttons that are creating using a for loop and Razor syntax. Here is the code:
#for (var i = 0; i < Model.Sessions.Count(); i++)
{
#Html.HiddenFor(it => it.Sessions[i].Id)
#Html.RadioButtonFor(it => it.Sessions[i].Checkbox, "0", new {#class = "Sessions", #id = id, #name="Sessions"})
#Html.LabelFor(it => it.Sessions[i].Name, Model.Sessions[i].Name)
<span class="time-span"><em>#Model.Sessions[i].StartTime</em><em>#Model.Sessions[i].EndTime</em></span>
<br />
}
The third line inside the for loop is where the problem is. Basically the name doesn't change and it's always "Sessions[x].Checkbox". The checkbox is a property (bool) of a custom class. I can't seem to get the hang of debugging Razor stuff, so any help would be greatly appreciated, I'm guessing this will be extremely obvious to someone here.
EDIT
Dimitrov's post helped a lot. Below is the final code I used. I use the #class and #id attributes to be able to use Javascript to select the session originally picked (since this is an edit, not create form).
#for (var i = 0; i < Model.Sessions.Count(); i++)
{
#Html.HiddenFor(it => it.Sessions[i].Id)
var SId = #Model.Sessions[i].Id;
#Html.RadioButtonFor(it => it.selectedSession, Model.Sessions[i].Id, new { id = SId, #class = "Sessions" })
#Html.LabelFor(it => it.Sessions[i].Name, Model.Sessions[i].Name)
<span class="time-span"><em>#Model.Sessions[i].StartTime</em><em>#Model.Sessions[i].EndTime</em></span>
<br />
}
If you want to be able to select only a single radio button you need to have a single property on your view model to hold the selected session id, like this:
public class SessionViewModel
{
public int SelectedSessionId { get; set; }
public IList<Session> Sessions { get; set; }
}
public class Session
{
public int Id { get; set; }
public string Name { get; set; }
}
and then have a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new SessionViewModel
{
SelectedSessionId = 2,
Sessions = Enumerable.Range(1, 5).Select(x => new Session
{
Id = x,
Name = "session" + x,
}).ToList()
};
return View(model);
}
[HttpPost]
public ActionResult Index(SessionViewModel model)
{
return Content("Thank you for selecting session id: " + model.SelectedSessionId);
}
}
and finally a view:
#model SessionViewModel
#using (Html.BeginForm())
{
for (var i = 0; i < Model.Sessions.Count(); i++)
{
#Html.HiddenFor(x => x.Sessions[i].Id)
#Html.RadioButtonFor(x => x.SelectedSessionId, Model.Sessions[i].Id, new { id = "session_" + i })
#Html.Label("session_" + i, Model.Sessions[i].Name)
<br/>
}
<button type="submit">OK</button>
}