Public variables in MVC 3 Razor _ViewStart - global-variables

I'm building a site on the new Razor engine that comes with MVC 3 (and loving the new syntax!). However, I am at a loss about using public properties / constants with it. I know that with WebForms we could add a public property in code behind:
public string ImageFolder { get; set; }
I would like to define important variables in one global place that my views can access, starting with paths to CSS files and images:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
var ContentFolder = "~/Content";
var CssFolder = ContentFolder + "/Stylesheets";
var ImageFolder = ContentFolder + "/Images";
}
I have tried putting the above code block in _Layout, as well as inside _ViewStart. However, accessing them from child views fails miserably. I thought of defining a public property in the above code block but it doesn't compile.
Solutions?
As far as I have seen, noone uses code behind with Razor.
I guess I should be able to inherit from the default view and define my properties there (as described on Stack).
But I'm strongly hoping that there should be an easier way to do something so simple?

I decided to follow yet another path, and extended UrlHelper to provide paths to all three folders I think I might need:
public static class ExtensionMethods
{
private const string ImagesFolder = "~/Images";
private const string StylesheetsFolder = "~/Stylesheets";
private const string ScriptsFolder = "~/Scripts";
public static string Images(this UrlHelper url)
{
return url.Content(ImagesFolder);
}
public static string Stylesheets(this UrlHelper url)
{
return url.Content(StylesheetsFolder);
}
public static string Scripts(this UrlHelper url)
{
return url.Content(ScriptsFolder);
}
}
All good to go... almost :-) I'm now wondering if there's a place where I would be able to define the using MyNamespace.Helper statement would go in order for these extension methods to be available application-wide. In the old days we would add an entry in web.config:
<system.web>
<pages>
<namespaces>
<add namespace="MyNamespace.Helper"/>
</namespaces>
</pages>
</system.web>
This doesn't seem to work with Razor :-( I tried adding a using statement in _ViewStart.cshtml but no luck either - the only way for my extension methods to be visible is to add a using statement on a particular page, which again isn't ideal.
Any suggestions? Have any of you seen an explanation of Razor's order of page parsing & delivery?

Your can create a folder "App_Code" and create a file "GlobalVal.cshtml".
bellow is a sample code in the file:
#functions{
public static readonly string __siteHome = "http://www.example.com";
public static readonly string __siteResource = "http://resource.example.com";
}
and bellow is a sample using it:
#GlobalVal.__siteHome

Use the PageData property:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
PageData.Add("ContentFolder", "~/Content");
}
and inside _Layout.cshtml:
<%=PageData["ContentFolder"]%>

In _layout view
#{
App.AnyName = "abc";
}
In Inherit view
#{
var anyVariable = App.AnyName;
}

Just place the constants in a public module inside your app_code folder, or if you don't want to do that just create a clasas in app_code and use the using (imports) keyword to import the namespace (class name) in each view and you can use it that way.
Alternatively, if it makes sense to do so, just add them in your view model - remember, it might not make sense to add those vars to your model, but it can make sense to add them to your view model! This is what the view model is for, and this view model can grab the constant values from a public module or class or you can even set it in your actual view model itself, this way you will only define the values in one place and you don't need to use any namespace imports into each view :)
Let me know how it goes and if there is anything else I can do to help you out.
In vb.net but same as csharp and its easy to understand since it's vb.
Public class YourModel
// this is where you have the normal model you have... No big deal
End Class
...
// now you make the view model urself
...
Public class MyViewModel
Public MyNormalModel as YourModel
//notice we r declaring ur normal model as a variable, u can use a property instead
Public MyPathConstant1 as string = "abc"
Public MyPathConstant2 as string = "abc"
Public MyPathConstant3 as string = "abc"
End Class
Now, you gotta set the value of MyNormalModel to ur current model instance, although you can do that in ur controller, it's best practice to create a method inside the MyViewModel class that takes a copy of ur current model as argument and does the setting of MyNormalModel to the current model we just passed in the argument.
You can still make that call in your controller, but on another note, what people prefer to do is, instead of passing the whole normal model as a property, the just take the bits and pieces they need from the normal model and place them into the view (ie: you might just need half the properties in the normal model to be in the view model). This is because, remember, the view model will be passed to the view and they don't wanna pass things they wont use :). But this means you are going to need to set each of those properties one by one most likely (unless those exact ones are encapsulated in a sub class which usually doesn't happen by chance lol).
I kept it in one so you can just copy the normal model over in one shot for simplicity.
Now when you pass the view model to your view (MyViewModel) you will be able to use and access the normal model through the object notation and it's properties, eg... Model.MyNormalModel.Property1. Etc and do whatever you want with it in the view... Also, you can access the rest of your view model (the const values that we set) like this... Model.MyPathConstant1 and Model.MyPathConstant2 etc... So you have access to practically everything you want, ur normal model and whatever else you added later on all through what is now called your view model.
Please excuse typos -writing from and ipad lol. Let me know if this is making more sense.

