How to create a InputText Component - razor

I'm trying to create a Input component that can:
Clear its value when press Escape.
On mousewheel change its value.
I'm creating a Input component cause i dont wanna put the same code in all Inputs elements and i need to administrate all from the same site. I have 6 days with dis issue. I'm reading and searching information that i could use but nothing help me at 100%.
#inherits Microsoft.AspNetCore.Components.Forms.InputBase<string>
#using Microsoft.AspNetCore.Components.Forms
#if (ReadOnly)
{
<InputText type="text"
tabindex="#TabIndex"
class="#Class"
id="#Id"
style="#Style"
maxlength="#MaxLength"
min="#Min"
max="#Max"
placeholder="#Placeholder"
readonly />
}
else
{
<InputText type="text"
maxlength="#MaxLength"
min="#Min"
max="#Max"
class="#Class"
tabindex="#TabIndex"
id="#Id"
style="#Style"
placeholder="#Placeholder"
#attributes="#AdditionalAttributes"
#bind-Value="#Value"
#bind-Value:event="oninput"
#onkeydown="#(e => { if (e.Code == "Escape") Value = Clear(); StateHasChanged(); })"
#onmousewheel="CaptureScroll"
/>
}
#code{
[Parameter]
public string Type { get; set; }
[Parameter]
public string Class { get; set; }
[Parameter]
public string Placeholder { get; set; }
[Parameter]
public string Id { get; set; }
[Parameter]
public string Style { get; set; }
[Parameter]
public bool ReadOnly { get; set; }
[Parameter]
public int MaxLength { get; set; }
[Parameter]
public int Min { get; set; }
[Parameter]
public int Max { get; set; }
[Parameter]
public int TabIndex { get; set; }
}
When i need to use it i just call it:
<HyperInput Type="text" Class="form-control" Id="inputName" TabIndex="1" #bind-Value="model.ModelName" />.
When i run the application trying to make this work i have this error:
Error: System.InvalidOperationException: Shared_Components.Forms.HyperInput requires a cascading parameter of type EditContext. For example, you can use Shared_Components.Forms.HyperInput inside an EditForm.
at Microsoft.AspNetCore.Components.Forms.InputBase1.SetParametersAsync(ParameterView parameters) at Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl) at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange1 oldTree, ArrayRange`1 newTree)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
I tried wrap HyperInput with a EditForm and not work.
I wish that someone could help me, i need it.
EDIT:
Now, i'm trying with a input element instead InputText and without inherit InputBase. I only added:
private string _binder;
[Parameter]
public string Binder
{
get => _binder;
set => Set(value);
}
[ParameterAttribute] public EventCallback<string> BinderChanged { get; set; }
public async void Set(string value)
{
_binder = value;
await BinderChanged.InvokeAsync(value);
}
The only thing that i can do with this component is delete with Backspace.
NOTE: Instead of use #bind-Value i'm using #bind-Binder.

You can use classic DOM events, like onkeyup and onmousewheel to make your HyperInput work in your favor. I'd strongly recommend to inherent from InputText since it makes your component much more powerful. The version I post here should you get started. It is far away from being completed.
Usage by other components
To show the usage within an EditContext and the two-way binding capabilities, I created a simple model, a display element, and a reset button
<EditForm Model="_model">
<HyperInput #bind-Value="_model.Name" Options="#(new String[]{ "ABC", "DEF","TEST","SOMETHING"})" class="form-control" />
</EditForm>
<span>#_model.Name</span>\
<button #onclick="#(() => _model.Name = "My Testname")" >Reset</button>
#code
{
public class TestModel
{
public String Name { get; set; } = "My test name";
}
private TestModel _model = new TestModel();
}
HyperInput
#inherits InputText
<input type="text" #bind-value="Value" #onkeyup="ClearInput" #onmousewheel="OnMouseWheelUp" #attributes=AdditionalAttributes />
#code {
[Parameter]
public IReadOnlyList<String> Options { get; set; }
private Int32 _index = 0;
public void ClearInput(KeyboardEventArgs args)
{
if(args.Key == "Escape")
{
//CurrentValue triggers the update process
base.CurrentValue = String.Empty;
}
}
public void OnMouseWheelUp(WheelEventArgs args)
{
Int32 nextIndex;
if(args.DeltaY > 0)
{
nextIndex = _index + 1;
}
else
{
nextIndex = _index - 1;
}
SuggestInput(nextIndex);
}
private void SuggestInput(Int32 requestedIndex)
{
if(Options == null || Options.Count == 0) { return; }
if(requestedIndex < 0 && Options.Count > 0)
{
_index = 0;
CurrentValue = Options[0];
}
else if(requestedIndex < Options.Count)
{
_index = requestedIndex;
CurrentValue = Options[requestedIndex];
}
}
}
If the escape button is pressed, CurrentValue is set to an empty string and the text box is empty. CurrentValue will trigger the update process. For the mouse wheel, there is a list of options represented by the parameter Options and a local field responsible for remembering the current position in the list.
The WheelEventArgs has a property DeltaY. If you scroll up, this value is negative. If you scroll down, the value is positive.
I make a simple "limiter" logic. If you reach the boundaries of the list (up or down), nothing happens anymore. But of course, you could change it to circular buffer or anything else you have a mind.

Related

Blazor Razor Validation Message don't display from component library

I have created a library of blazor components to be able to call the components from the app but the message validation doesn't show. At the moment, the validation is done in a InputText (it validates the format or the length of the Input) but the message or the style of the component is not shown.
The code of the component library:
CustomInputText
<input value="#Value" #oninput="OnValueChanged" placeholder=#placeholderText class="form-control i-component o-my-4" />
<ValidationMessage For="#(() => model)" />
#code {
[Parameter]
public string placeholderText { get; set; }
[Parameter]
public object model { get; set; }
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
private Task OnValueChanged(ChangeEventArgs e)
{
Value = e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
}
I import the component from a nuget package to be able to use it in the App
The App code:
<CustomInputText placeholderText="Place Holder Test" model="filterPayroll.IPF" #bind-Value="filterPayroll.IPF"/>
When I put the ValidationMessage directly in the app it works correctly, but not in the library. For the two cases, the validation linked to the "filterPayroll" class is done correctly, the difference is that in one the message is displayed and the other does not.
You need to pass the For for the Validation Summary as an expression.
CustomInputText needs to look like this:
<input value="#Value" #oninput="OnValueChanged" placeholder=#placeholderText class="form-control i-component o-my-4" />
<ValidationMessage For="#For" />
#code {
[Parameter]
public string placeholderText { get; set; }
[Parameter] public Expression<Func<string>>? For { get; set; }
[Parameter]
public string Value { get; set; }
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
private Task OnValueChanged(ChangeEventArgs e)
{
Value = e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
}
And your markup:
<CustomInputText #bind-Value="filterPayRoll.IDF" For="() => filterPayRoll.IDF" />

Pass Value in Nested Parameter in Blazor Component

How do i pass value in nested parameter. suppose i have a custom control called mycomponent
mycomponent.Razor
<label>
</label>
#code
{
[Parameter]
public TestBase Test { get; set; } = new TestBase();
}
TestBase Class
public class TestBase
{
[Parameter]
public string Cap { get; set; }
[Parameter]
public string Cap5 { get; set; }="hai"
[Parameter]
public string Cap2 { get; set; }
[Parameter]
public string Cap3 { get; set; }
}
MyPage
<mycomponent Test.Cap="my value">
</mycomponent>
Test.Cap="my value" is not working
What is the right way to pass value in nested Parameter
You should be passing TestBase into MyComponent, not trying to set a value of TestBase in MyComponent. Setting a default value for Test in MyComponent just covers the situation where the Parameter is not provided.
<mycomponent Test="MyTest">
</mycomponent>
#code {
private TestBase MyTest = new TestBase() {Cap = "Test Value"};
SomeButtonClick Event()
{
MyTest.Value = "Another Value";
}
}
I suggest you read up about components - This is a good start or MS Docs.
Update
straight answer to your question is: No you can't do what you're trying to do.

Azure Mobile Services (Windows Phone) - store complex object

I am playing around with Azure Mobile Services. Right now I am trying to store an object of my custom class in a table.
Here is a snippet from the class which represent the object which I want to store in Azure.
public class CustomItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Id { get; set; }
[JsonProperty(PropertyName = "Categorie")]
public CategorieObject Categorie { get; set; }
[JsonProperty(PropertyName = "Subcategorie")]
public SubcategorieObject Subcategorie { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
...
}
My question is how to store custom types like CategorieObject or SubCategorieObject. These classes contain a String for the name and many other properties, but I only need to store the name of the Categorie and SubcategorieObject.
Maybe you can give me a hint, to solve my problem.
Thanks!
The post at http://blogs.msdn.com/b/carlosfigueira/archive/2012/09/06/supporting-arbitrary-types-in-azure-mobile-services-managed-client-complex-types.aspx shows one way to support complex objects in azure mobile service. For your scenario, you can send the data to the server and in the insert/read/update scripts you "change" the data to only store what you need. For example, assuming that you have those types on the client:
public class CustomItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string Id { get; set; }
[JsonProperty(PropertyName = "Categorie")]
public CategorieObject Categorie { get; set; }
[JsonProperty(PropertyName = "Subcategorie")]
public SubcategorieObject Subcategorie { get; set; }
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
}
public class CategorieObject
{
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "SomethingElse")]
public string SomethingElse { get; set; }
}
public class SubcategorieObject
{
[JsonProperty(PropertyName = "Name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "SomethingElse")]
public string SomethingElse { get; set; }
}
You'd change your insert script to replace the complex object (categorie / subcategorie) with the name, which is what you want to store:
function insert(item, user, request) {
// Replace the complex object with their "Name" property.
// Omitting error / format checking here for conciseness.
item.Categorie = item.Categorie.Name;
item.Subcategorie = item.Subcategorie.Name;
request.execute({
success: function() {
// Recreate the original object
item.Categorie = { Name: item.Categorie };
item.Subcategorie = { Name: item.Subcategorie };
request.respond();
}
});
}
Likewise when updating an item you'd need to do the same:
function update(item, user, request) {
// Replace the complex object with their "Name" property.
// Omitting error / format checking here for conciseness.
item.Categorie = item.Categorie.Name;
item.Subcategorie = item.Subcategorie.Name;
request.execute({
success: function() {
// Recreate the original object
item.Categorie = { Name: item.Categorie };
item.Subcategorie = { Name: item.Subcategorie };
request.respond();
}
});
}
And the same for reading:
function read(query, user, request) {
request.execute({
success: function(results) {
results.forEach(function(item) {
item.Categorie = { Name: item.Categorie };
item.Subcategorie = { Name: item.Subcategorie };
});
request.respond();
}
});
}
Notice that any other properties in the subclasses will be lost when reading from the database (after all, you're not storing them).

Use Enum Values in DropDownListFor

This is my ViewModel:
public class EntityViewModel
{
[Required(ErrorMessage = "Title is required")]
[StringLength(255)]
[DisplayName("Title")]
public string Title { get; set; }
[Required(ErrorMessage = "Description is required")]
[DisplayName("Description")]
public string Description { get; set; }
[Required]
public DateTime StartTime { get; set; }
[Required]
public DateTime EndTime { get; set; }
[Required]
public Decimal InstantSellingPrice { get; set; }
public Nullable<Decimal> ShippingPrice { get; set; }
public Int64 Views { get; set; }
public Int32 UserId { get; set; }
public int RegionId { get; set; }
public short SelectCategoryId { get; set; }
public SelectList Categories { get; set; }
public IEnumerable<HttpPostedFileBase> Files { get; set; }
public Condition? Condition { get; set; }
}
public enum Condition
{
New=1,
Used=2
}
This is my Create Action in my Controller:
public ActionResult Create()
{
ViewBag.DropDownList = ReUzze.Helpers.EnumHelper.SelectListFor<Condition>();
var model = new ReUzze.Models.EntityViewModel
{
Categories = new SelectList(this.UnitOfWork.CategoryRepository.Get(), "Id", "Name")
};
return View(model);
}
In my Create View:
<div class="form-group">
#Html.LabelFor(model => model.Condition)
#Html.DropDownListFor(model => model.Condition, ViewBag.DropDownList as SelectList, null)
</div>
I am using the Enumhelper that you can find here.
But now I always get this error in my Create View on this rule:
#Html.DropDownListFor(model => model.Condition, ViewBag.DropDownList as SelectList, null)
The error:
Error 1 The call is ambiguous between the following methods or properties: 'System.Web.Mvc.Html.SelectExtensions.DropDownListFor(System.Web.Mvc.HtmlHelper, System.Linq.Expressions.Expression>, System.Collections.Generic.IEnumerable, string)' and 'System.Web.Mvc.Html.SelectExtensions.DropDownListFor(System.Web.Mvc.HtmlHelper, System.Linq.Expressions.Expression>, System.Collections.Generic.IEnumerable, System.Collections.Generic.IDictionary)' c:\Users\Niels\Documents\Visual Studio 2012\Projects\ReUzze\ReUzze\Views\Entity\Create.cshtml 57 30 ReUzze
I use code like this usually.
public static class Enums {
public static IList<SelectListItem> SelectListOf<TEnum>(bool empty = false)
{
var type = typeof(TEnum);
if (type.IsEnum)
{
var list = Enum.GetValues(type)
.Cast<TEnum>()
.OrderBy(x => x)
.Select(x => new SelectListItem { Text = GetDescription(x), Value = x.ToString() })
.ToList();
if (empty)
{
list.Insert(0, new SelectListItem());
}
return list;
}
return new List<SelectListItem>();
}
private static string GetDescription(object enumerator)
{
try
{
//get the enumerator type
Type type = enumerator.GetType();
//get the member info
MemberInfo[] memberInfo = type.GetMember(enumerator.ToString());
//if there is member information
if (memberInfo != null && memberInfo.Length > 0)
{
//we default to the first member info, as it's for the specific enum value
object[] attributes = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
//return the description if it's found
if (attributes != null && attributes.Length > 0)
return ((DescriptionAttribute)attributes[0]).Description;
}
//if there's no description, return the string value of the enum
return enumerator.ToString();
}
catch (Exception e)
{
return string.Empty;
}
}
}
Then you can use it like this:
Conditions = Enums.SelectListOf<Condition>();
Check out my blog post on this very subject.
http://jnye.co/Posts/4/creating-a-dropdown-list-from-an-enum-in-mvc-and-c%23
Here is an enum helper I use that turns an enum into a select list. Note: If the enum has a description (using the DescriptionAttribute) it will use that as its display text
public static class EnumHelper
{
// Get the value of the description attribute if the
// enum has one, otherwise use the value.
public static string GetDescription<TEnum>(this TEnum value)
{
var fi = value.GetType().GetField(value.ToString());
if (fi != null)
{
var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (attributes.Length > 0)
{
return attributes[0].Description;
}
}
return value.ToString();
}
/// <summary>
/// Build a select list for an enum
/// </summary>
public static SelectList SelectListFor<T>() where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null
: new SelectList(BuildSelectListItems(t), "Value", "Text");
}
/// <summary>
/// Build a select list for an enum with a particular value selected
/// </summary>
public static SelectList SelectListFor<T>(T selected) where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null
: new SelectList(BuildSelectListItems(t), "Value", "Text", selected.ToString());
}
private static IEnumerable<SelectListItem> BuildSelectListItems(Type t)
{
return Enum.GetValues(t)
.Cast<Enum>()
.Select(e => new SelectListItem { Value = e.ToString(), Text = e.GetDescription() });
}
}
Once you have this helper class in place you can do the following.
In your controller:
//If you don't have an enum value use the type
ViewBag.DropDownList = EnumHelper.SelectListFor<MyEnum>();
//If you do have an enum value use the value (the value will be marked as selected)
ViewBag.DropDownList = EnumHelper.SelectListFor(MyEnum.MyEnumValue);
In your View:
#Html.DropDownList("DropDownList")
#* OR *#
#Html.DropDownListFor(m => m.Property, ViewBag.DropDownList as SelectList, null)
if you want a quick workaround just for this one view you can do something like this. Although Khalid's approach is recommended.
<select id = 'myenumdropdown' class='something'>
#foreach(var item in Enum.GetValues(typeof('yourenumtype')))
{
<option value=item.ToHashCode()>item.ToString()</option>
}
</select>

invalid object name 'dbo.Table' - EntityFramework 4.1

I keep getting this error when i run my MVC application. The app builds fine but throws this error on the runtime.
Here is the scenario.
[DisplayColumn("Name", "Name", false)]
public partial class ApplicationAction
{
public Guid ApplicationActionId { get; set; }
[Required, StringLength(150), Column("ActionName")]
public string Action { get; set; }
[Required, StringLength(150)]
public string Controller { get; set; }
[StringLength(150)]
public string Area { get; set; }
[StringLength(250)]
public string Description { get; set; }
//Navigation Properties
public virtual ICollection<ApplicationActionGroup> ApplicationActionGroups { get; set; }
public ApplicationAction()
{
ApplicationActionId = Guid.NewGuid();
}
}
public DbSet<ApplicationAction> ApplicationActions { get; set; }
public static ApplicationAction GetAction(string actionName, string controller, string area, IEnumerable<ApplicationAction> actions)
{
foreach (ApplicationAction a in actions)
{
if (a.Action.Equals(actionName, StringComparison.InvariantCultureIgnoreCase)
&& a.Controller.Equals(controller, StringComparison.InvariantCultureIgnoreCase)
&& (
(string.IsNullOrEmpty(a.Area) && string.IsNullOrEmpty(area)) ||
(!string.IsNullOrEmpty(a.Area) && a.Area.Equals(area, StringComparison.InvariantCultureIgnoreCase))
)
)
{
return a;
}
}
return null;
}
I get an error on GetAction method. It comes up as Invalid object name 'dbo.ApplicationAction'. The sql database, there is a table called ApplicationActions. Not sure whats causing this error.
are you sure that [DisplayColumn("Name", "Name", false)]
is a valid attribute for a class?