DataAnnotations attributes on custom control - asp.net-mvc-4

I've used a custom control (HTML Helper) to build an Autocomplete controller.
it works great, the only thing is the validation problem.
on the client side, the validation works fine when jquery.validation.js is out of the picture, (for empty text box it gives an error message).
if the user selects something from the autocomplete, so im fine.
but when the user input is just junk, then the HttpPost needs to handle the junk & return an error message to the user.
HOW??
also, i've seen a DataAnnotation called Remote, which can manage the validation on the client side, is it better ? if so, how can i add DataAnnotaion on a custom control ??
Thank's :)
here is my code:
Index.cshtml
#using (Html.BeginForm("Index", "Create"))
{
#Html.AutocompleteFor(Url.Action("AutoCompleteServiceProviders", "Create"), true, "ex. Shower", c => c.service_id, a => a.name)
<input type="submit" id="search" value="" />
}
AutoComplete.cs
private static MvcHtmlString CreateAutocomplete<TModel>(this HtmlHelper<TModel> helper, string actionUrl, bool? isRequired, string placeholder, params Expression<Func<TModel, object>>[] expression)
{
var builder = new StringBuilder();
foreach (var item in expression)
{
var attributes = new Dictionary<string, object>
{
{ "data-autocomplete", true },
{ "data-action", actionUrl }
};
if (!string.IsNullOrWhiteSpace(placeholder))
{
attributes.Add("placeholder", placeholder);
}
if (isRequired.HasValue && isRequired.Value)
{
attributes.Add("required", "required");
}
Func<TModel, object> method = item.Compile();
var value = (Object)null;
if ((TModel)helper.ViewData.Model != null)
{
value = method((TModel)helper.ViewData.Model);
}
var baseProperty = (string)null;
var hidden = (MvcHtmlString)null;
if (item.Body is MemberExpression)
{
baseProperty = ((MemberExpression)item.Body).Member.Name;
hidden = helper.Hidden(baseProperty, value);
attributes.Add("data-value-name", baseProperty);
}
else
{
var op = ((UnaryExpression)item.Body).Operand;
baseProperty = ((MemberExpression)op).Member.Name;
hidden = helper.Hidden(baseProperty, value);
}
attributes.Add("data-value-id", "service_id");
var automcompleteName = baseProperty + "_autocomplete";
var textBox = (MvcHtmlString)null;
if (value != null)
{
textBox = helper.TextBox(automcompleteName, value, string.Empty, attributes);
}
else
{
textBox = helper.TextBox(automcompleteName, null, string.Empty, attributes);
}
builder.AppendLine(hidden.ToHtmlString());
if (baseProperty == "name")
{
builder.AppendLine(textBox.ToHtmlString());
}
}
return new MvcHtmlString(builder.ToString());
}

You can get your validation from here:
var validation = htmlHelper.ValidationMessageFor(expression, null, new Dictionary<string, object>());
UPDATE:
I use TagBuilder to create tags. What I do with tagbuilder is add that validation to a span or div tag and let the unobtrusive javascript hide/show it when needed. It returns an MVCHtmlString you can just append it to the element you want to display it in

Related

Deleting one object from CartItems in razor pages

