How do I open a browser window/tab from blazor webassembly project behind code: - browser

I'm converting a UWP app to Blazor WebAssembly with ASP.NET Core hosted.
I have my markup code in Index.razor and behind code in Index.razor.cs.
In the UWP project I opened a browser window from an onclick function like this:
var success = Windows.System.Launcher.LaunchUriAsync(targetPage);
What can I use in my Blazor project onclick event, that won't lead to "unhandled error has occurred"?

You can open a tab by making use of the 'window.open' function:
#inject IJSRuntime JSRuntime;
....
await JSRuntime.InvokeAsync<string>("open", $"https://www.mysite/mypage", "_blank");
if the page is internal to your website you can use the base uri:
#inject NavigationManager Navigation
#inject IJSRuntime JSRuntime;
...
await JSRuntime.InvokeAsync<string>("open", $"{Navigation.BaseUri}/mypage", "_blank");

You case use below way
razor file
JSRuntime.InvokeVoidAsync("OpenWindow");
html file
<script>
function OpenWindow() {
window.open('https://www.google.com', 'Test', 'width=800,height=860,scrollbars=no,toolbar=no,location=no');
return false
}
</script>

These are great answers and I took it one step further to make a re-usable component. My app uses anchors and link buttons so I have a quick-and-dirty switch for each.
ExternalLink.razor
#inject IJSRuntime JSRuntime
#if (Type == "link")
{
#(Text ?? Url)
}
else if (Type == "button")
{
<button type="button" class="btn btn-link" #onclick="OpenWindow">#(Text ?? Url)</button>
}
#code {
[Parameter]
public string Url { get; set; }
[Parameter]
public string Text { get; set; }
[Parameter]
public string Type { get; set; } = "link";
private async Task OpenWindow()
{
await JSRuntime.InvokeAsync<string>("open", Url, "_blank");
}
}
Usage
<ExternalLink Url="https://www.ourwebsite.com/" Text="Visit Us!" />
<ExternalLink Url="https://stackoverflow.com/" />
<ExternalLink Url="https://duckduckgo.com/" Type="button" />

Why not just use the build in NavigationManager?
Navigation.NavigateTo(a_url, true);
The trick is to use the force parameter.
Happy coding.

Related

Posting an array of string

I am trying to post a string array to the post action in an Razor Pages project. For this, I thought about using a hidden <select> tag. The user would enter text into a text box, press a button and I would then add a new option to the <select> then post the whole thing with a submit button. However, after everything is posted, the array property of my model is empty.
Does anyone know if there is a better way of doing this or what I am doing wrong?
Razor:
<form method="post">
<input id="string-value" />
<input type="button" id="add-item" value="Add item" />
<select asp-items="#Model.Model.ArrayOfStrings" id="hidden-select"></select>
<table id="table-items">
</table>
<input type="submit" value="Submit" />
</form>
public class ArrayModel
{
public List<SelectListItem> ArrayOfStrings { get; set; } = new List<SelectListItem>();
}
public class IndexModel : PageModel
{
[BindProperty]
public ArrayModel Model { get; set; }
public void OnGet()
{
Model = new ArrayModel();
}
public void OnPost()
{
System.Diagnostics.Debugger.Break();
}
}
JS:
$('#add-item').on('click', function () {
debugger;
var value = $('#string-value').val();
$('#hidden-select').append(new Option(value, value));
$('#table-item tr:last').after('<tr><td>' + value + '</td></tr>')
});
Repository can be found here.
The options of the select will not be posted so this will not work.
The easiest way to do this is append the results to a hidden input with a separator char, then do a string split on the server side.
Another, maybee more elegant way, would be to add hidden inputs with the same name. Each input with it's own value. You should then be able to get this as a List or Array on the server.
Razor:
<input value="#String.Join(",", Model.Model.ArrayOfStrings)" id="tags"></select>
JS
$('#tags').val($('#tags').val() + ',' + value);
Controller
public void OnPost(string tags)
{
var tagsArray = tags.split(',');
}