You could use the built-in property of UrlHelper Content:
#Url.Content("~/Content/Stylsheets")
#Url.Content("~/Content/Images")

Related

Entity Framework Class Manipulation

I'm using Entity Framework (DB First) on a new project and wanted to add some customisation to the classes generated. However, my changes are obviously lost every time that the edmx is refreshed. I was just wondering if there is a design pattern for handling this sort of thing?
As an example, suppose I have a class with a integer property; StatusID - and I'd like to extend the entity class so that the status value can also be accessed/set via the related enum and finally a property that gets a text representation of that Enum from the description attribute. This all works, but those customisations are lost when the model is refreshed. I appreciate that the property can be converted to an enum, so the latter property that gets the description of the enum is perhaps a better example for this question.
I think I know the answer but I just wanted to put this out there in case there were some magic tricks that would allow this to work and prevent those customisations from being lost.
public int StatusID { get; set; }
public Enumerations.ValidationStatus StatusEnum
{
get
{
return (Enumerations.ValidationStatus)StatusID;
}
set
{
StatusID = (int)value;
}
}
public string StatusText
{
get
{
return MyMethodThatGetsTheEnumDescription(StatusEnum);
}
}
Two Solutions to work around the problem:
User Data Transfer Object(DTO) nd put the enum there. then use Automapper or manually map between the DB Model and the DTO Model (best practice)
Instead of enum you can use extension functions on the model and define your getter, setters and any extra properties you want as extension functions to the class
(will add some complexity to your models)

How can I edit the default TestNG html report

