How to pass data from asp.NET MVC to Angular2 - c#

What is the best way to pass data from an ASP.NET MVC controller to an Angular 2.0 component? For example, we use the ASP.NET MVC Model and would like to send a JSON version of it to Angular to use it in Angular.
When the controller is serving the view, we can already push some data to Angular2 (the model). So additional AJAX call to fetch that data is not required.
However, I am struggling to "inject" it into the Angular component. How do you do this? Any good references for this? As you may have noticed, I'm quite new to Angular2.
My index.cshtml looks like this.
<div class="container">
<div>
<h1>Welcome to the Angular 2 components!</h1>
</div>
<div class="row">
<MyAngular2Component>
<div class="alert alert-info" role="alert">
<h3>Loading...</h3>
</div>
</MyAngular2Component>
</div>
</div>
Kind regards,
Rob

The best way that I have found to pass data in from MVC (or any hosting/startup page) is to specify the data as an attribute on the bootstrapped component, and use ElementRef to retrieve the value directly.
Below is an example for MVC derived from the this answer, which states that it is not possible to use #Input for root-level components.
Example:
//index.cshtml
<my-app username="#ViewBag.UserName">
<i class="fa fa-circle-o-notch fa-spin"></i>Loading...
</my-app>
//app.component.ts
import {Component, Input, ElementRef} from '#angular/core';
#Component({
selector: 'my-app',
template: '<div> username: <span>{{username}}</span> </div>'
})
export class AppComponent {
constructor(private elementRef: ElementRef) {}
username: string = this.elementRef.nativeElement.getAttribute('username')
}
If you want to retrieve more complex data that is not suitable for an attribute, you can use the same technique, just put the data in a HTML <input type='hidden'/> element or a hidden <code> element, and use plain JS to retrieve the value.
myJson: string = document.getElementById("myJson").value
Warning: Access the DOM directly from a data-bound application such as Angular, breaks the data-binding pattern, and should be used with caution.

You might want to look for similar questions related to AngularJS, not Angular 2 specific, as the main gist of the thing remains the same:
you want your server-side Razor engine to render some kind of view (i.e. HTML or JS directly)
this view contains a JS template where part of the content is filled from a server model instance or anyway server data (e.g. a resource, dictionary, etc.)
in order to properly fill a JS variable from Razor, C# server-side data has to be properly serialized into a JSON format
In this post by Marius Schulz you can see as he serializes the data and uses that to fill a template AngularJS value component:
<script>
angular.module("hobbitModule").value("companionship", #Html.Raw(Model));
</script>
Something similar could be made to inject some data e.g. into window.myNamespace.myServerData, and then have Angular2 bootstrap that value among other providers.
In this post by Roel van Lisdonk, a similar approach is used, again, to fill an AngularJS-based template, with that ng-init directive:
<div ng-controller="main"
ng-init="resources={ textFromResource: '#WebApplication1.Properties.Resources.TextFromResource'}">
<h1>{{ title }}</h1>
{{ resources.textFromResource }}
</div>
As the first post points out, there's something to think about (pasting here):
A word of caution: The method I'm about to use is probably not a good fit for large amounts of data. Since the JavaScript data is inlined into the HTML response, it's sent over the wire every single time you request that page.
Also, if the data is specific to the authenticated user, the response can't be cached and delivered to different users anymore. Please keep that in mind when considering to bootstrap your Angular Apps with .NET data this way.
The second part may be less of an issue if your served page is already dynamic server-side, i.e. if it already has bits filled in out of server-side data.
HTH

You need to first bundle your services and controllers in separate module files and load services before controllers.
For example:
dist
|--services.js
|--controllers.js
Then you need to load the JavaScript code of the Services via ASP.NET MVC JavaScript result, here you need to inject your startup data.
public class ScriptController: Controller
{
public ActionResult GetServices(){
string file= File.ReadAllText(Server.MapPath("~dist/services.js"));
//modify the file to inject data or
var result = new JavaScriptResult();
result.Script = file;
return result;
}
Then in the index.html load the scripts as follows
<script src="/script/getservices"></script>
<script src="/dist/controller.js"></script>
This way you can inject data into angular code while loading.
However, even this has a performance impact due to time spent on fetching the view, compiling the view, and binding data to the view. For an initial load performance can still be improved if you use Server Side Rendering.

You can use the Input function exposed by #angular/core, I have for example an Angular 2 component to display information messages to the user of the application
My HTML template take an Angular 2 Message property
<div class="alert alert-info col-lg-10 col-lg-offset-1 col-md-10 col-md-offset-1 col-sm-10 col-sm-offset-1 col-xs-10 col-xs-offset-1">
<i class="fa fa-info-circle"></i> {{ Message }}
</div>
The Message property is passed as an input to my Angular 2 component named informationmessage.component.ts, for example
import { Component, Input } from '#angular/core';
#Component({
selector: 'informationmessage',
templateUrl: '../Templates/informationmessage.component.html'
})
export class InformationMessageComponent {
#Input() Message: string;
}
I then pass the data to my InformationMessageComponent using property binding in the HTML page, for example.
<informationmessage [Message]="InformationMessage"></informationmessage>
You can replace InformationMessage in the above example with the data that you get from your MVC controller for example
<informationmessage [Message]="#Model.InformationMessage"></informationmessage>
Please note: I did not test this scenario, but there is no technical reason for it not working, at the end of the day you are just binding a value to an Angular 2 property.