i have some products in my Cart via cookies, now i want to select and delete them from cart,
public class CartModel : PageModel
{
public List<CartItem> CartItems;
public const string CookieName = "cart-items";
public void OnGet()
{
var serializer = new JavaScriptSerializer();
var value = Request.Cookies[CookieName];
CartItems = serializer.Deserialize<List<CartItem>>(value); //error accurred in this line
foreach (var item in CartItems)
item.TotalItemPrice = item.UnitPrice * item.Count;
}
public IActionResult OnGetRemoveFromCart(long id)
{
var serializer = new JavaScriptSerializer();
var value = Request.Cookies[CookieName];
Response.Cookies.Delete(CookieName);
var cartItems = serializer.Deserialize<List<CartItem>>(value);
var itemToRemove = cartItems.FirstOrDefault(x => x.Id == id);
cartItems.Remove(itemToRemove);
var options = new CookieOptions { Expires = DateTime.Now.AddDays(2) };
Response.Cookies.Append(CookieName, serializer.Serialize(cartItems), options);
return RedirectToPage("/Cart");
}
until i don't click on the delete button, everything is ok, i don't have any error in OnGet on Cart Razor page. but when i click on the delete button and OnGetRemoveFromCart's handler is executed,CartItems is null on OnGet!
the errorr: 'Object reference not set to an instance of an object.CartItems was null.'
Response.Cookies.Delete(CookieName);
You delete the cookie in the OnGetRemoveFromCart handler, so value becomes null in the OnGet handler. You should always check for null before accessing cookie values:
public void OnGet()
{
var serializer = new JavaScriptSerializer();
var value = Request.Cookies[CookieName];
if(value is not null)
{
CartItems = serializer.Deserialize<List<CartItem>>(value);
foreach (var item in CartItems)
{
item.TotalItemPrice = item.UnitPrice * item.Count;
}
}
}

Why are my swagger docs showing 'additionalProperties = false' for my custom schema filter?

I have this SchemaFilter in my swagger config
public class SmartEnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (!TryGetSmartEnumValues(context.Type, out var values))
{
return;
}
var openApiInts = new OpenApiArray();
openApiInts.AddRange(values.Select(x => new OpenApiInteger(x.Value)));
schema.Type = "integer";
schema.Enum = openApiInts;
schema.Properties = null;
schema.Description = string.Join(", ", values.Select(v => $"{v.Value}: {v.Name}"));
}
}
It is working well, but for some reason this "additional properties" always appears in the docs:
But only for this particular schema - all the other schemas don't have it. Is there some way of removing it?
I tried setting both
schema.AdditionalProperties = null;
schema.AdditionalPropertiesAllowed = false;
but it made no difference

Sending emaills with template MVC using Razor