I don't want to create a new one or a custom listener. Is that possible? Where is the html report created in TestNG?
SuiteHTMLReporter [source] is the reporter creating the html report. You can extend and override. Disable default listeners and add your own.
I know this is old , but these reports can be edited and custom reports can be made like below. I have explained here how TestHTMLReporter can be edited
With your customReport You'd have to implement IReporter , extend TestListenerAdapter and override generateReport method if you want to implement a custom TestHTMLReporter . For other reporters you may have to do things a bit differently but the concept will remain the same. You'd achieve custom 'TestHTMLReporter' like below .
Create a CustomReport.java file in your project and copy-paste the whole content of TestHTMLReporter.java , change the name of file in getOutputFile method and it would look like below
public class CustomReport extends TestListenerAdapter implements IReporter {
#Override
public void generateReport(List<XmlSuite> xmlSuites, List<ISuite> suites,
String outputDirectory) {
}
...
//paste the content of TestHTMLReporter.java here
...
...
Make sure all your imports are in place from TestHTMLReporter.java
Now, in this file change as per your requirement . For ex: if you'd like to add the end time of each of the test then at the correct place in generateTable method add the below snippet
// Test class
String testClass = tr.getTestClass().getName();
long testMillis = tr.getEndMillis();
String testMillisString = Long.toString(testMillis);
if (testClass != null) {
pw.append("<br>").append("Test class Name: ").append(testClass);
// this line to add end time in ms
pw.append("<br>").append("End Time(ms): ").append(testMillisString);
// Test name
String testName = tr.getTestName();
if (testName != null) {
pw.append(" (").append(testName).append(")");
}
Then you'll get like below
Now, You'll get two reports one with default and the other with your file name.
The only thing now remains is switching off the default reporting listeners, so you get only one report. For that you can follow this or you may search for solutions. Hope this helps

How should I auto-fill the Html.EditorFor with a value in ASP.Net MVC3?

I'm just taking a look at ASP.Net MVC3 and in one of the auto-generated views for Create, it uses "Html.EditorFor(model => model.User)" to provide a text box for the user to enter their username. Ideally, I would auto-populate this with #User.Identity.Name.
What is the correct way to achieve this? Does Html.EditorFor allow me to automatically populate it in the view, or should I be setting that at the controller when passing it in?
I've found that if I change the Create method in the controller from this:
public ActionResult Create()
{
return View();
}
To this:
public ActionResult Create()
{
MyObject myobject = new MyObject();
myobject.User = User.Identity.Name;
return View(myobject);
}
This seems to work. Is this the correct way to do this?
Thanks in advance for any confirmation that I'm doing this right.
Absolutely, the assignment is fine.
This is absolutely the correct way. You define a view model (MyObject) containing the User string property, then have your controller action instantiate and populate this model and finally pass the view model to the view for displaying. It is also easy to unit test because the User.Identity property on the controller is an abstraction that could be mocked.
its a good way in this case, but if you bild a big project it's better to create a global model class where you will put all your models, not in controller.

Where to put custom object formatter in ASP.NET MVC application?

I'm teaching myself ASP.NET MVC, and am trying to figure out where best to put a function which takes in an Models.Address instance and returns an IHtmlString instance which reads something like:
Line 1<br />
Line 2<br />
City, State
The string could be used in many places throughout my project, so I don't want to write it in one view and have to keep copy-pasting it or attaching that view: By the same logic attaching it to ViewData seems like a bad idea. However, the HtmlEncode method requires an instance of server, which means I can't add the function to the model class either.
Is there a right place to put this - some sort of shared view? (does this belong in the master?) Or am I going about this all wrong?
My current best idea is to create a ViewModel with the method public IHtmlString FormatAddress(Address address, HttpServerUtility server), but I don't know if that's the ASP.NET MVC way to do it.
As this is presentation UI logic, the best place to put it is in a HtmlHelper class.
public static class HtmlHelper
{
public static HtmlString FormatAddress (this HtmlHelper, Address address)
{
string formattedAddress = // the formatted address...
return HtmlString.Create(formattedAddress);
}
}
then, from any view you simply call:
<%= Html.FormatAddress(address) %>
I usually create something like that as an Extension Method on a HelperExtensions class and then place that class in a Helpers folder at the top level of the site.
public static class HelperExtensions
{
public static Format(this Address address, HttpServerUtility server)
{
// Do the work to format here.
}
}

ASP.NET MVC: How to transfer more than one object to View method?

I finished NerdDinner tutorial and now I'm playing a bit with project.
Index page shows all upcoming dinners:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
return View(dinners);
}
In DinnerRepository class I have method FindAllDinners and I would like to add to above Index method number of all dinners, something like this:
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(dinners, numberOfAllDinners);
}
Of course, this doesn't work. As I'm pretty new to OOP I would need help with this one.
Thanks,
Ile
Create view model:
public class DinnerViewModel
{
public List<Dinner> Dinners { get; set; }
public int NumberOfAllDinners { get; set; }
}
public ActionResult Index()
{
var dinners = dinnerRepository.FindUpComingDinners().ToList();
var numberOfAllDinners = dinnerRepository.FindAllDinners().Count();
return View(new DinnerViewModel { Dinners = dinners, NumberOfAllDinners = numberOfAllDinners } );
}
You need to create a "wrapper" object that contains the objects you wish to pass as public properties of it. For instance, create an object called DinnerViewModel and give it two properties and set these with two properties, one a List called Dinners and one an int called DinnerCount. Then pass the DinnerViewModel to the view and you can then access Model.Dinners and Model.DinnerCount
In your case I would prefer the solution mentioned by LukLed.
In general you could of course also transfer multiple values from your controller to your view using ViewData:
ViewData["dinners"] = dinners;
ViewData["numberOfAllDinners"] = 150;
...
For more information also take a look at this link.
Just simply use dinners.Count property instead.
Remember, you start off using the ViewData inherts in you .aspx filesand returning the same in you return statements. Because of that, I figure that it was an issue with the Inherits attribute on the top of the ASP.NET files. But, if you are getting the error when trying to create or edit a new Dinner when you are on the 'Upcoming Dinners' page (generated from the Details.aspx and the LINQ file that gets all Dinners that are after todays date), go into your 'Controllers' directory, specifically the DinnerController.cs. Then look at the Edit and or Create methods. the answer lies right here. If you put breakpoints on these methods, you should be able to figure it out. If not, continue reading:
Look where it fails, the 'return...' line. Maybe I am the only person who forgot to change this, but my error is the same as people are getting in this page and this os how I fixed it.....the 'return(dinner)' line, in Create and Edit (and any others that you are having issues with), they are using the NerDinner.Model.Dinner / ViewData method. However, if you change it to the ViewModel return method instead, it should fix it, For example: 'return(new DinnerFormViewModel(dinner));', it should work for you. I hope this helps, as it was what my issue was. Just a simple overlook.

Resources