src/ApplicationConfiguration.ts
export class ApplicationConfiguration {
public setting1: string;
public setting2: string;
}
src/main.ts
declare var config : ApplicationConfiguration;
var providers = [{ provide: ApplicationConfiguration, useValue: config }];
platformBrowserDynamic(providers).bootstrapModule(AppModule)
.catch(err => console.log(err));
src/index.html
<script type="text/javascript">
var config = {setting1: "value1", setting2: "value2"};
</script>
src/app/app.component.ts
export class AppComponent {
private _config : ApplicationConfiguration;
constructor(config: ApplicationConfiguration) {
this._config = config;
}
}

I found a much simpler solution. Don't attempt to get the attribute from in the constructor! Use the ngOnInit() hook instead. The property will be accessible as long as it has been decorated with #Input(). It just appears that it is not available by the time the constructor is called.
Component HTML:
<MyComponent [CustomAttribute]="hello"></MyComponent>
Component TS:
export class MyComponentComponent {
#Input()
public CustomAttribute: string;
constructor() {}
ngOnInit() {
console.log("Got it! " + this.CustomAttribute);
}
}

Further to #Joseph Gabriel's approach, if you want a complex object to be passed via an attribute, in MVC you should serialize it to a string, and then in the angular side de-serialize it using JSON.Parse.

Related

Blazor Dynamic Custom Elements in application

I'm developing a webapp built in C# with Blazor WASM that is Asp.Net hosted. I'm making a blazor component that through the use of a library already in production, will generate a HTML fragment (or full embed) that is then displayed in this way
...
<div>
#((MarkupString)document)
</div>
...
with document containing the markup generated by the library.
As long as we're doing it with static content all is fine and dandy, but now we need to have some input in there that will then be sent back to the server to execute some actions.
In a MarkupString there is no way to include <InputFile /> or <InputText /> components in such a way that they are shown in the fragment and I can read their contents, and I can find no way to actually interact with the standard HTML tags, especially regarding the file upload.
Moreover we'll probably soon need to have a specific image uploader with preview which would be a custom Blazor component and this led me to the CustomElements .NET 7 feature that looks like what I need for both problems.
However I couldn't find how to actually implement this in my app, and the documentation I found is still very partial in that way. Is there a way to do what I need?
EDIT: Managed to fix this partially, with Chen's answer. I still have trouble with the binding though, as the #bind-Value directive is not working with
Unhandled exception rendering component: Microsoft.AspNetCore.Components.Forms.InputText requires a value for the 'ValueExpression' parameter. Normally this is provided automatically when using 'bind-Value'.
with this markup:
...
<custom-input-text #bind-value="$field1" name="$field1"></custom-input-text>
...
(the capital V in bind-Value becomes lowercase all by itself)
Am I doing something wrong again?
CustomElements should meet your requirements, you can create your own logic in Blazor components, and then use it in your application.
To use the component, you need to add the following JavaScript script references to your host app in this specific order.
<script src="_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
<script src="_framework/blazor.webassembly.js"></script>
You also need to add the corresponding middleware:
app.UseBlazorFrameworkFiles();
And use app.UseWebAssemblyDebugging(); for debugging.
Then you need to register the corresponding component in the Blazor program:
builder.RootComponents.RegisterCustomElement<Counter>("my-counter");
Then you can call this component in your application, including passing parameters, etc.
<my-counter title="Khalid" increment-amount="2" />
Here is a complete example with detailed explanation, you can use it as a reference.
Helpful links:
Blazor Custom Elements.
Using .NET 7's Blazor Custom Elements to render dynamic content.
ASP.NET Core Razor components.
Hope this can help you.
Edit1:
It looks like you can't do two-way binding between Razor Page and Razor Component. The official document mentions that parameters can be passed through JavaScript properties, but it doesn't seem to be able to read the parameters.
For example:
<my-counter ></my-counter>
<button onclick="Test()">click</button>
<script>
function Test()
{
const elem = document.querySelector("my-counter");
//get parameter failed, it's undefined
var text = elem.incrementAmount;
//successfully set parameter
elem.incrementAmount = "test";
}
</script>
So I'm guessing that CustomElements only have writable properties in Razor Page.
Edit2:
I found that JQuery can be used to detect the value of the input box. When getting this value and performing certain operations, can it also achieve the same effect as two-way binding?
<my-counter ></my-counter>
<button onclick="Test()">click</button>
<script>
function Test()
{
const elem = document.querySelector("my-counter");
//get the value of input
var value = $("my-counter").find("input").val();
elem.incrementAmount = "test";
}
</script>