I want to send some mails from my site.
I've created a template: OrderPlacedEmail.cshtml
#model OnlineCarStore.Models.PurchaseVM
<h1>Order Placed Email Notification</h1>
<p>#Model.Comments</p>
Dear #Model.Name,
<h2>Thank you.</h2>
<p>
You’ve made a purchase on #Model.Comments
</p>....and so on...
I've created a view model, and I use it like this:
var template = Server.MapPath("~/Templates/OrderPlaced.cshtml");
var viewModel = new PurchaseVM
{
GuId = new Guid(guidValue),
Name = name,
Address = address,
Phone = phone,
Email = email,
Comments = comments,
Date = DateTime.Now,
CartList = cartList
};
var body = Razor.Parse(template, viewModel);
As I understood, the Razor.Parse method, should replace all the details from my template with the values from view model. But, the body gets the value of the location of the template, as you can see below:
Can you please advise what I'm doing wrong.
If you wish there is a helper that i use
public static class HtmlOutputHelper
{
public static string RenderViewToString(ControllerContext context,
string viewPath,
object model = null,
bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
if (viewEngineResult == null)
throw new FileNotFoundException("View cannot be found.");
// get the view and attach the model to view data
var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view,
context.Controller.ViewData,
context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
}
On your controller
var viewModel = new PurchaseVM
{
GuId = new Guid(guidValue),
Name = name,
Address = address,
Phone = phone,
Email = email,
Comments = comments,
Date = DateTime.Now,
CartList = cartList
};
var emailTemplate = "~/Views/Templates/OrderPlaced.cshtml";
var emailOutput = HtmlOutputHelper.RenderViewToString(ControllerContext, emailTemplate, emailModel, false);
You also can use ActionMailerNext lib from NuGet Gallery for this scenario.
public class EmailController : MailerBase
{
//...
public EmailResult OrderPlaced(Order order)
{
MailAttributes.To.Add(new MailAddress("to#email.com"));
MailAttributes.From = new MailAddress("from#email.com");
return Email("OrderPlaced", new PurchaseVM
{
//...
});
}
//...
}
You can leave your View unchanged.

Convert and save MvcHtmlString to Image or PDF

I am currently working on an application that basically builds an MvcHtmlString by mapping a HtmlTemplate with some data dynamically.
What I want to be able to do is to Convert and save this MvcHtmlString as an Image/ PDF to my local disk.
Here is my function that produces the MvcHtmlString after mapping:
public static MvcHtmlString Map(this IDictionary<string, object> row, string htmlTemplate)
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlTemplate);
foreach (var key in row.Keys)
{
var elements = htmlDoc.DocumentNode.SelectSingleNode("//body")
.Descendants()
.Where(d => d.Attributes
.Any(a => a.Name == "class" && a.Value == key));
if (elements != null && elements.Count() > 0)
{
foreach (var element in elements)
{
object attributeValue = null;
row.TryGetValue(key, out attributeValue);
if (element.HasChildNodes)
{
// We only get the first img element within the element
// as we dont expect there to be more than one <img> tag
// within a parent element
var imgChildNode = element.Descendants("img").FirstOrDefault();
if (imgChildNode != null)
{
imgChildNode.SetAttributeValue("src", attributeValue.ToString());
}
else
{
element.InnerHtml = string.Empty;
element.InnerHtml = attributeValue.ToString();
}
}
else
{
element.InnerHtml = string.Empty;
element.InnerHtml = attributeValue.ToString();
}
}
}
}
var sw = new StringWriter();
htmlDoc.Save(new StringWriter(sw.GetStringBuilder()));
var htmlString = MvcHtmlString.Create(sw.ToString());
return htmlString;
}
And then I am using this function to save an image (But this just renders a black block)
private void SaveImageFromHtml(MvcHtmlString html)
{
var decodedHtml = html.ToHtmlString();
Bitmap m_Bitmap = new Bitmap(600, 800);
PointF point = new PointF(0, 0);
SizeF maxSize = new System.Drawing.SizeF(600, 800);
HtmlRenderer.HtmlRender.Render(Graphics.FromImage(m_Bitmap), decodedHtml,
point, maxSize);
m_Bitmap.Save(#"D:\Test.png", ImageFormat.Png);
}
Any help will be appreciated!
Found the reason. The HtmlRenderer library that I was using didnt support "float" in css markup and hence the output image was messed up

Set the value of custom webpart property in c#

How to set the value of custom webpart property Programatically in C#.
SPSecurity.RunWithElevatedPrivileges(delegate()
{
using (SPSite SiteCollection = new SPSite(mySiteGuid))
{
SPWeb myWeb = SiteCollection.OpenWeb(myWebGuid);
myWeb .AllowUnsafeUpdates = true;
Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager mgr = null;
mgr = myWeb.GetLimitedWebPartManager ("default.aspx",System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
foreach (System.Web.UI.WebControls.WebParts.WebPart myWebPart in mgr.WebParts)
{
if (myWebPart.Title == "Other Webpart Name")
{
myWebPart.Visible = ! myWebPart.Visible;
myWeb.Update();
break;
}
}
}
});
I have a custom property in the webpart of type string to get the input from the user.
I wanted to updated the value of the property from c#.
Is there any way to set the value?
TIA
Try myWebPart.Update() instead of myWeb.Update().
Maybe it's a bit late for the answer, but here i let a piece of code i used for this.
var webCollection = new SPSite("http://mySharePointSite").AllWebs;
foreach (SPWeb web in webCollection)
{
var landingPageReference = #"/Pages/default.aspx";
var page = web.GetFile(landingPageReference);
if (!page.Exists)
continue;
page.CheckOut();
var spLimitedWebPartManager = web.GetLimitedWebPartManager(page.ServerRelativeUrl, PersonalizationScope.Shared);
foreach (WebPart webPartItem in spLimitedWebPartManager.WebParts)
{
if (webPartItem.Title.Equals("myWebPartTitle"))
{
// Specify Properties to change here
webPartItem.ChromeType = PartChromeType.Default;
webPartItem.Description = "AGAIN CHANGED";
// Save made changes
spLimitedWebPartManager.SaveChanges(webPartItem);
break;
}
}
page.CheckIn("Add Comment if desired");
page.Publish("Add Comment if desired");
web.Update();
web.Dispose();
}