How to make a dynamically generated URL redirect to another tab?

I'm using PrimeFaces 6.2
Hi everyone. As mentionned in the title, I need to open a new tab when a user clicks on a link (which is dynamically generated). I tried 2 solutions for now, and none of them works entirely :
1st solution : attributes url and target in PrimeFaces component
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" url="#{arboParObjView.sessionService.lienUrl()}" target="_blank"/>
</p:contextMenu>
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
SessionService sessionService;
private TreeNode selectedNode //changes everytime a node is selected - both right and left clicks work
...some code here...
public void genererLienBirt() //called everytime the selectedNode value is changed
{
String libelle="";
if (selectedNode != null)
{
//code to find the id of the associated to the selected node.
//I need the id because I want to pass it as a parameter of the link
//And this part of code works well
sessionService.setIdMesure(idMesure);
}
}
}
Session Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public String lienUrl()
{
String lien = "";
if (idMesure != null)
{
lien = lienUrl.getUrl();
lien += idMesure.toString();
return lien;
}
return "";
}
}
Bean :
public class LienURL
{
private String url;
public LienURL()
{
this.url = "myLink&BirtParameter="; //The base link with a Birt parameter waiting for the idMesure to be passed.
}
}
This solution doesn't work. When the user click on the menu item of the context menu component, it's opening a new tab but the opened page is the same as the one the user just leaved. I think that's because the PF's attribute url loads the url once (and the first time, my url is null because the idMesure isn't filled yet), and it just ignores the good link I try to pass after idMesure is filled.
2nd solution : use the redirect of the FacesContext
Facelet :
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" />
</p:contextMenu>
Service :
#Named(value="sessionService")
#SessionScoped
public class SessionService implements Serializable
{
private LienURL lienUrl = new LienURL();
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
}
The bean and the view don't change. It's the same as in the 1st solution.
The second solution works better than the first one. It is opening the good page with the good url, but in the same tab as the page where the user was. Is there a way to use the FacesContext redirect, but in another tab, as the target="_blank" do (the target only works with the url attribute) ? Or is there a way to make the url attribute read other urls than the first passed (which is null) ?
Thanks, and excuse my english.
Please use target="_blank" in p:menuitem only in second solution and it should work.
Below is updated code
<p:contextMenu id="menuMesure" for="treeVArboParents" nodeType="3">
<p:menuitem value="OPL" actionListener="#{arboParObjView.sessionService.lienUrl()}" target="_blank" />
</p:contextMenu>
and
public void lienUrl() throws IOException
{
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
String url = lienUrl.getUrl()+idMesure.toString();
ec.redirect(url);
}
Thanks to all the contributors for their help. Solution below :
View :
#Named(value="arboParObjView")
#ViewScoped
public class ArboParObjView implements Serializable
{
#Inject
private TreePodeService treePodeService;
private TreeNode selectedNode;
private Integer idMesure;
private String lienOplBirt;
...
//redirect to the generated link (called by the UI)
public void redirectOpl()
{
try {
FacesContext.getCurrentInstance().getExternalContext.redirect(lienOplBirt);
} catch (IOException e) {
e.printStackTrace();
}
}
//generate the Birt Link
public void genererLienBirt()
{
String libelle = "";
if (selectedNode != null)
{
libelle = selectedNode.getData().toString();
VArboParObjectifsParents mesureSelected = treePodeService.getPodeArboObjParentDao().findByLibelle(libelle);
idMesure = mesureSelected.getIdRoot();
}
lienOplBirt = "https://theLinkToPass"+"&RP_idMesure="+this.idMesure;
}
...
//Execute the genererLienBirt() method everytime selectedNode's value changes
public void setSelectedTreeNode(TreeNode selectedNode) {
if (selectedNode != this.selectedNode)
{
this.selectedNode = selectedNode;
genererLienBirt();
}
this.selectedNode = selectedNode;
}
}
Facelet (UI)
<p:menuitem value="OPL" includeViewParams="true" action="#{arboParObjView.redirectOpl()}" ajax="false" />

How to enable CSRF protection in ServiceStack

There is AntiXsrf code in ServiceStack, but it's not clear how to use it or enable it. Looking at network requests using chrome devtools, it doesn't appear to be turned on by default.
In your Razor page you can embed the token in your Form with:
<form action="/antiforgery/test" method="POST">
#Html.AntiForgeryToken()
<input name="Field" value="Test"/>
<input type="submit"/>
</form>
Which you can then validate in your Service with:
[Route("/antiforgery/test")]
public class AntiForgeryTest
{
public string Field { get; set; }
}
public class AntiForgeryService : Service
{
public object Any(AntiForgeryTest request)
{
AntiForgery.Validate();
...
}
}

SessionScoped controller not working

EDIT:
Okay, so I tried setting a few console.writes to check what's happening... It seems my logout script is called upon navigation. But I don't call it anywhere except on my logout button.
Here is my template code:
<div class="navbar">
<div class="navbar-inner">
<ul class="nav">
<li class="active">Home</li>
<li>Races</li>
<li>Horses</li>
<h:panelGroup rendered="#{memberController.logged == true}">
<li>History</li>
<li>Logout</li>
</h:panelGroup>
<h:panelGroup rendered="#{memberController.logged == false}">
<li>Login</li>
<li>Create Account</li>
</h:panelGroup>
</ul>
</div>
</div>
Original message:
I'm creating a website for my school project (Java EE)... It's our first year doing so.
Now as this is evening school and only had a semester learning it, you might see that my way of doing things ain't the best out there :)
So to get started, I'm trying to create a login feature but instead of those hundered lines of security codes, we may use a simple session scoped member object.
So here you have a few of my classes:
Member class:
#Entity
#Table(name = "members")
public class Member implements Serializable {
//+ Getters, setters, HashCode and equals
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
private double money;
#NotNull(message = "Username cannot be null")
#Size(min = 4, message = "Username should be of minimum 4 characters")
private String userName;
#NotNull(message = "Password cannot be null")
#Size(min = 4, message = "Password should be of minimum 4 characters")
private String password;
#PostPersist
private void initDefault() {
this.money = 500;
}
}
MemberBean class:
#Stateless
public class MemberBean {
#PersistenceContext(unitName="HorseRacingPU")
private EntityManager em;
public Member getMember(long id){
return em.find(Member.class, id);
}
public Member getMember(String username, String password){
TypedQuery<Member> q = em.createQuery("SELECT u FROM Member u WHERE u.userName=?1 AND u.password=?2", Member.class);
q.setParameter(1, username);
q.setParameter(2, password);
return q.getSingleResult();
}
public List<Member> getAllMembers(){
TypedQuery<Member> q = em.createQuery("SELECT u FROM Member u", Member.class);
return q.getResultList();
}
public Member addOrUpdateMember(Member u){
Member original = em.find(Member.class, u.getId());
if(original == null){
em.persist(u);
return u;
}else{
return em.merge(u);
}
}
public Member deleteMember(long id){
Member original = em.find(Member.class, id);
if(original != null){
em.remove(original);
}
return original;
}
}
MemberController class:
#SessionScoped
public class MemberController implements Serializable {
#EJB
private MemberBean bean;
private String username;
private String password;
private Member member;
private boolean logged = false;
// + their getters and setters
public List<Member> getAllMembers() {
return bean.getAllMembers();
}
public String login() {
member = bean.getMember(username, password);
if (member != null) {
logged = true;
return "/races/list.xhtml?faces-redirect=true";
}
return "/users/login.xhtml?faces-redirect=true";
}
public String logout() {
FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
return "/index.xhtml?faces-redirect=true";
}
public void checkLogin(ComponentSystemEvent e) {
if (!logged) {
FacesContext context = FacesContext.getCurrentInstance();
ConfigurableNavigationHandler handler = (ConfigurableNavigationHandler) context.getApplication().getNavigationHandler();
handler.performNavigation("/users/login.xhtml?faces-redirect=true");
}
}
public Member getMember() {
return member;
}
public void submit() {
bean.addOrUpdateMember(member);
}
}
The main error I'm getting is the following:
INFO: Exception when handling error trying to reset the response.
A more specific detail error can be found here: http://pastebin.com/h5nTNnes
So what happens is that when I login, everything works great. The moment I navigate to another url (after being forwarded to /races/list) I get logged out. The error itself shows when I use the checkLogin():
<f:event type="preRenderView" listener="#{memberController.checkLogin}" />
I'm not sure whether this is related, but when I login without any demo data (or with wrong credentials) I get an evaluation exception and that no entity could be retrieved.
Here more details: http://pastebin.com/Tv9mQ1K9
What could this be? I scratched my head for 3 days now and can't seem to find an issue anywhere.
This,
<li>Logout</li>
is not right.
The onclick attribute should reference a JavaScript handler. E.g. alert('peek-a-boo');. JSF/EL treats it as a plain vanilla string and expects that the logout() method returns some JavaScript code as String which should then be inlined in the HTML result. Imagine that the method actually returned alert('peek-a-boo');, then the final result (as you see in browser by rightclick, View Source) would be this:
<li>Logout</li>
However, in your particular case you're actually performing a logout and returning a string value of /index.xhtml?faces-redirect=true. So the generated HTML ends up being
<li>Logout</li>
Which is invalid JS code. But that's not the major problem: the user is been logged out without clicking the link!
You need a fullworthy JSF command component instead. E.g. <h:commandLink>.
<li><h:form><h:commandLink value="Logout" action="#{memberController.logout()}"/></h:form></li>
The method is this way only invoked when the link is actually clicked, which is exactly what you need.

Sitefinity widget Object reference not set to an instance of an object

I've created a Sitefinity widget with only and iframe in it.
<%# Control Language="C#" AutoEventWireup="True" CodeBehind="ApplicationFrame.ascx.cs"
Inherits="MyProject.Web.Ui.Customized.EmbeddedApplications.ApplicationFrame" %>
<iframe runat="server" id="ApplicationIFrame" height="250" width="250" scrolling="no" frameborder="0" seamless="seamless" src=""></iframe>
In the Page_Load of the widget I trie to access any properties of the server side iframe but I always get "Object reference not set to an instance of an object".
Here's the C#
namespace MyProject.Web.Ui.Customized.EmbeddedApplications
{
[ControlDesigner(typeof(ApplicationFrameDesigner))]
public partial class ApplicationFrame : System.Web.UI.UserControl
{
public string FrameSourceUrl {get;set;}
public string FrameHeight { get; set; }
public string FrameWidth { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
//set the values of the iframe to the current properties
ApplicationIFrame.Attributes["src"] = FrameSourceUrl;
ApplicationIFrame.Attributes["height"] = FrameHeight;
ApplicationIFrame.Attributes["width"] = FrameWidth;
}
}
}
I recently change the project from a Website to a Web Application, but that hasn't seemed to impact the project in any way.
Other than that I can't see why this exception keeps being thrown no matter what I do.
Anyone else know what the problem might be?
Thanks,
Jacques
This may not work depending on the type of widget you have created. It would probably work for normal User controls, but not custom controls.
If you are following the sitefinity documentation, you can inherit from SimpleView. Then you have access to helper methods for retrieving controls from the template. So instead of this:
ApplicationIFrame.Attributes["src"] = FrameSourceUrl;
You can do this:
this.GetControl<HtmlControl>("ApplicationIFrame", true).Attributes["src"] = FrameSourceUrl;

Resources