Convert Blazor HTML markup portion to string for email body

I have a simple Blazor component that take some inputs (some lists of object and few strings) and formats them into simple HTML for display, (tables generated from the lists of objects, simple text, etc.).
This HTML is a report that is intended to be both displayed to users in the app and also emailed to various people (via SendGrid). For compatibility, we are keeping the email HTML as simple as possible.
The component works fine, however I am not sure how to translate a component's markup portion into a simple string of escaped HTML so that I can pass the string to SendGrid and fire off an email.
I am aware of MarkupStrings, but I have only used them in reverse--to write a string containing HTML tags that will be properly displayed in my app. I can't find any suggestions for doing the conversion the way that I need it done.
Is there any simple way to have a component write all of its markup into a string so that I can email it out?
Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?
The simplest way to do that is to employ JSInterop to retrieve the Html markup for the component, produced by the browser. Let's say You've defined a child component, and you want to retrieve its html source. You can do that like this:
Define the child...
SelectGender.razor
<div id="selectGender">
<h1>Select Gender</h1>
<select>
#foreach (var gender in genders)
{
<option>#gender</option>
}
</select>
</div>
#code {
private List<string> genders = new List<string> { "Male", "Female", "Other" };
}
Usage
#page "/"
#inject IJSRuntime JSRuntime
<div>#((MarkupString) html)</div>
<SelectGender />
<button #onclick="GetHtml">Get Html</button>
#code{
private string html;
protected async Task GetHtml()
{
html = await JSRuntime.InvokeAsync<string>("myJsFunctions.getHtml");
}
}
_Host.cshtml
<script>
window.myJsFunctions =
{
getHtml: function () {
return document.getElementById("selectGender").innerHTML;
}
};
</script>
Is there any simple way to have a component write all of its markup into a string so that I can email it out?
No, your C# code has no simple way to do this - you could use JS Interop to get the rendered HTML from the dom, but nothing built in for it.
Or, would I be better off writing a .cs file with a static method that takes in the parameters in question, renders it into a MarkupString, and then passes the string both to SendGrid for email and also to a Blazor component for in-app display?
That is a possibility - I can't comment on the value of that to you, but it is a technique that could work if the component you are rendering is static,

Need to pass parameter to multiple Partial Views on single view page

