I am using blazor webclient to display an enum value in the UI. however when I run the client and a value is assigned nothing displays. I have taken this code from the GetName method. however this displays nothing on the page.
my razor component
<div>
<p>Nationality: #Enum.GetName(typeof(Nationality), user.Nationality).ToString()</p>
<p>Favourite Medium: #Enum.GetName(typeof(Medium), user.FavouriteMedium).ToString()</p>
</div>
#code {
[Parameter]
public UserViewModel user {get; set;}
}
when the page is loaded in no error is given and the value is shown as blank. I followed this method from the documentation here.
https://learn.microsoft.com/en-us/dotnet/api/system.enum.getname?view=net-6.0
Edit.
The issue here was that there was a view model conversion point on my API that was not sending the enum value to the user model. because of this a 0 was being passed by the value where the enum values for these objects start at 1. therefore nothing was being displayed by the component where the #code block was inserted into the HTML.
thanks guys!
Not sure what your code is doing, but here's a dirty demo that shows you how to display an enum in a Blazor component.
#page "/"
<h1>Hello, your #nationality</h1>
<div>
<button class="btn btn-dark" #onclick=ChangeNationality>Change Nationality</button>
</div>
#code {
private Nationality nationality = Nationality.French;
private void ChangeNationality()
=> nationality++;
public enum Nationality
{
French,
English,
Welsh,
Portuguese,
Spanish
}
}
I found the issue to be that the enum was getting a value of 0 from the model object when the first value of enum was set to 1. therefore no value was being returned from the GetName() method.
Related
I have two RenderFragments in blazor component. One is MainContent another one is AuxilaryContent i.e. I place the AuxilaryContent at first followed by MainContent. As of now, Auxilary content rendered succeeded by MainContent because as I placed AuxilaryContent at first.
But my requirement is that I need to render MainContent first, based upon rendering of MainContent, I may render AuxilaryContent or not. But in DOM, AuxilaryContent always lies before MainContent.
Is this possible?
If I am using bool in MainContent, then by using the bool to trigger SecondaryContent means, it requires another StateHasChanged(). It involves unwanted re-rendering of components.
#page "/check"
#AuxilaryContent
#MainContent
#code {
RenderFragment MainContent => (builder) =>
{
//It must be rendered first
};
RenderFragment AuxilaryContent => (builder) =>
{
//It should rendered after MainContent rendering. But in DOM, it always lies before MainContent
};
}
Suppose you have the 2 components, main and aux.
The second aux can be shown or not by the result of the main one ,thats what i understand.
First of all Blazor is a spa (single-page application) it means that everything is in fact in the same page i mean,suppose you start by the index of the page , it contains all the components of the blazor proyect.
The components inside can contain or not others.
For example, suppose a collection of books:
The index page will be more less like that:
#page "/"
<book1_component></book1_component>
<book2_component></book2_component>
<book3_component></book3_component>
Inside of each component you can put another component if u want.
<h1>book1</h1>
<label>title</label> <input type="text" />
<label>author</label> <input type="text" />
<other_component> </other_component>
#code{
//methods
}
The interesting thing about blazor is that you should work with it as you have State-Patron:
You can check here what its a state patron:
https://refactoring.guru/es/design-patterns/state
What i mean is if the state changes, then the behavior of blazor will change too.
Example: Here you can see that if the state of the index component change , there will be one or other components or even none.
#page "/"
<input type="int" max="4" min="1" #bind="state" />
#if( state == 1 )<book1_component></book1_component>
#if( state == 2 )<book2_component></book2_component>
#if( state == 3 )<book3_component></book3_component>
#if(i>4) <label> no books!</label>
#code{
int state;
}
So in your example you should do more less the same , for example, if a condition is true then you show, your component.
So if you want to refresh some component with statehaschange, you can do it in its own .razor page .
#page "/"
<InputCheckbox #bind="state" />
#if( state) <aux_component></aux_component>
else Not loading component.
#code{
bool state;
//operations to change the state
}
Other way ,can be : If you want to keep the logic on only one component, you can use the lifecycle methods:
https://learn.microsoft.com/es-es/aspnet/core/blazor/components/lifecycle?view=aspnetcore-5.0
So if you want to execute some code first put onInitiallize, then go to the next lifecycle method and execute second block by a condition, something like that.
#page "/check"
#AuxilaryContent
#MainContent
#code {
protected override void OnInitiallize(){
//executing the first
RenderFragment MainContent => (builder) =>
{
//It must be rendered first
};
}
protected override void OnParametersSet(){
//main component rendered
RenderFragment AuxilaryContent => (builder) =>
{
//It should rendered after MainContent rendering. But in DOM, it always lies before MainContent
};
}
}
Hope it helps.
I would consider restructuring this component into two nested components: main container controlling the conditional rendering of the aux one.
So I have this form with this inside it
<div class="form-group">
#*<div class="form-check form-check-box">
#for (int i = 0; i < Model.Features.Count; i++)
{
<div class="form-check form-check-box">
<input class="form-check-input" type="checkbox" asp-for="#Model.Features[i].Selected" value="#Model.Features[i].Text">
<label class="form-check-label" asp-for="#Model.Features[i].Text">#Model.Features[i].Text</label>
</div>
}
</div>
And when I post the form and inspect the properties of the SelectedListItem it looks like this
Even though the Selected should be True because I checked it. Everything else that's in the form gets posted fine, like the text input binds just fine to the string Text property inside my ProductModel, I believe the issue is with the way I'm using the checkbox element properties.
So I can see the checkboxes, and I can check them, etc, but when I post the form, nothing related to the checkboxes applies to the ProductModel
public class ProductModel
{
public string Title { get; set; }
public IList<SelectListItem> Features { get; set; }
}
This is how I show set the Model for the view
public IActionResult PostAd()
{
var model = new ProductModel
{
Features = new List<SelectListItem>()
{
new SelectListItem { Text = "Camera", Value = "Camera" },
new SelectListItem { Text = "Touch Screen", Value = "Touch Screen" }
}
};
return View(model);
}
How do I properly use checkboxes so that when I post the form, the data comes through to the model?
This is the action that gets fired when posting
[HttpPost]
public IActionResult CreateAd(ProductModel Product)
{
return View("Index");
}
The problem you are having is that when the form is posted, ASP.NET will bind the posted data to your model, but only input values within the form are posted. The reason your SelectListItem has null Text and Value properties is because these values are not being posted in the form. The form would need to contain an input (such as a hidden input) for #Model.Features[i].Text and another for #Model.Features[i].Value for these to be bound back to the SelectListItem during model binding because it is the input's name that binds it to a model property. But keep in mind, receiving these from even hidden inputs would enable a user to change them to any value they want so you would need to validate on the server side that they are 1) valid values and 2) allowed to be selected.
Given that fact, I find it makes more sense to simply reload the list of available options, in your case Features, in your HttpPost action, then update that rebuilt list with the user's submitted selections.
Now the only problem left is you're not even getting Selected set to true. This is again, because it's based on the input's (the checkbox's) value. asp-for="#Model.Features[i].Selected" will give the checkbox the name it needs to bind back to that property, and will bind its value attribute to the value of Selected. However, you then also define your own value attribute as value="#Model.Features[i].Text" which overrides the one that would've been generated by the asp-for helper. So when your form is submitted, the model binder tries to bind "Camera" to the boolean Selected property which can't be done so it just gets it's default false value. Generally, a SelectListItem is used for a dropdown (<select>) input. There's no reason you can't use it for this purpose, but you could also just use your own model type for the checkboxes.
I have a button that when clicked, will change the value of the variable _name. But this doesn't work when I click Change Name then Refresh.
View
#{
var _name = ViewContext.RouteData.Values["name"].ToString();
}
<div>
<h1>Hello, #name!</h1>
<button type="button" onclick="#(_name = "John Doe")">Change Name</button>
<button type="button" onclick="location.href = '#Url.Action("MyAction", new { name = _name })'">Refresh</button>
</div>
Controller
public ActionResult MyAction(string name = "default") {
ControllerContext.RouteData.Values["name"] = name;
return View;
}
On refresh, the h1 should say "Hello, John Doe!". What is the right way of setting a variable inside HTML using Razor?
I think you are going in wrong direction. do this
put the name in a property in your model
create a hidden with Html.HiddenFor for name
change the value of hidden field with javascript on onClick
when the form is posted you have the new value of name in your model and you don't need new { name = _name } any more
instead of having name in your model and hiddenfor you can use this as alternative
Html.Hidden("name", ViewData("name"))
then for your link make url by this hidden value
Also using RouteData is not a good idea for your purpose, I would recommend to use ViewData["xxx"] instead
been trying to fix this for a few days now and just cant get it to work!
i have a radio button list which determines the output of a form - one of the radio buttons is to download X amount of files. this radio button has a text box for the user to enter the amount (X) they wish to download.
i only need this textbox to validate if the radio button that corresponds to it is selected - this is what i have so far but cannot get it to work. any help would be appriciated.
MODEL
public class myClass
{
[Required(ErrorMessage = "Please select the type of output you wish to generate")]
public int providerType { set; get; }
public int? numOutput { set; get; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (providerType == 2 && numOutput == null)
yield return new ValidationResult("Description must be supplied.");
}
}
CONTROLLER
[AcceptVerbs(HttpVerbs.Post)]
[HttpPost, ValidateInput(false)]
public ActionResult Spin(myClass theData)
{
int? ProviderType = Convert.ToInt16(Request.Form["providerType"]);
if (ModelState.IsValid)
{
//other random processing
}
}
VIEW
<ul>
<li>
<%=Html.RadioButton("ProviderType","1")%><label>Output A Single Article (With Visible HTML Tags)</label>
</li>
<li>
<%=Html.RadioButton("ProviderType","4")%><label>Output A Single Article (With HTML Pre Rendered - Not Recommended For Articles With Videos)</label>
</li>
<li>
<%=Html.RadioButton("ProviderType", "3")%><label>Output For Mass Submission</label>
</li>
<li>
<%=Html.RadioButton("ProviderType", "2")%><label>Download Several Copies Of Article (With Visible HTML Tags)</label>
How Many Artilces (Max 20)
<%= Html.TextBoxFor(Model => Model.numOutput)%>
<%= Html.ValidationMessageFor(Model => Model.numOutput)%>
im still new to MVC so im probably doing something stupid here - any help is much appriciated. The error im getting is that when i select the radio button and dont enter anything in the textbox "Input string was not in a correct format." so obviously the validation is not firing - cant quite figure out why.
The reason why its not hitting Validation is because your model is not implementing IValidatable Object.
e.g. public class MyClass:IValidatableObject
Let me know if that works for you. I was able to get the above code working locally and can send you the code if it still doesnt work for you.
Scenario
I have a parent/child model (to be exact a small questionnaire form and a one or more number of contacts). For historic reasons, all of this would have been done on the same form so user would have a form for the parent and one child and they would hit a button to add more children. Child has a few standard fields and the same with the parent, nothing fancy. Main requirement is that the data must not touch the database until all is valid and setup while I would have to go back to server for adding deleting children.
Implementation
It was very quick to get this working in ASP.NET MVC (using MVC 2 with VS 2010). I got two models, one for parent and one for the child and got only one controller. Controller has a Create Method which is a get and gets a default view with a fresh brand new parent containing one child. I use editor template for the child model which works nicely.
I have one HTML form which has a "save" and "add child" and I have "delete" button for each form. Since this cannot be stored in database, I store the temp model in the form itself and it goes back and forth between browser and server. Perfromance is not much of an issue here but the cost of development since there are quite a few of these forms - so please do not get distracted too much by suggesting an alternative approach although I appreciate comments anyway.
In order to find out which child to delete, I create temp GUID Ids and associate them with the child. This will go onto the HTML input's value for delete button (usual trick when you have multiple actions and the same form).
I have disabled caching.
Issue
Please have a look at the snippets below. I have debugged the code and I have seen always correct GUID being passed, correct item removed from the list in the controller and correct items being rendered in the template. BUT ALWAYS THE LAST ONE GETS DELETED!! I usually click the first delete and can see that the last gets deleted. I carry on and first item is the last being deleted.
Controller
public ActionResult Create()
{
EntryForm1 entryForm1 = new EntryForm1();
entryForm1.Children.Add(new Child("FILL ME", "FILL ME"){ TempId = Guid.NewGuid()});
return View("EntryForm1View", entryForm1);
}
[HttpPost]
public ActionResult Create(EntryForm1 form1, FormCollection collection, string add)
{
if (add == "add")
form1.Children.Add(new Child("FILL ME", "FILL ME") {TempId = Guid.NewGuid()});
var deletes = collection.AllKeys.Where(s => s.StartsWith("delete_"));
collection.Clear();
if (deletes.Count() > 0)
{
string delete = deletes.FirstOrDefault();
delete = delete.Replace("delete_", "");
Guid g = Guid.Parse(delete);
var Children = form1.Children.Where(x => x.TempId == g).ToArray();
foreach (Child child in Children)
{
form1.Children.Remove(child);
}
// HERE CORRECT ITEM IS DELETED, BELIEVE ME!!
}
if (ModelState.IsValid)
{
return Redirect("/");
}
return View("EntryForm1View", form1);
}
View snippet
<% for (int i = 0; i < Model.Children.Count;i++ )
{%>
<h4> <%: Html.EditorFor(m=>m.Children[i])%></h4>
<%
}%>
<p>
<input type="submit" value="Create" name="add" />
<input type="submit" value="add" name="add" />
</p>
Child Editor template snippet
<%: Html.HiddenFor(x=>x.TempId) %>
</span>
<input type="submit" name='delete_<%: Html.DisplayTextFor(m => m.TempId) %>' value="Delete" />
Many thanks for your time and attention
UPDATE
I was asked for model classes and I am sharing them as exactly as they are.
Entryform1 is the parent and Somesing is the child.
public class Somesing
{
public Somesing()
{
}
public Somesing(string o, string a) : this()
{
OneSing = o;
AnozerSing = a;
}
[StringLength(2)]
public string OneSing { get; set; }
[StringLength(2)]
public string AnozerSing { get; set; }
public Guid TempId { get; set; }
}
public class EntryForm1
{
public EntryForm1()
{
Sings = new List<Somesing>();
}
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public List<Somesing> Sings { get; set; }
}
I believe that problem lies with ModelState. When the view gets rendered, which I assume is where the issue lies, after the POST, the last value is not displayed i.e. removed from the view.
The issue is that Model.Children.Count will return the correct number of elements to display.
Lets break this down...
So if you have initially had 5 then removed the first one which is at index 0 based on the Guid, you now have items 4 items left with indexes 1 to 4.
However, when rendering the view after the post, the HtmlHelpers do not look at the values in model posted, but rather the values contained within the ModelState. So in the ModelState, item with index 0 still exists and since the loop is now looping to 4, the last element will not be displayed.
The solution, use ModelState.Clear()
OK, as Ahmad pointed out, ModelState is the key to the issue. It contains the collection as such:
FirstName
LastName
...
Sings[0].OneSing
Sings[0].AnozerSing
Sings[1].OneSing
Sings[1].AnozerSing
Sings[2].OneSing
Sings[2].AnozerSing
Now if I delete item 0 from the list, now the items will move up in the list and the data in the ModelState will go out of sync with the model. I had expected ASP.NET MVC to be clever enough to find out and re-order, but well that is asking for too much.
I actually implemented PRG (post-redirect-get) and by keeping the model in session, I was able to display correct information but again, this will remove all the validation in the collection and if model itself is valid, it will happily save and redirect back to home "/". Clearly this is not acceptable.
So one solution is to remove all items in the ModelState and then add a new entry for the model itself (with key of EmptyString). This can actually work alright if you populate it with error "Item deleted" as this will be displayed in the validation summary.
Another solution is to manually change the items in the model state and re-arrange them based on the new indexes. This is not easy but possible.
ModelState.Clear() will Solved this problem.
ModelState.Clear() is used to clear errors but it is also used to force the MVC engine to rebuild the model to be passed to your View.