Running VS2019 16.8.0, .Net SDK from 3.1.302 to 5.0.100 - Blazor Server project
I have a razor form and the compiler does not recognize 'EditForm', 'InputText' etc.
Here is the code:
page "/GetCustomerProfile"
#using QuoteBL.Data
#inject IJSRuntime JSRuntime
<h1>Please fill out form</h1>
<EditForm Model="srcfields" OnValidSubmit="#InvokeGetQuote">
<p>
<label for="age">Age:</label>
<InputText id="age" #bind-Value="QuoteBLModel.age1" />
</p>
<p>
<label for="smoke">Do you smoke?</label>
<InputCheckbox id="smoke" #bind-Value="AllFieldsModel.smoker" />
</p>
<p>
<label>
Sex:
<InputSelect #bind-Value="QuoteBLModel.sex1">
<option value="Select Sex"></option>
<option value="Male">Male</option>
<option value="Female">Female</option>
</InputSelect>
</label>
</p>
</EditForm>
<div>
<span id="resultjson"></span>
</div>
#code {
private AllFieldsModel scrfields = new AllFieldsModel();
private async Task InvokeGetQuote()
{
// await etc...
}
}
=================================
Model has 2 classes
namespace QuoteBL.Models
{
public class QuoteBLModel
{
public static string productLine = "QoLFlexTerm";
public static string display = "101";
public static string state = "";
public static string sex1 = "";
public static string age1 = "";
public static string face_Amount = "";
public static string premMode = "3";
public static string flatAmount1 = "0";
public static string tableRating1 = "0";
public static string command = "submit";
}
public class AllFieldsModel
{
public static string CustPhone = "";
public static string CustEmail = "";
public static string age1 = "";
public static string sex1 = "";
public static string state = "";
public static bool smoker = false;
public static string name = "";
public static string address = "";
public static string city = "";
public static string state = "";
public static string zip = "";
public static string loan = "";
public static string agentPhone = "";
public static string agentEmail = "";
}
====
I know that I must be doing something wrong. The answers that I saw pertained to a different situation.
Help is appreciated
Related
I was needing a way to display certain options on the Blazor NavMenu according to the webpage i was in.
There is a similar question on StackOverflow that helped me out archieving this
Exchange Data between Page and NavMenu in Blazor
But I wanted a specific topic on how to do this(Dynamic NavMenu according to the page) and i'll post my solution to get some feedback if this is the correct way of doing it and also to help others with the same doubt. The following article helped me a lot too:
https://chrissainty.com/3-ways-to-communicate-between-components-in-blazor/
So, my solution is the following.
First i created a class to manage the content of NavManeu and allow communicatin between the pages and NavMenu
public class NavMenuState
{
//The items for the NavMenu that will have the display name[0] and url[1] in a list of a string array
public List<string[]> MenuItems { get; private set; } = new();
//The last location that was clicked
public string Location { get; private set; } = "";
public event Action OnChange;
//Allow pages to set neu menu items for that page
public void SetNewMenu(List<string[]> newMenuItems)
{
MenuItems = newMenuItems;
NotifyStateChanged();
}
//Alow pages to add items to the menu
public void AddMenuItem(string[] newMenuItem)
{
MenuItems.Add(newMenuItem);
NotifyStateChanged();
}
//Triggred when clicked in a navmenu item
public void Navigation(string url)
{
//Check if the location if different, otherwise keep the same apearance
if (url!=Location)
{
Location = url;
List<string[]> vs = new();
string[] vs1 = new string[2];
vs1[0] = "Inicio";
vs1[1] = "";
vs.Add(vs1);
vs1 = new string[2];
vs1[0] = "Counter";
vs1[1] = "counter";
vs.Add(vs1);
vs1 = new string[2];
vs1[0] = "Fetchdata";
vs1[1] = "fetchdata";
vs.Add(vs1);
vs1 = new string[2];
vs1[0] = "Utilizador";
vs1[1] = "genericform/utilizador";
vs.Add(vs1);
MenuItems = vs;
NotifyStateChanged();
}
}
private void NotifyStateChanged() => OnChange?.Invoke();
//Creates the base menu to be displayed on the website first render
public NavMenuState()
{
Navigation("Inicio");
}
}
Then register this class as a singleton
public class Program
{
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
//Register as singleton
builder.Services.AddSingleton<NavMenuState>();
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
}
}
The NavMenu component. -> Every time I click a menu item it resets the NavMenu items(or tries to).
#using MecanicoAppSqlite.Shared
#inject NavMenuState NavState
#implements IDisposable
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">MecanicoAppSqlite</a>
<button class="navbar-toggler" #onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="#NavMenuCssClass" #onclick="ToggleNavMenu">
<ul class="nav flex-column">
#if (NavState.MenuItems != null)
{
#foreach (string[] menuItem in NavState.MenuItems)
{
<li class="nav-item px-3">
<NavLink class="nav-link" #onclick="()=>LocationChanged(menuItem[1])" href="#menuItem[1]">
<span class="oi oi-list-rich" aria-hidden="true"></span> #menuItem[0]
</NavLink>
</li>
}
}
</ul>
</div>
#code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
protected override void OnInitialized()
{
NavState.OnChange += StateHasChanged;
}
public void Dispose()
{
NavState.OnChange -= StateHasChanged;
}
public void LocationChanged(string url)
{
NavState.Navigation(url);
}
}
Then on a page where I want to make changes to NavMenu I inject there the NavMenuState as like in NavMenu component and i can change the items on the OnInitialized method or on any other method
protected override void OnInitialized()
{
string[] vs1 = new string[2];
vs1[0] = "New Item";
vs1[1] = "editarentidade";
NavState.AddMenuItem(vs1);
}
public void SetAllNewMenuButtonClick()
{
List<string[]> vs = new();
string[] vs1 = new string[2];
vs1[0] = "Fim";
vs1[1] = "";
vs.Add(vs1);
vs1 = new string[2];
vs1[0] = "Contadeiro";
vs1[1] = "counter";
vs.Add(vs1);
vs1 = new string[2];
vs1[0] = "Meteorologia";
vs1[1] = "fetchdata";
vs.Add(vs1);
vs1 = new string[2];
vs1[0] = "users";
vs1[1] = "genericform/utilizador";
vs.Add(vs1);
NavState.SetNewMenu(vs);
}
In Asp.Net MVC we can add class conditionally as following code:
<div class="choice #(Model.Active?"active":"")">
</div>
How can do this by using tagHelper and by remove else part in condition.
Ability to add a conditional css class by following tagHelper provides.
this code like AnchorTagHelper asp-route-* for add route values acts.
[HtmlTargetElement("div", Attributes = ClassPrefix + "*")]
public class ConditionClassTagHelper : TagHelper
{
private const string ClassPrefix = "condition-class-";
[HtmlAttributeName("class")]
public string CssClass { get; set; }
private IDictionary<string, bool> _classValues;
[HtmlAttributeName("", DictionaryAttributePrefix = ClassPrefix)]
public IDictionary<string, bool> ClassValues
{
get {
return _classValues ?? (_classValues =
new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase));
}
set{ _classValues = value; }
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var items = _classValues.Where(e => e.Value).Select(e=>e.Key).ToList();
if (!string.IsNullOrEmpty(CssClass))
{
items.Insert(0, CssClass);
}
if (items.Any())
{
var classes = string.Join(" ", items.ToArray());
output.Attributes.Add("class", classes);
}
}
}
in _ViewImports.cshtml add reference to taghelper as following
#addTagHelper "*, WebApplication3"
Use tagHelper in View:
<div condition-class-active="Model.Active" condition-class-show="Model.Display">
</div>
result for Active = true and Display = true is:
<div class="active show">
</div>
There's no default way to do what you're asking. You would have to write a TagHelper that did that logic for you. Aka
[HtmlTargetElement(Attributes = "asp-active")]
public class FooTagHelper : TagHelper
{
[HtmlAttributeName("asp-active")]
public bool Active { get; set; }
public override void Process(TagHelperOutput output, TagHelperContext context)
{
if (Active)
{
// Merge your active class attribute onto "output"'s attributes.
}
}
}
And then the HTML would look like:
<div class="choice" asp-active="Model.Active"></div>
So this is my first time building an Java EE application. I watched a lot of tutorials but most of them are using eclipse.
So the problem is this:
<h:panelGroup layout="block">
<p:commandButton ajax="false" action="#{loginBean.login()}"
styleClass="btn btn-info" value="Login" />
</h:panelGroup>
When I start Wildfly server and try to access the login page. If there are no
brackets after the login method I get:
The class 'LoginBean' does not have the property login.
If I try it with the brackets. The method is invoked when page is initialized and I get exception that the values for username and pass are null.
When I commented the method content I got the page to initialize properly, but another issue occured.
The JSF components like:
<h:panelGroup>
<h3 class="loginTitle">#{msgs['default.title']}</h3>
</h:panelGroup>
Are rendered correctly, but the Primefaces components
<h:panelGroup layout="block">
<p:inputText value="#{loginBean.username}" id="username"
styleClass="loginInputField" required="true"
requiredMessage="Username is required field">
<p:watermark for="username" value="Username" />
</p:inputText>
</h:panelGroup>
Are rendered with 0px height and width.
Here is my LoginBean.java
public class LoginBean implements Serializable {
private static final String SUCCESS_LOGIN_REDIRECT = "/pages/success?faces-redirect=true";
private static final String LOGIN_PAGE_REDIRECT = "/pages/login?faces-redirect=true";
private static final long serialVersionUID = 1L;
#Inject
private HttpServletRequest request;
private String username;
private String password;
#PostConstruct
public void init() {
//TODO
}
public String login() {
username = "";
password = "";
if(StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Invalid Credentials"));
return "";
} else if ("admin".equals(username) && "123".equals(password)) {
request.getSession().setAttribute("LOGGED_USER", username);
return SUCCESS_LOGIN_REDIRECT;
}
MessageUtils.addErrorMessage("login.error.invalid.credentials");
return "";
}
public String logout() {
request.getSession().invalidate();
return LOGIN_PAGE_REDIRECT;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
And last here is my project structure https://github.com/M-Veselinov/Java-EE
I think that I'm doing something wrong with web.xml or other config files, but I have no idea what.
I'll appreciate some help. Thanks.
Client Source:
<p:inputtext id="username" styleclass="loginInputField" required="true" requiredmessage="Username is required field">
<p:watermark for="username" value="Username"></p:watermark>
</p:inputtext>
With RC1 you could construct a new TagBuilder, and just append that to the output of a custom TagHelper as such:
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var indicator = new TagBuilder("span");
indicator.AddCssClass("indicator");
output.Content.Append(indicator);
}
That now fails with RC2, as TagHelperOutput.Content.Append() only accepts a string.
I can mess about with TagBuilder.WriteTo(), but that seems overly complex compared to how it was.
Is there a new way to construct new tags and append to the output that I've missed?
In RC2 you can use
output.Content.AppendHtml(tag);
Sample of menu tag helper:
view:
<ul>
<menu action="Index" controller="Home">Home page</menu>
<menu action="List" controller="Home">List</menu>
</ul>
MenuTagHelper.cs:
[HtmlTargetElement(Attributes = "controller, action")]
public class MenuTagHelper : TagHelper
{
public string Controller { get; set; }
public string Action { get; set; }
[ViewContext]
public ViewContext ViewContext { get; set; }
private readonly IUrlHelperFactory urlHelperFactory;
public MenuTagHelper(IUrlHelperFactory urlHelperFactory)
{
this.urlHelperFactory = urlHelperFactory;
}
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var sb = new StringBuilder();
var urlHelper = urlHelperFactory.GetUrlHelper(ViewContext);
var url = urlHelper.Action(Action, Controller);
var text = (await output.GetChildContentAsync()).GetContent();
output.TagName = "li";
var a = new TagBuilder("a");
a.MergeAttribute("href", $"{url}");
a.MergeAttribute("title", text);
a.InnerHtml.Append(text);
var routeData = ViewContext.RouteData.Values;
var currentController = routeData["controller"];
var currentAction = routeData["action"];
if (string.Equals(Action, currentAction as string, StringComparison.OrdinalIgnoreCase) && string.Equals(Controller, currentController as string, StringComparison.OrdinalIgnoreCase))
output.Attributes.Add("class", "active");
output.Content.AppendHtml(a);
}
}
I am trying to upload a file (picture) which is part of a Product object to a database. The file upload is successful according to the jsf confirmation. However, when persisting Product the persistence is completed successfully without the picture. The server is not returning any stack.
This is my form (simplified):
<h:form enctype="multipart/form-data">
<h1><h:outputText value="Create New Product"/></h1>
<h:panelGrid columns="2">
<h:outputLabel value="Name:" for="name" />
<h:inputText id="name" value="#{productController.product.name}" title="Name" />
<h:outputLabel value="Description:" for="description" />
<h:inputText id="description" value="#{productController.product.description}" title="Description" />
<h:outputLabel value="Price:" for="price" />
<h:inputText id="price" value="#{productController.product.price}" title="Price" />
<h:outputLabel value="Category:" for="category_fk" />
<h:selectOneMenu id="category_fk" value="#{productController.product.category_fk}"
converter="#{categoryConverter}" title="Category_fk" >
<f:selectItems value="#{productController.categoryList}" var="prodCat"
itemValue="#{prodCat}" itemLabel="#{prodCat.name}"/>
</h:selectOneMenu>
<p:fileUpload fileUploadListener="#{productController.handleFileUpload}" update="msg" auto="true" />
<p:growl id="msg" showDetail="true"/>
<h:inputHidden id="dateAdded" value="#{productController.product.dateAdded}">
<f:convertDateTime pattern="yyyy/MM/dd HH:mm:ss" />
</h:inputHidden>
</h:panelGrid>
<h:commandButton value="Create Product" action="#{productController.doCreateProduct()}"/>
</h:form>
This the product Controller (Simplified):
#ManagedBean
#RequestScoped
public class ProductController {
#EJB
private ProductEJB productEjb;
#EJB
private CategoryEJB categoryEjb;
private Product product = new Product();
private List<Product> productList = new ArrayList<Product>();
private Category category;
private List<Category> categoryList = new ArrayList<Category>();
// -------------------------------------------------------- Business Methods
public String doCreateProduct()
{
product = productEjb.createProduct(product);
productList = productEjb.findAllProducts();
return "listProduct?faces-redirect=true";
}
public String doDeleteProduct()
{
productEjb.deleteProduct(product);
return "listProduct?faces-redirect=true";
}
public String cancelDeleteAction()
{
return "listProduct?faces-redirect=true";
}
// Update product listing
public void preRenderView()
{
if(product == null)
{
product = new Product();
}
}
public String doUpdateProduct()
{
if(product.getProduct_id() != 0)
{
productEjb.updateProduct(product);
}
else
{
productEjb.createProduct(product);
}
//addFlashMessage("Product " + product.getName() + " (" + product.getProduct_id() + ") has been updated");
return "listProduct?faces-redirect=true";
}
public void handleFileUpload(FileUploadEvent event)
{
byte[] uploadedFile = new byte[event.getFile().getContents().length];
System.arraycopy(event.getFile().getContents(), 0, uploadedFile, 0, event.getFile().getContents().length);
product.setImageFile(uploadedFile);
FacesMessage msg = new FacesMessage("Succesful", event.getFile().getFileName() + "is uploaded.");
FacesContext.getCurrentInstance().addMessage(null, msg);
}
#PostConstruct
public void init()
{
categoryList = categoryEjb.findAllCategory();
productList = productEjb.findAllProducts();
}
This is the product entity(Simplified):
#Entity
#NamedQueries({
#NamedQuery(name="findAllProducts", query = "SELECT p from Product p")
})
public class Product implements Serializable
{
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy= GenerationType.AUTO)
private int product_id;
private String name;
private String description;
#Lob
#Column(name="imageFile")
protected byte[] imageFile;
private Float price;
#Temporal(TemporalType.TIMESTAMP)
private Date dateAdded;
#ManyToOne
private Category category_fk;
#OneToOne(mappedBy = "product_fk")
private SaleDetails saleDetails_fk;
SOLUTION:
I changed the scope of the managed bean to:
#ViewScope
Fileupload happens in a first request.
When form is submited with the input data, a new 2nd request will be initiated. A new requestScoped ManagedBean will be created, that doesnt know about the previous fileupload.
To allow the two requests to share the same Managedbean, Change the scope to:
#ViewScoped