I am trying very hard to rewrite this question better than my previous effort which received no responses. Even though I’m nearly done with this application, I am still a relative newbie at programming and it seems like one challenge just leads to another. I have looked at many posts related to the problem of passing a parameter to several Partial Views in a single view page. So let’s take this in order from the AlertPick.cshtml page where the user chooses one of three Alert_Identifier/SelectedAlertIndex parameters from the application database. I’m only showing the #model and Select Tag Form.
#model edxl_cap_v1_2.Models.ContentViewModels.EdxlCapMessageViewModel
#{
<h4>#Model.Alerts.Count Alerts</h4>
<form asp-controller="Alerts" asp-action="PickAlert" method="post">
<select class="cap_select" id="cap_select" style="width:100%;max-width:95%;"
asp-for="SelectedAlertIndex" asp-items="Model.Alert_Identifiers">
<option>Select one</option>
</select>
<br />
<input type="submit" name="PickAlert" value="Pick Alert to Assemble EDXL-Cap Message" />
</form>
}
This takes the user to thePickAlert.cshtml page, a table of five rows where the first four rows are the Data Categories of the application: Alert, Info, Area and Resource each with the Alert_Identifier repeated as a reminder in a text box followed by its own submit button named Check Alert, Check Info, Check Area, and Check Resource, respectively. These submit buttons take the user to a _DetailsAlert.cshtml, _DetailsInfo.cshtml, _DetailsArea.cshtml, and _DetailsResource.cshtml pages and they work correctly, with the data item names and values from the record that matches the Alert_Identifier. The fifth row repeats the Identifier and its button reads Add All, to assemble the whole set together for review and takes the user to the_Assemble.cshtml page below, where the individual data categories are correctly assembled with the data item names, but lack the correct data values that match the record that corresponds to the Alert_Identifier. I’m thinking that I need to add a third parameter for the SelectedAlertIndex or Alert_Identifier to each of the #Html.Partial(...) Views, but I haven’t found the correct form/syntax for that, and If someone could supply that or point me to an example similar enough to this, I would deeply appreciate it.
#model edxl_cap_v1_2.Models.ContentViewModels.EdxlCapMessageViewModel
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width" />
<link rel="stylesheet" href="~/css/capv1_2_refimp.css" />
<title>Assembled EDXL-CAP Message</title>
</head>
<h4>Assemble EDXL-CAP Message</h4>
<!-- DetailsAlert -->
<div class="content-wrapper">
#Html.Partial("_DetailsAlert", Model.Alert)
</div>
<!-- End of DetailsAlert -->
<!-- DetailsInfo -->
<div class="content-wrapper">
#Html.Partial("_DetailsInfo", Model.Info)
</div>
<!-- End of DetailsInfo -->
<!-- DetailsArea -->
<div class="content-wrapper">
#Html.Partial("_DetailsArea", Model.Area)
</div>
<!-- End of DetailsArea -->
<!-- DetailsResource -->
<div class="content-wrapper">
#Html.Partial("_DetailsResource", Model.Resource)
</div>
<!-- End of DetailsResource -->
Responding to first comment below, I'm showing the InfosController.cs code for _DetailsInfo(int? id) the controller action for the Info Data Category. It is virtually identical for each of the data categories except that the line ... .SingleOrDefaultAsync(m => m.InfoIndex == id); becomes ....SingleOrDefaultAsync(m => m.AlertIndex == id); and the method itself becomes_DetailsAlert(int? id).
// GET: Infos/Details/5
public async Task<IActionResult> _DetailsInfo(int? id)
{
if (id == null)
{
return NotFound();
}
var info = await _context.Info
//.Include(e => e.Elements)
// .ThenInclude(d => d.DataCategory)
.AsNoTracking()
.SingleOrDefaultAsync(m => m.InfoIndex == id);
if (info == null)
{
return NotFound();
}
return View(info);
}
PickAlert method from AlertsController follows:
public IActionResult PickAlert(Alert obj, int? SelectedAlertIndex)
{
if (SelectedAlertIndex.HasValue)
{
ViewBag.Message = "Alert loaded successfully";
}
return View(_context.Alert.Where(x => x.AlertIndex == SelectedAlertIndex));
}
I am not sure if I got the requirement correctly, but I think you have to create another model for all 4 partial views, e.g. for Alert, create a new model
class AlertModel:EdxlCapMessageViewModel
{
int SelectedAlertIndex {get;set;}
}
And then your view would look like:
<!-- DetailsAlert -->
<div class="content-wrapper">
#Html.Partial("_DetailsAlert",new AlertModel { Alert = Model.Alert,
SelectedAlertIndex = <ID SOMEHOW>
});
</div>
In .net core when I need to pass around a lot of data across the views, I usually find it cleanest to use services and DI. First, you can create a class that could store a set of data:
class MyDataForViews {
// the following is an example. You can have any properties
public string Info { get; set; }
}
You now have to add this class as a service. To do so go to your startup class and add the following within the services function:
services.AddScoped<MyDataForViews>();
Scoped means that the framework will create a new object of MyDataForViews for each HTTP request. No matter how many places you "inject" an object of MyDataForViews, it would use the same object across the current HTTP request. You can also replace the function with AddSingleton if you want to use the same object throughout the web app. The following is how you inject an object into your controller:
public class MyController : Controller
{
MyDataForViews myData;
// in controllers injection is done using the constructor
public MyController(MyDataForViews MyData) => myData = MyData;
public IActionResult Index()
{
myData = .... // assign all required data here
View();
}
}
Once this is done, instead of passing models to each view, you can inject the data into views using the following:
#inject MyDataForViews MyData;
Once you use this line on the top of any view, you can use the MyData object and there is no need to pass models to each partial view.
Here's a bit more detailed answer, since you've said at softwareengineering.stackexchange.com site that you still need help with this.
Let's first make sure you understand the basics correctly.
When it comes to passing data to the view, each controller in ASP.NET MVC has a property named ViewData, which is essentially a dictionary of key-value pairs. The ViewData itself has a property called Model, and this is what you access in the page using the Razor syntax #Model. You can use this property to pass a model that is strongly-typed, to avoid using magic strings for the keys of ViewData.
Note: ViewBag is a dynamic wrapper around the ViewData, so it's essentially the same thing (ViewBag.SomeProperty is the same as ViewData['SomeProperty']); the use of ViewBag is discouraged, though.
In a controller action when you do something like return View(), ASP.NET uses the cshtml page as a template to create actual HTML, and return it as the response to the client (this is all server-side).
There are a few ways to pass data to the view which are equivalent, for example:
ViewData.Model = someObject;
return View();
is the same as:
return View(someObject); // the View method can accept a model object
When it comes to partial views, by default, they get passed a copy of the parent page ViewData (this includes the reference to the Model), so you don't have to do anything special to pass this data to a partial view (but you can pass data of your choice if you want to).
The select tag helper renders (generates HTML) for the select element with the options specified. This is then sent as HTML to the client. On the client side, when the user clicks the submit button, a POST request is sent to the server, which is ultimately handled by the method PickAlert method on the AlertsController. If everything is setup correctly, you should get the SelectedAlertIndex as the parameter. Note that this is happening back at the server side, and that you now need to again return a page as the response.
You can pick the corresponding Alert object from your _context. Use the FirstOrDefault method for this instead of Where, as you only need a single item (convert types for comparison if necessary - e.g., if you have a string, but you are comparing to an int, or something along those lines).
var selectedAlert = _context.Alert.FirstOrDefault(x => x.AlertIndex == SelectedAlertIndex);
Now, all you need to do is set this selectedAlert and any other data that you need as a property on your model object (or under some key in ViewData), and render the correct view.
Note that if you just return View(model) without specifying the name of the view, the system will look for a view with the same name as your action method (here, PickAlert.cshtml), so use return View("ViewName", model) to change that if necessary.
For example, based on the code you've posted in your question, you could do something like this:
[HttpPost]
public IActionResult PickAlert(int? SelectedAlertIndex)
{
var model = new EdxlCapMessageViewModel(/* ... params, if any */);
if (SelectedAlertIndex.HasValue)
{
ViewBag.Message = "Alert loaded successfully";
var selectedAlert = _context.Alert.FirstOrDefault(x => x.AlertIndex == SelectedAlertIndex);
// I added a property to your model to store the alert;
// if you already have one, just use that one instead.
model.SelectedAlert = selectedAlert;
}
return View("YourViewName", model);
}
The YourViewName should be the parent view that has the partial views in it (the "Assembled EDXL-CAP Message" view, I presume).
BTW, I know that the way the system is passing the parameters to the action methods in a controller may seem a bit like magic, but it's convention-based. In the example above, it works because the parameter is named SelectedAlertIndex, and the model object has a property with the same name (and because you've specified that property in the select tag helper using asp-for="SelectedAlertIndex"). You can also modify the method signature so that it receives the entire model object (assuming that the model class is not too complicated - you can read more about how parameter binding works here):
[HttpPost]
public IActionResult PickAlert(EdxlCapMessageViewModel model)
{
// extract the index from model.SelectedAlertIndex
// you can also pass this same model object to the view
// (set some properties first if necessary)
// ...
}
Now for the partial views. Assuming that you are relying on the default mechanism which passes the parent ViewData to each partial view, you need to modify each partial view so that the code is written under the assumption that you can access the selected alert using #Model.SelectedAlert (the property you've set in the PickAlert action).
For example, a here's a simple partial view:
<div style="border: solid 1px #000000; padding: 30px; margin: 2px 2px 10px 2px;">
<p>The selected index is: #Model.SelectedAlert.AlertIndex</p>
</div>
Note that I'm just using the same model as in the parent view to access the SelectedAlert object: #Model.SelectedAlert.AlertIndex.
Again, when rendering the partial views, if you pass no additional parameters, they'll get a copy of the ViewData dictionary, and the same Model:
#Html.Partial("_DetailsAlert");
If you pass something else as the model, e.g., only the selected alert, then you need to change the partial view code accordingly:
#Html.Partial("_DetailsAlert", Model.SelectedAlert);
<div style="border: solid 1px #000000; padding: 30px; margin: 2px 2px 10px 2px;">
<p>The selected index is: #Model.AlertIndex</p>
</div>
Note that now, in the partial view, the local #Model refers to what was #Model.SelectedAlert in the parent view. (In other words, here #Model is of type Alert.) This only affects the ViewData.Model property; the key-value pairs stored in ViewData are still the same as those in the parent view.

