Passing args from Razor Page to a Razor Component - c#

The scenario
I've created a Razor Pages project and added server side blazor rendering so that I can add Razor Components. I am having an issue where _Layout.cshtml renders a razor component on the page and passes an argument to it, yet at runtime, the razor component appears to not receive an argument for this parameter.
Example with actual output
_Layout.cshtml
#using Microsoft.AspNetCore.Http
#inject IHttpContextAccessor HttpContextAccessor
<!DOCTYPE html>
<html lang="en">
<body>
IP ON GET WAS: #HttpContextAccessor.HttpContext.Connection.RemoteIpAddress
<component type="typeof(MyFormComponent)" IPAddress="#HttpContextAccessor.HttpContext.Connection.RemoteIpAddress" render-mode="ServerPrerendered"/>
</body>
</html>
When the website renders, the visitor's IP address is displayed directly above the component. (I'm doing this purely to prove to myself that _Layout.cshtml, the page rendering the razor component, does indeed have access to a concrete working child of IHttpContextAccessor.)
MyFormComponent.razor
#using System
<button type="button" #onclick="doStuff">
Click Me
</button>
#code
{
[Parameter]
public string IPAddress { get; set; }
private void doStuff()
{
Console.WriteLine("We've got the IP! -> " + this.IPAddress);
// prints: "We've got the IP! -> "
}
}
When the above button is clicked on the component, this.IPAddress is null. I've checked this value on a breakpoint while debugging and I've observed that the IP address is not printed to the console.
The expected output
I expected that my razor component would receive the argument value passed to it and print it to the console whenever the button is clicked. I expected that _Layout.cshtml would be able to pass this value to the razor component because it has proven that it does indeed have access to the desired value I want to pass by displaying the value on the page.
Closing notes
This was initially a Razor Pages project, created via dotnet new webapp.
Server side blazor was introduced to the project and has been working with great success, with this issue being the only exception so far. So, the project does have a _Host.cshtml and _Imports.razor file, along with the AddServerSideBlazor() call in Startup.ConfigureServices(), and the MapBlazorHub() endpoint added in Startup.Configure(). I didn't think it would be necessary to share the contents of these files for this specific problem. If I'm wrong about this, please, let me know!
You might notice that this issue specifically involves a reference to IHttpContextAccessor. If you were not aware, this specific class is NOT to be accessed directly from within a razor component for the reasons described here.

It appears that the solution to this problem is simply an alternate syntax being used to render the razor component in the _Layout.cshtml file.
By replacing (the current code)
<component type="typeof(MyFormComponent)" IPAddress="#HttpContextAccessor.HttpContext.Connection.RemoteIpAddress" render-mode="ServerPrerendered"/>
with the following (a new snippet):
#(await Html.RenderComponentAsync<MyFormComponent>(RenderMode.ServerPrerendered, new { IPAddress=HttpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString()}))
I was able to resolve this issue.

Related

Vue Component does not render...Renders as empty HTML comment

Problem:
Vue component renders on screen as <!---->
Setup:
Application is in C# Core 2.0
Vue version 2.5.16
Use gulp to copy static js files to /wwwroot
gulp.src('./node_modules/vue/dist/vue.min.js')
.pipe(gulp.dest('./wwwroot/dist/'))
Call to load Vue in HTML
<script src="~/dist/vue.min.js" asp-append-version="true"></script>
I have four components. Two use string literal templates and two use <template>. Of the later, only one displays.
<template id="paging-template"></template>
<template id="modal-template"></template>
Vue.component('modal',{ template:'#modal-template'});
Vue.component('paging',{ template:'#paging-template'});
Modal is called with <modal></modal> and Paging with <paging></paging>.
Modal displays properly. Paging displays as <!----> but using the Vue debugger addon in Chrome I can see the model and everything for the Paging component is there.
What am I missing?
Edit:
JSFiddle Link
This css is not mine, just the default on the Vue template they provide
There are a number of things wrong with your example(All of them are visible in the console tab of the browser):
Your class binding syntax is incorrect. All instances where you are trying to bind classes as follows:
v-bind:class="'disabled': previousDisabled"
Should be updated to:
v-bind:class="{disabled: previousDisabled}"
isNumber is not a method. It can be replaced with !Number.isNaN(
Updated JSFiddle

C# and Html: How to implement an already created C# class for windows form to html?

Beginner here,
I have asked a similar question and I apologize if my terminology is incorrect.
I have this windows app form project that has basically a class with five fields and another class that makes the form do stuff like change text inside a textbox when a button is clicked. I used the form designer to test out the code to make sure it works and it did now I believe all I have to do is replace code that says "Textbox.Text" with it's html equivalent and place methods inside html button event handlers.
I am working as a team so I have to understand other people's html code. To keep things short, how do I identify the button and it's event handler and textbox's text attribute in html, and where to a store the class files to make things work?.
I already tried googling and Youtubing and I am not getting the results I'm looking for.
Edit: The previous question I asked involved using asp or aspx files which I don't think exist in my team projects. I'm using Visual Studio for all of this and unlike with forms I don't think or know that I can just click components to summon up its event-handlers.
You should take any beginner to intermediate course or follow any tutorials to learn about asp.net/core MVC.
In asp.net core with MVC,
You would have a view with .cshtml(your html file) extention and your controller having .cs extention (your class file) and a Model Class file.
For Example,
A simple controller might look like ,
public class HelloWorldController : Controller
{
//
// GET: /HelloWorld/
public string Index()
{
return "Hello World !!";
}
//
// GET: /HelloWorld/Welcome/
public string welcome()
{
return "Welcome to .NET Core!!";
}
}
When you navigate to /helloworld you will see Hello World!! in your browser.
And when you navigate to /welcome it will display Welcome to .NET Core
You can find some tutorials here
take a look at JavaScript https://www.w3schools.com/js/default.asp or JQuery https://www.w3schools.com/jquery/default.asp
<!DOCTYPE html>
<html>
<body>
<h2>JavaScript in Body</h2>
<p id="demo"></p>
<button type="button" onclick="myFunction()">Click Here</button>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Hello World";
}
</script>
</body>
</html>

