Open VBAndCs opened 5 years ago
Finally: A working VB.NET ASP.NET MVC Core Razor sample! https://github.com/VBAndCs/VB.NET-Razor I implemented a simple VBRazorViewEngine in the VbRazor project. To use VBRazorViewEngine in the project, I added these two statements to the Startup.ConfigureServices method:
services.AddTransient(Of IConfigureOptions(Of MvcViewOptions), VBRazor.VBRazorMvcViewOptionsSetup)()
services.AddSingleton(Of IViewEngine, VBRazor.VBRazorViewEngine)()
The VBRazor is just a VB class that implements the IVBRazor Interface:
Public Interface IVBRazor
ReadOnly Property Razor As String
End Interface
The Razor property uses the xml literals to compose the HTML code and returns it as a string.. Example:
Imports VbRazor
Public Class IndexView
Implements IVBRazor
Dim students As List(Of Student)
Public Sub New(students As List(Of Student))
Me.students = students
End Sub
Public ReadOnly Property Razor As String Implements IVBRazor.Razor
Get
Dim x = <html>
<h3> Browse Students</h3>
<p>Select from <%= students.Count() %> students:</p>
<ul>
<%= (Iterator Function()
For Each std In students
Yield <li><%= std.Name %></li>
Next
End Function)() %>
</ul>
</html>
Return x.ToString()
End Get
End Property
End Class
To use the IndexView from the Controller, I passed it to the View method as the model data in the action method, and passed the actual model data to its constructor:
Public Function Index() As IActionResult
Return View(New IndexView(Students))
End Function
That’s all!! If you run the project, you will see this web page:
This was really easy, but needs more work, so I hope you start contribute to this project to make it a real productive tool! The first thing to do, it to create a VB.NET template for ASP.NET MVC Core. I had to create a C# project then convert it to VB!
The second thing to do, is to add intellisense support for html attributes in xml literals in VB!
We need to try more advanced pages with JavaScript and other components. I hope VB team give us the support wee need to make the most of xml literals.
This is AWESOME! Now I might actually take some time to wrap my head around Razor (when, of course, I can come up from air from my current workload). ;-)
@DualBrain I am waiting. Wish you safe landing :)
This is a really interesting direction.
My main question at this point is what benefit the infrastructure of Razor Views brings?
If you took the base concept, using XML Literals instead of .cshtml, and didn't try to use any of the rest of the pattern, could you get an even simpler version of MVC.
For example, could you give the
I'm explaining that to clarify that it is a serious question to ask what any part of razor brings to an XML literal based approach - beyond the controller routing and action results.
Or put another way - can an XML Literal approach be better/simpler than Razor.
(and regardless, I think there may be benefit in not calling it Razor as that is often associated with the interpretation of pages, which invites an unnecessary comparison).
Thanks @KathleenDollard We can choose any suitable name instead of Razor, like XML views or something. This is not the main issue at this point. I am also ok with changing the model, put I am still alone, and walking in a new territory according to my experiences, so, it is easier to prove the concept by plugging in a custom view engine and let MVC do all the work. At least it does the parsing and rendering of HTML and its scripts, so I can focus only on delivering the right html format. If I have the community support, we can make any changes to the model in the right moment. I am building things up, so I didn't try view inheritance and adding page header and footer and so. I just tried adding a script today, and it works fine in the browser, but vb editor doesn't add any support for the scripts in the XML literal. This is not big issue now, because I can over he script to a separate file so it can have the full editor support for java script or other script languages. If the script depends on some model values, we can pass them to a function call, so the script in xml will just be a one line to call this function, and this will need a little support from the editor to auto complete the function name and view its params info. Luckily, the solution explorer is clever enough, so it added the script file (IndexView.vb.js) as a sub element of the view file (IndexView.vb) Same goes for CSS. It is not suitable to mix VB code with JavaScript and CSS. But the really needed support now, is the editor support for HTML tags and their attributes. we need to borrow this from the .html editor in VS.NET or VScode.
There is a strange behaiviour of XElement: It is implicit conversion operator returns XElement.Value not XElement.ToString( ). This gives unexpected results such in this example:
Dim s = <p>Test</p>
Console.WriteLine(s) ' <p>Test</p>
Dim s2 As String = s
Console.WriteLine(s2) 'Test
I fall into this in the Razor property, when I accidentally erased the .ToString() from the end of return statement, and got a plain text in the html page!
I expect that VB will not change this strange behavior not to break any existing code! So, To get red of this probable error, I changed the definition of the Razor property to return an XElement nad made change to the VBRazorView to call its .ToString. I also changed the Razor to be a function to save some indent consumed by Get… End Get
.
Public Interface IVBRazor
Property ViewBag As Object
Property ModelState As ModelStateDictionary
Function Razor() As XElement
End Interface
I added two new properties to pass the ViewBag and ModelState to the View
This is the modified VBRazorView.RenderAsync
Public Async Function RenderAsync(ByVal context As ViewContext) As Task Implements IView.RenderAsync
Dim vbRazor = CType(context.ViewData.Model, IVBRazor)
vbRazor.ViewBag = context.ViewBag
vbRazor.ModelState = context.ModelState
Dim r = Await Task.Run(AddressOf vbRazor.Razor.ToString).ConfigureAwait(False)
context.Writer.Write(r)
End Function
And this is the IndexView Class:
Imports Microsoft.AspNetCore.Mvc.ModelBinding
Imports VbRazor
Public Class IndexView
Implements IVBRazor
Dim students As List(Of Student)
Public Sub New(students As List(Of Student))
Me.students = students
End Sub
Public Property ViewBag As Object Implements IVBRazor.ViewBag
Public Property ModelState As ModelStateDictionary Implements IVBRazor.ModelState
Public Function Razor() As XElement Implements IVBRazor.Razor
ViewBag.Title = "VB Razor Sample"
Return _
<html>
<h3> Browse Students</h3>
<p>Select from <%= students.Count() %> students:</p>
<ul>
<%= (Iterator Function()
For Each std In students
Yield <li><%= std.Name %></li>
Next
End Function)() %>
</ul>
<script>
var x = 5;
document.writeln("students count = <%= students.Count() %>");
</script>
</html>
End Function
End Class
The ViewBag.Title = "VB Razor Sample"
has no effect, so, we need to write the layout view class. I will try this.
I didn't upload these changes to the repo, since I am still experimenting.
I made changes to use a layout View Class.. I used the same layout provided in the C# project template to create this layout class
Things are easy. The layout has a Body property that uses to insert the body in the XML representation of the layout:
<main role="main" class="pb-3">
<%= Body %>
</main>
It is the job of each View to render itself as the body of the layout. This is done by the RenderView function:
Public Function RenderView() As XElement Implements IRazor.RenderView
Dim layout As New LayoutView(Razor(), ViewBag, ModelState)
Return layout.Razor
End Function
Note: We need a command to create new View with the basic code like this one:
Imports Microsoft.AspNetCore.Mvc.ModelBinding
Imports VbRazor
Public Class <<ViewName>>
Implements IRazor
Dim <<ModelVarName>> As <<ModelType>>
Public Sub New(<<ModelVarName>> As <<ModelType>>)
Me.<<ModelVarName>> = <<ModelVarName>>
End Sub
Public Property ViewBag As Object Implements IRazor.ViewBag
Public Property ModelState As ModelStateDictionary Implements IRazor.ModelState
Public Function RenderView() As XElement Implements IRazor.RenderView
Dim layout As New LayoutView(Razor(), ViewBag, ModelState)
Return layout.Razor
End Function
Public Function Razor() As XElement Implements IRazor.Razor
ViewBag.Title = "VB Razor Sample"
Return _
<div>
' Write you vbxml view here
</div>
End Function
End Class
Note That I had to add a div or p tag to contain the vbxml code. I want to avoid this if possible but have no ideas now.
The same can be done to generate the prototype for the layout class.
@KathleenDollard s you said, no need to search for file locations. We just use the name LayoutView for the layout razor class.
Now, the output page is shown like this:
There is more work to do to render sections and other things. I will see also why home and privacy are not shown as links, but now I need to sleep :)
I want to use a new approach. I can generate a simplified cshtml file from the vbxml code , so the C# Razor an carry out all the work as usual! This will: 1- Simplify my job (no need to re-invent the Razor View Engine) 2- Eliminate the need to do any future development, as MS are the responsible for improving the Razor. Ti achieve this, these are the steps: If the View class contains this Vazor function:
Public Function Vazor() As XElement
ViewBag.Title = "VB Razor Sample"
Return _
<vazor>
<h3> Browse Students</h3>
<p>Select from <%= students.Count() %> students:</p>
<ul>
<%= (Iterator Function()
For Each std In students
Yield <li><%= std.Name %></li>
Next
End Function)() %>
</ul>
<script>
var x = 5;
document.writeln("students count = <%= students.Count() %>");
</script>
</vazor>
End Function
I want to generate this code inside the view class:
Dim Vazor_Value1 As String = students.Count().ToString()
Dim Vazor_Value2 = <vb_xml>
<%= (Iterator Function()
For Each std In students
Yield <li><%= std.Name %></li>
Next
End Function)() %>
</vb_xml>.ToString().
Replace("<vb_xml>" + vbCrLf, "").
Replace(vbCrLf + "</vb_xml>", "")
Public Sub PassData()
ViewBag.Vazor_Value1 = Vazor_Value1
ViewBag.Vazor_Value2 = Vazor_Value2
End Sub
and PassData() must be called somewhere (say at the end of the constructor of the view class). Also, we auto generate this cshtml file:
<h3> Browse Students</h3>
<p>Select from @ViewBag.Vazor_Value1 students:</p>
<ul>
@ViewBag.Vazor_Value2
</ul>
<script>
var x = 5;
document.writeln('students count = @ViewBag.Vazor_Value1');
</script>
@KathleenDollard , @AnthonyDGreen I can do parsing and generate the chtml file, put what I need help is: 1- How do this at the building time of the project (before compiling the View classes). 2- How I read the get all classes that implement IVazor interlace (the view classes) and how I read the Vazor function code? Is it better to read it as a plain text and do the usual search for <%= %> blocks, or it is easier to get the syntax tree and work with it? 2- How to generate code and add it to existing code file? Can I generate a temp or in-memory vb code file that contains a partial definition of the View class and add the methods to it? and how I make the compiler see and compile it? I need some links about this to start read and work. I don't want to read huge books to only do this task. How to do articles or articles about these specific topics will more practical. and I will be glad if any one wants to participate in this.
@VBAndCs Do you imagine the vb and cs code existing in the same project? That is probably not going to work.
To answer some specific questions, you could use a custom build task as part of the build - but this won't happen as part of design time build - no IntelliSense.
I would try to avoid parsing the Vazor code yourself if you can just use the XML.
Generating code and adding to an existing code file during the build is tricky - has anyone else done that? It's been a super long time as most of my generation was prior to build in a separate step.
What problems did you find with the straight XML Literal approach? I imagined that we could hand back HTML and bypass large parts of the Razor complexity. Of course with some pretty involved headers and such.
@KathleenDollard
Thanks for your support.
Do you imagine the vb and cs code existing in the same project? That is probably not going to work.
Typically, they are not in the same project, because C# in cshtml files is just a script and Razor (which is another project) is responsible for compiling it. This already working and I tried it : https://github.com/mevdschee/aspnetcorevb It works fine in ASP.NET Core 2.1, but when I tried the same in ASP.NET core 3.0 preview, I got an error page saying it can't find Index.cshtml! This can be fixed later, but the main concept is still applied: VB code an cshtml file can co-exist in one project.
prior to build in a separate step
Yes, I want to do this. Or, it can be done after saving the changes of each view class (that is free of bugs), so we grantee that the chml files are in place b4 the build starts.
What problems did you find with the straight XML Literal approach?
1- Need to resolve paths to js and image files and other components. 2- Need to process some asp tags 3- Need to consider user credentials, security and encryption. and so on. The code is there in C# Razor, but input is different. Razor expected a path to a cshtml raw file to parse, compile and generate html object element tree, while I deliver a string containing the processed Html. I need to skip the first steps and jump to the step where Razor processes HTML contents and resolve paths and asp tags and other stuff. Another issue, is that Razor uses buffers. I expect it is a more efficient way to handle large pages. By the way, I have some thoughts to optimize the VBXML code. For example: if the layout differs only by the title, then we can cash the html layout and only change the title for each view. I expect razor to do something like that in some stage. I succeeded to build the Razor.dll today, but I still facing problems in adding a web project to the solution and make it use the Razor project so I can trace how it processes chtml files. The code depends on interfaces, and I wasted a lot of time trying to trace functions calls, and failed! I need to understand how the Razor works, so I can call the right methods to process my html content, or if it is not possible (because internal accessibility or incompatible inputs), I can manually copy and modify the code to make a fully functional Vazor view engine. Thanks.
@VBAndCs The suggestion is simply to write any vb.net code directly inside the <%= %> marks
As a heavy user of XML literals I definitely support this suggestion :+1: :+1: though I am not yet sure about the specifics. For example, the For Each
scenario is already easy to implement using LINQ queries like this (actual example from shipping code):
Dim TaskInfoEntries =
<order-summary-tasks>
<%= From task In Me.GetPendingTasks() Select task.ToXML() %>
</order-summary-tasks>
By combinining LINQ queries and function calls you can express pretty much anything you want. Since XML literals already support taking collections of XElement
s, I don't think we need anything special for the For Each
type scenarios.
However, the Select Case
scenario is impossible to do directly with current language features and perhaps a broader feature we could consider is an expression version of Select Case (similar to the one from T-SQL) which would be useful in general (i.e without using XML literals) and could then be used with XML literals too.
Some food for thought!
@ericmutta Thanks for your support. I already showed how to use lambda expressions to embed any code inside XML literals. This is how you use Select case:
<ul>
<%= (Function()
Select Case Model.Count
Case 1
Return <li>One Item</li>
Case 2
Return <li>Two Items</li>
Case Else
Return <li>Many Items</li>
End Select
End Function)() %>
</ul>
So, there is nothing you can'y do. Please take some time to read all the discussion here, maybe you can help in some issues mensioned. Thanks.
I'll just note that #109 (Block expressions) from a while ago seems to fit in well with some of the ideas here.
@gilfusion
Yes, but I don't like more { }
blocks in VB, espetially it can be confued with array and object initializers. I already poposed to add property lambda expressions using Get ... End Get
in #398 .
Thanks.
I saw this chtml
@{
var value = "Value 1";
}
<p>@value</p>
@{
value = "Value 2";
}
<p>@value</p>
So, I tried to write it with xml literals. I got this:
Dim value = "Value 1"
Dim html = GetVazorContent(
<Vazor>
<p><%= value %></p>
<%= (Function()
value = "Value 2"
Return ""
End Function)() %>
<p><%= value %></p>
</Vazor>)
It is a long workaround just to set a value inside xml! Even If my proposal excepted and we get red of the lamda body:
<Vazor>
<p><%= value %></p>
<%= value = "Value 2"
Return ""
%>
<p><%= value %></p>
</Vazor>)
It is still feels strange! So, this brings me back to my rejected proposal #393 ! Using Let to define a scope can allow us to use code blocks as in Razor views :
<Vazor>
<p><%= value %></p>
<%= Let
value = "Value 2"
End Let %>
<p><%= value %></p>
</Vazor>)
And when it is just one statement, we can do it in one line as in LinQ:
<Vazor>
<p><%= value %></p>
<%= Let value = "Value 2"%>
<p><%= value %></p>
</Vazor>)
Or another thought: to be compatible with Get… End Get
read-only lambda property, maybe a Set … End Set
block that acts as a write-only lambda property is considered.
<Vazor>
<p><%= value %></p>
<%= Set
value = "Value 2"
End Set %>
<p><%= value %></p>
</Vazor>)
And when it is just one statement:
<Vazor>
<p><%= value %></p>
<%= Set value = "Value 2"%>
<p><%= value %></p>
</Vazor>)
Another issue with Xaml literals, is that it doesn't allow write the & even inside quotes!
<x note="&Note">&Note</x>
VB refuses both &s, saying:
XML entity references are not allowed
I hope to allow escaping & like this \& or any other way. The only solution is to use any text like __amp and replace it in when we get the string representing xml.
Surprise: While I was looking for a solution for this, I found this old project that uses XML literals to create the Views! https://archive.codeplex.com/?p=vbmvc The project is before razor pages (talking about ASPX pages) and I couldn't load the sample in VB.NET 2019!
View Engine for ASP.NET MVC using VB.NET XML Literals instead of traditional ASP.NET pages. Each view is a VB.NET class and master pages are base classes. Views are compiled into the assembly, so no ASPX files to deploy. Based on code by Dmitry Robsman
Another issue with Xaml literals, is that it doesn't allow write the & even inside quotes!
<x note="&Note">&Note</x>
can you give more context?
I don't do much XAML, but I do a fair bit of XML, and I'd normally expect the XML for that to look more like <x note="&Note">&Note</x>
Ok. VB accepts &
I was afraid it will not work inside attributes:
Dim param1Value as String = "test"
Dim html =
<div>
<a
id="myLink"
href=<%= "/mysite/myfile?param1=" + param1Value + "amp;param2=test2" %>>
link
</a>
</div>
But it worked! The &
becomes & in the url.
Thanks :)
But, there is still a problem:
<x>©MyCompany</x>
VB didn't allow & here. It seems it is not aware of ©
which is the symbol ©, and need to be updated.
I tried: <x><%= "©" %>MyCompany</x>
but it yields <x><%= "&copy;" %>MyCompany</x>
which is wrong, because browsers will view it as ©
not © !
The only solution I know is this:
Dim amp = "_vb_amp_"
Dim s = <div>
<x><%= amp %>copy;MyCompany</x>
</div>.ToString().Replace(amp, "&")
but inside quotes, the string _vbamp must inserted directly not because <%= amp %>
is not allowed inside quotes.
It seems it is not aware of
©
which is the symbol ©, and need to be updated.
VB XML literals are following XML character entities, which is a restricted list and doesn't include things like ©
:
HTML defines a bunch of extra ones like ©
but that's not something you can put in an arbitrary XML document -- it's up to the language atop XML (like HTML, but it could be anything) that's specifying what those extra entities mean.
@jasonmalinowski OK. We don't want to change VB specifications about XML. We only want a way to escap & char so we can use extra & terms as we need. For example, I will be OK with using &&& as an escape for &, and I think &&& is unusual equence to frear breake anything.
So the bigger problem is that VB XML literals are only XML 1.0, and don't support DTDs, and what you're after is adding HTML5 support (the HTML5 DTD) to your XML.
I would say that there is a good argument for expanding VB's XML literals to support this, but that may require expanding LINQ to XML to support them properly (LINQ to XML has only limited DTD support).
StackOverflow was helpful here
Interestingly, good 'ol System.Xml has it - an XmlEntityReference class.
I guess the System.Xml.Linq folks didn't see the value . But then I think LINQ was focused on reading XML, not writing it, whereas System.Xml was from the days when XML was still cool and so needs to do both.
@pricerc What about the cheap solution al the parser level: escape &&& and output it as & ?
@pricerc What about the cheap solution al the parser level: escape &&& and output it as & ?
I prefer good solutions to cheap ones. Expanding XML support to include DTD's would be a much more powerful option and allow the XML literal to 'pretty print' any markup built on top of XML, including HTML5.
I just don't know 1) if it would need LINQ-to-XML expanded, or if there another workaround (e.g. deserializing any DTD separately) and 2) how the compiler converts from literals to LINQ-to-XML, so I don't know how much work there is.
To be honest, I think &&& is awful. I don't want to be counting punctuation when figuring out code.
If there were going to be a 'cheap' solution, I think I'd prefer to see any XML identity that literals don't understand 'ignored'. So & > > ' and " are understood and handled. Anything else that looks like an XML IDENTITY should be ignored (e.g. © , etc) and passed through for serialization (which is presumably what's happening to your code).
But once again, I don't know how much work is involved.
I'd love to start digging into the compiler myself, but unfortunately, being self-employed means that earning takes precedence over learning, and I don't currently have the luxury of time to commit to learning a whole new set of tricks.
(edit: fixed those identities!)
If someone has the time to put together a proposal on this, that would be very helpful. I'm not sure we need full DTD support, but understand the request for more than a one-off solution for ampersand. Is it only literals (where a "don't escape this" gesture might be enough). Is there more in XHTML that will affect the XML literal MVC ("Vazor") work. Doesn't need to be fancy, just the start of a list of problems like &xcopy
I am excited to announce that: My Work Is Complete!
I found an easy solution to make Razor all its magic for our Vazor!
All I had to do, is to use the IFileProvider to define a virtual file system that delivers the html output produced from Vazor to Razor as if it is the cshtml view that Razor expects! So, Razor resolves the tag helpers, paths, combine the layout and sections, and any other stuff!
There is one issue here, that is Vazor delivers different compiled pages for each View, so I used a Mapper class that appends a unique ID the name of each page, so that multiple synchronous requests to the same view can happen safely.
The amazing thing here is that we have a mixed Vazor/Razor! This means we can write some views as Vazor classes, and write some other as Razor views and they will integrate an co-work smoothly!
This is important to save us unnecessary effort to convert Razor views that doesn't contain any code (like the layout page and View imports pages.. etc) to Vazor classes!
If we want to use the Vazor Layout, we must map it in the configure method like this:
Vazor.VazorViewMapper.Push(New LayoutView(), False)
To use the Vazor IndexView calss, we map it in the Home.Index acctiom method and pass the unique name generated by the mapper to the view method like this:
Return View(Vazor.VazorViewMapper.Push(New IndexView(Students, ViewBag)))
and that is all!
This image shows the rendered Page resulted of:
I will wrap somethings up and publish this work at a new repo called Vazor. It seems like a few lines of code, but it is a big step for VB.NET : )
@KathleenDollard
Now, the main concern is to make writing Vazor code pleasant. We have no support for tag names, attributes and of course Tag Helpers in XML literals. I hope to write direct code inside <%= %>
without any lambda expression tricks. and of course there is the & issue.
I can workaround Tag Helpers lake of support by providing wrapper classes with shared methods, but this is a huge work, and will not be bratty to use <%= %>
to add tag helpers!
So, the next level is to work on xml and intellisense. This is where I can't go on alone. I need the help of the team an community. Thanks all.
Vazor 1.0: https://github.com/VBAndCs/Vazor
I moved the vxml code to a pratial class to be in its own file, whicvh I named IndexView.vbxml.vb
Partial Public Class IndexView
Private Function GetVbXml() As XElement Implements Vazor.IVazorView.GetVbXml
ViewBag.Title = "Vazor Sample"
Return _
<vbxml>
<h3> Browse Students</h3>
<p>Select from <%= students.Count() %> students:</p>
<ul>
<%= (Iterator Function()
For Each std In students
Yield <li><%= std.Name %></li>
Next
End Function)() %>
</ul>
<script>
var x = 5;
document.writeln("students count = <%= students.Count() %>");
</script>
</vbxml>
End Function
End Class
I wanted to put the signature of the GetVbXml method in the other prat of the class, but I figured out that partial functions are now allowed! Why?
Anyway, this is not the only thing I want. I want to be able to insert the mehod body alone in the partial file without the header, So, we can have a pure vbxml code, while VB still treats it as the code of the body of the GetVbXml function! This is how I want to see in the Indexview.vbxml.vb file:
<vbxml>
<h3> Browse Students</h3>
<p>Select from <%= students.Count() %> students:</p>
<ul>
<%= (Iterator Function()
For Each std In students
Yield <li><%= std.Name %></li>
Next
End Function)() %>
</ul>
<script>
var x = 5;
document.writeln("students count = <%= students.Count() %>");
</script>
</vbxml>
and this is what the how should the function look like:
Private Function GetVbXml() As XElement Implements Vazor.IVazorView.GetVbXml
ViewBag.Title = "Vazor Sample"
Return Partial "IndexView.vbxml.vb"
End Funcrion
In fact this new use of Partial keyword can be generalized as
Partial " code file that contains the Expression"
so we can use it anywhere in the class, such as:
Dim html = Partial "IndexView.vbxml.vb"
The implementation should be easy: the compiler just substitute the partial part with the expression. But the work needed is to make the editor treat the partial file as a part of the main class in design time.
Vazor processes the view in two stages: 1- VB processes xml literals and evaluates expressions to deliver html page. 2- Razor processes the html page to resolve tag helpers and other stuff, to deliver the page to the browser.
In fact, we can add a third stage in the middle, to do ant thing we want with the html page. for example, I decided to add this sort of data templates, which uses the model as the data source by default:
<ul>
<li ForEach="m">
<p>Id: <m.Id/></p>
<p>Name: <m.Name/></p>
<p>Grade: <m.Grade/></p>
</li>
</ul>
Note: you can use any var name other than m.
The above code is a shortcut for:
<ul>
<%= (Iterator Function()
For Each std In students
Yield <li>
<p>Id: <%= std.ID %></p>
<p>Name: <%= std.Name %></p>
<p>Grade: <%= std.Grade %></p>
</li>
Next
End Function)() %>
</ul>
I manage to do it by using Reflection in the function ParseTemplate: https://github.com/VBAndCs/Vazor/blob/master/Vazor/VazorExtensions.vb
ParseTemplate is an extension method to the XElement class, and it only receives the model object (I assume it is an IEnumerable(Of T)), so, It can be used as this in our IndexView class in the sample project:
Dim html = GetVbXml().ParseTemplate(students)
It works fine, but I wish XML literals support this template model, so:
1- It provide editor intellesense when writing m.
This may require adding the type of the var like this:
<li Temp.ForEach="m" Temp.Type="Student">
2- Allow using nested objects like m.Grades.Math. The ParseTemplate method doesn't support this, so now it can only done by normal VB code.
In fact, I am wishing this simple template formula be used in XAML instead of current DataTemplate which is rather complex and verbose!
(Now I am thinking how Xaml make use of VB literals!.. VB literals can change the way we program!).
In Vazor, we have endless possibilities to add any features we want in the middle stage, to make vbxml code easier and more powerful. So, if you have any ideas, please share it.
@DualBrain @KathleenDollard @ericmutta @gilfusion @jasonmalinowski I created a VB.NET Razor Page app and used vbxml code to render the page! https://github.com/VBAndCs/VB.NET-Razor/tree/master/RazorPages1
I didn't use the virtual IFileProvider, because the routing system used in Razor pages opens the cshtml pages directly to get info about the model class! This means that we can not get rid of the cshtml files! So, I used a simple trick: I put the vbxml code in a property named VbXml in the IndexModel class, and used it in the Razor page like this:
@page
@model IndexModel
<div>
@Html.Raw(Model.VbXml)
</div>
And that is all! There is nothing C# in this Razor page, except it has the cshtml extension! And we even don't want the Vazor engine! This is a pure VB.NET/Razor project! VB.NET was capable on its own to write Razor pages and fully run on ASP.NET Core without any further work! Just a small workarounds! It is really a big surprise! By the way, the same approach can be used in MVC apps. I added the Vazor virtual file system to get rid of cshtml files. I think it is ok to keep them as just names, and inject vbxml code into them. I did that in this MVC Core project: https://github.com/VBAndCs/VB.NET-Razor/tree/master/WebApp1
Note: The start pages and layout must be pure cshtml. any part of them that contains C# code can be moved to a section or a partial view hence we can use the vbxml trick with it.
So, here is the fact: Vazor is just a technique not a real app! Still, we can add more functionality in the middle stage as I mentioned before, and of course we need the xml literals enhancements I mentioned in this thread, and I hope that we can have a .vbxml file to contain the view code and treated as a vb file that contains only xml literals, that can be loaded in the XElement by some method that accepts the file name as a parameter.
Now You can start to use VB.NET Web apps in production, and write pages with vbxml code!
All we need is a template for VB MVC Core and VB Razor Pages apps. And I hope VB team provide pull some strings with the ASP.NET Core team to add it in VS.NET 2019. This vbxml technique is built on the existing Razor, and will benefit from any future development of it (like Blazor), without adding any cost or effort on the ASP team! There will be some coast on VB team of course if they agreed to enhance xml literals, which is not a pressing matter at the moment, because every thing is already up and running!
VB was a real player on .net core from the beginning, but the coach didn't see his potential and kept away with no reason at all!
Using @html.Raw()
has a limitation: it doesn't resolve TagHelpers. So, this method valid only for pure vbxml that doesn't use Tag helpers!
I used another approach in this Razor Pages app:
https://github.com/VBAndCs/Vazor/tree/master/VazorPages1
I used the virtual file provider again. This will allow to write a vbxm layout, and with a small trick I injected the Index.vbxml page as a partial view in the Index.cshtml like this:
@page
@model IndexModel
<div>
@Html.Partial(Model.ViewName + ".cshtml")
</div>
Note that I have to map the each vbxml instance to have a unique name, which means that the partial view has no static name. So I put name returned by the mapper in the ViewName property, in the OnGet method of the IndexModel class:
Public Class IndexModel : Inherits PageModel
Public Property ViewName As String
Public Sub OnGet()
Dim iv = IndexView.CreateInstance(Students, ViewData)
ViewName = Vazor.VazorViewMapper.Add(iv)
End Sub
End Class
Note I reported the unexpected behavior of the Razor Pages with the razor pages, and hope they solve this issue.
Now, we can write MVC and Razor Pages apps using VB.NET, and create the views in three ways:
ForEach = "m"
template.This is the most flexible view engine ever, yet it came out of the box!
Unfortunately, the xsd schema for XML literals doesn't work currently in Roslyn (seems it was forgotten while moving to Roslyn) as reported here: https://github.com/dotnet/roslyn/issues/34816 I was trying to add html auto complete using html5.xsd I got. Currently, I added a project and item templates for Vazor: https://github.com/VBAndCs/Vazor/blob/master/VazorTemplateSetup.zip?raw=true Refer to ReadMe.md to how to use them. The reaming work is to enhance XML as I requested in this issue. This is wher I can't move forward alone. I will focus on testing Vazor on web projects.
I have a new idea to implement Vazor view, called ZML. vbxml code is not compatable with Tag Helpers. I used workarounds to make it work, but the resulted code is longer than it should! So, I decided to expand my data template idea but in another direction: Writing structural code as xml tags, so we have a new ZML Razor layer built on tip of C# Razor! I already implemented it in Vazor 1.5 and hope it can be implemented in .NET, so please support this proposal (all details are there): https://github.com/aspnet/AspNetCore/issues/9270 Thanks.
I faced a new xml literals limitation: Is refuses to use > and < in attr values in many cases! This is a limitation of XML specification itself which doesn't exist in HTML5! This expression can't be used in VB (or in any XML parser!):
Dim x = <if condition="a>3 and y<5">
<p>x = 4</p>
</if>
I use this workaround:
x = <if condition=<%= "a>3 and y<5" %>>
<p>x = 4</p>
</if>
But with all xml literals limitations, I find using ZML pages better:
<%= %>
You are required to escape them
& <-> &
< <-> <
> <-> >
" <-> "
' <-> '
@AdamSpeight2008
I know, but I am seeking for a friendly way for writing vbxml code, becuase it is not a one time job, but a way to designh web views and pages. Embeding thse symbols in <%= %>
forces vb to automatically escape them, so it does the trick and keep readable.
Thanks.
XElement has a formating issue with xml containing literal strings as explained here: https://github.com/dotnet/corefx/issues/36871
krwq offered a solution by using:
XElement.Parse(s, System.Xml.Linq.LoadOptions.PreserveWhitespace);
but this works only with xml written in string literals. XML literals are parsed by VB, so can vb solve this issue by using the PreserveWhitespace option?
@AdamSpeight2008 I know, but I am seeking for a friendly way for writing vbxml code, becuase it is not a one time job, but a way to designh web views and pages. Embeding thse symbols in
<%= %>
forces vb to automatically escape them, so it does the trick and keep readable. Thanks.
The thing to remember is that XML has rules, which VB's literals have to comply with, otherwise their usefulness is grossly undermined.
As programmers, we sometimes have to live with external constraints, and learn how to deal with them. This is an example of one.
I suggested a way to embed code blocks directly in xml literals here https://github.com/dotnet/vblang/issues/422
The second thing to do, is to add intellisense support for html attributes in xml literals in VB!
Finally done :). Details in: https://github.com/dotnet/vblang/issues/527#issuecomment-610232087
In this proposal https://github.com/aspnet/AspNetCore/issues/8674 (please all VB.NET fans, support it), I suggested to use xml literals to implement ASP.NET MVC razor in VB.NET instead of vbhtml files, like this:
In this code, I used LinQ because I need to return a value to be embedded in the xml literal. I tried another way:
Using inline lambdas like this, makes it possible to evaluate any expression with any VB code we want, but I think this can be simplified and shortened as this:
This is more like a vbhtml razor page code! The suggestion is simply to write any vb.net code directly inside the
<%= %>
marks, and use return or Yield to return the desired value that will be used in the xml literal. Return and Yield here are scoped to the Xml literal, and all what VB.NET needs to do, is to wrap all this insideWhere [Iterator] is added if Yield is used in code. Another example:
Which is a shortcut for:
I hope these proposal are taken seriously. VB.NET is too powerful and has many precious hidden treasures, and it is really unfortunate that MS decided to left VB.NET behind to save some effort and money, while VB.NET could have been saved too much time , effort and money spent to develop Razor syntax, while it is already supported in VB syntax!