How to send server side CSS and JS to client side in .cshtml file

I'm new at MVC application development.I'm working on Visual studio ASP.NET MVC4 project. I want to send my server-side CSS and JS to the client side in a .cshtml file. How the work will be done. Is it right way of doing this?
Controller method:
public ActionResult GetCss()
{
Response.ContentType = "text/css";
return View("Css1", new FontSpec {FontName = "Arial"});
}
View:
#model MvcApplication2.Models.FontSpec
.foo
{
font-family: #Model.FontName;
}
Simplified Model:
public class FontSpec
{
public string FontName {get; set;}
}
The important part is to set the ContentType in the controller method. The model can be used to pass dynamically generated data to your view, making your CSS truly dynamic.
In your page view (the view that actually generates the page) you can use the following link tag:
<link rel="stylesheet" href="#(Url.Action("GetCss", "Home"))"/>
And finally, of course, you can use your CSS classes in your tags:
<span class="foo">This will be in your specified font.</span>
You can, of course, generate your CSS by any means you like, as long as it is legal CSS at the end of the processing. This will also work for generating dynamic Javascript, but of course you have to change the content type you use.

How to use Razor instead Jquery ajax to get data from a function

I never use Razor in asp.net MVC so I really don't know how it works.
Every time I want to tag a function in my Controller I use jquery ajax and I write very javascript code and my projects are very messy.
Now I want to make something different.
I have controller named as HomeController where I get data from Sql Server Database, using model entity for that.
public class HomeController : Controller
{
public JsonResult getClasses()
{
IList<Class> classes = (from x in db.Class select x).Distinct().ToList();
return Json(classes, JsonRequestBehavior.AllowGet);
}
public JsonResult getTypes(string className)
{
IList<String> allTypes = (from type in db.Type
where type.class_name == className
orderby type.type_name
select type.type_name).Distinct().ToList();
return Json(allTypes, JsonRequestBehavior.AllowGet);
}
}
And I have master.page (named as SiteMaster) where I get this data through jquery ajax.
<body>
<div id="header">
<ul class="classesName"></ul>
</div>
</body>
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: "/Home/getClasses",
type: "GET",
data: {},
success: function (result) {
for (n = 0; n < result.length; n++) {
$('.classesName').append('<li><a href="#" onclick="showTypes( \'' + result[n].class_name + '\')" >' + result[n].class_name + '</a></li>');
}
}
});
</script>
I have another ajax for getting the Types when some Class is clicked..but all this I want to change so I can use #Razor.
All I want is to show the data from getClasses() into the masterPage and when some Class is clicked, to send that argument into getTypes(string className) but without using javascript and use #Razor.
So my question is can I do this and how?
The first part (sending the result of getClasses() to your view) can be done with Razor alone, because all of that work can be done on the server.
It is important to understand that the primary strength of Razor is that it allows you to perform (almost, if not) any C# on your views. So doing what you want can be as simple as writing the code as if you were coding in C#:
#{
// I'd suggest you refactor your code to have your `getClasses()` function
// readily available somewhere that's not a controller
var classes = Data.getClasses();
}
<div id="header">
<ul class="classesName">
#foreach (var clazz in classes) {
<li>
#clazz
</li>
}
</ul>
</div>
For the second part (getting the Types when the link is clicked), you're out of luck. The click has to happen on the client, so by then your C# would have been evaluated, the HTML flushed, and the time for your Razor to run would have come and gone. If those types aren't cached on the page somehow, you'll have to make a request against the server again (and AJAX is one way to do that).
You don't need to use ajax to load the initial master page, just return a view containing all the class data you need to display as that saves you the extra trip to the server. As for getting the data when a class is clicked that is an ideal place to use Ajax and to do that in Razor you would use one of the many #Ajax.xxx helpers (#Ajax.RawActionLink, #Ajax.BeginForm, etc.). The following link might be a good place to start looking at how views are used in asp.net MVC views tutorial

Categories