Razor tag inside Html.Raw(...)

Is it possible to render Razor in Html.Raw()? I have a dynamic page being generated that uses the Html.Raw() method to render the page that is created in the controller. My raw html has razor tags in it. That is, I am trying to generate a link by
#Html.Raw(Model.myRawHtmlContainingRazorTags);
where Model.myRawHtmlContainingRazorTags contains
<html>
...
...
#Model.TheLink
...
...
<html>
I need the value of Model.TheLinkto be rendered when #Html.Raw(Model.myRawHtmlContainingRazorTags) is called within the cshtml.
You may have some architectural issues that lead you to have some razor code on your Model property.
Anyway, you can do it by using some external libs such RazorTemplates.
Here is a sample :
var template = Template.Compile(Model.myRawHtmlContainingRazorTags);
#Html.Raw(template.Render(Model));

C# access element in master page from logic layer

I have a content page connected to a master page. I can access an element on the master page and modify it directly from the content page .cs file by calling a method on the site master. (this is probably the most standard bug people have in this type of area)
My problem is that I wanted to extend this functionality to update the site master page from an AJAX request as well. The ajax file calls a different page which in turns starts an instance of the logic layer which I use for all the calculations and connections. What I am trying to do is access the sitemaster directly from the logic layer (only a .cs file).
My current code is this:
SiteMaster sm = new SiteMaster();
sm.MyMethod("param1", "param2");
This successfully accesses the method called "MyMethod" in the site master but inside this method I have this code:
mySpan.InnerText = "this is a test";
which doesn't work because I get the "Object refernce not set to an instance of an object...." error. This is because mySpan is NULL. If I call it using this.mySpan.InnerText though, if I hover over "this" then I can see the ID "mySpan".
Does anyone know how I can get around this problem? Every search I have made is regarding people who want to access the elements from the content page which already works for me.
I believe you've got a misunderstanding here. If I understand correctly you've got a page with a MasterPage. On that aspx page you're doing an ajax call (perhaps to a WebService) which does something like:
[WebMethod]
public void UpdateText(string message)
{
var master = new SiteMaster();
master.mySpan.Text = message;
}
There are a couple of things wrong here.
When you use this approach is an aspx page you're updating that Page's master. For example:
public void OnSomeRandomButtonClick(object sender, EventArgs e)
{
((SiteMaster)this.Page.Master).mySpan.Text = "Some Text";
}
What you're doing here is updating the span on the master page before it's being sent to your browser. The other subtly is that you're not creating a new SiteMaster, you're using the Page's existing Master and casting it to a SiteMaster.
There are a couple of reasons you can't do this with ajax:
A webservice doesn't have a MasterPage
By the time you send an ajax request your Master page has already been created and sent to the browser.
So your question becomes how do we update a span in the Master without posting back to the server?
Lets look at the html which is actually on your box, it will look something like this:
<html>
<head>
<title>My Awesome Page</title>
</head>
<body>
<h1>This is my Awesome Website</h1>
<span id="mySpan">I'm sure you'll like it</span>
<div>
<p>Page Content</p>
<div>
</body>
</html>
Lets assume that everything here is generated by the master and only the <p>Page Content</p> is your aspx page (There will also be loads of ASP.NET junk added, we'll ignore that for the time being).
What you want to do is update the text in mySpan without posting back to the server. You can do this via the javascript - don't get ajax involved at all!
I'm going to assume you're using jQuery (mostly because I'm more familiar with it that plain old JS). You've got the ID of your span ("mySpan") so the rest is easy:
$('#mySpan').html('This is the updated message');
You can put this in either a click or a page load.
No. You can not simply construct an ASP.NET page and use its state.
ASP.NET pages (and controls and Master pages) are being constructed and initialized from inside the ASP.NET engine based on the Markup provided for them. There is for example no initialization for mySpan inside the codeBehind of your master page, that will be constructed when the code generated based on the Markup is invoked based on a user request.
So you define this in your class:
protected HtmlGenericControl mySpan;
But the ASP.NET engine will compile this markup
<span id="mySpan" style="color:green"></span>
to this code:
this.mySpan = new HtmlGenericControl();
this.mySpan.Style.Add("color", "green);
and that is why you can use this object inside your code.
So if you want to use a property of your Master page from your Business layer, you have so many choices. On of the fastest one to implement is to make your Logic class singleton inside the Session scope, store the value you want to use inside the master page into that singleton object and then read that value from the master Page. This is an example of what you should do, of course it is rough.
class Logic
{
public static Logic Instance
{
get
{
if (HttpContext.Current.Session["LogicInstance"] == null)
HttpContext.Current.Session["LogicInstance"] = new Logic();
return (Logic) HttpContext.Current.Session["LogicInstance"];
}
}
public string TextForSpan {get;}
// The rest of your implementation
}
Instead of the code to assign the inner text, write:
Logic.Instance.TextForSpan = "This is my text";
And inside your master page:
this.mySpan.InnerText = Logic.Instance.TextForSpan;

Including scripts in sections in MVC EditorTemplate [duplicate]

So I'm trying to create a custom editor so that for a DataType of "Duration" a textbox appears with a masked format of HH:MM:SS.
I've created a very simple piece of code so far
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line", type = "duration" })
<script>
$(document).ready(function () {
$("##Html.NameFor(c => c)").mask("00:00:00");
});
</script>
This is in my ~/Views/Shared/EditorTemplates/Duration.cshtml file. However it requires an additional javascript to be loaded (maskedInput.js).
Is there any razor includes I can use here so that I can include the maskedInput.js file once and only once in a page load. I realise I could add it to the parent page the editor will be on (but that would require knowing every page where this editor is used). I could add it to the master layout view but this would mean overhead for the pages that don't use this editor.
So I suppose in summary all I'm asking is :- "Is there a way to include a javascript file once and only once from a EditorTemplate".
I wrote a nuget package exactly for this purpose and wrote the blog post that YD1m has referred to.
To use the package, first thing you need to do is add a call to Html.RenderScripts() somewhere in your layout so that all of the script file references and blocks added using the helpers during the rendering of a Razor view are outputted in the response. The typical place to put this call is after the core scripts in your top level layout. Here's an example layout:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>#ViewBag.Title</title>
<meta name="viewport" content="width=device-width">
</head>
<body>
#RenderBody()
<script src="~/Scripts/jquery-2.0.2.js"></script>
#* Call just after the core scripts in the top level layout *#
#Html.RenderScripts()
</body>
</html>
If you're using the Microsoft ASP.NET Web Optimization framework, you can use the overload of Html.RenderScripts() to use the Scripts.Render() method as the function for generating scripts:
#Html.RenderScripts(Scripts.Render)
With that done, now all you need to do in your editor template is use the helpers in the nuget package to add a reference to the script and add your code block
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "text-box single-line", type = "duration" })
#using (Html.BeginScriptContext())
{
Html.AddScriptFile("~/Scripts/maskedInput.js");
Html.AddScriptBlock(
#<script>
$(document).ready(function () {
$("##Html.NameFor(c => c)").mask("00:00:00");
});
</script>);
}
The referenced script file will only be added once to the page and all of the script blocks will be written out at the end of Html.RenderScripts() call.
Using the helpers, you can add script files and script blocks in Views, Partial Views and Editor/Display Templates. Note that the current version (1.1.0.0) will not render out scripts using the helpers when called via AJAX but this is something that I'm looking to add in the next version.
Well, you can do following:
Add #RenderSection("MaskedInput", false) to your master layout. That'l render
#section MaskedInput{}
on each page that has that section;
On a page you need to add maskedInput.js you put:
#section MaskedInput
{
#*Include scripts, styles or whatever you need here*#
}
You can create singleton class with Dictionary property whic will store scripts from your custom helpers/templates.
In your templates you can call method, that put script in singleton dictionary property with some string key. In that method you can prevent adding scripts with same keys.
Finally you should to write a render action for rendering scripts from dictionary and call this action from your master layout.
Here you can find solution similar to mine:
Managing Scripts for Razor Partial Views and Templates in ASP.NET MVC

Categories