I should think this problem could come to anyone, I'm interested in ways of making the best use of technology to keep my code-base DRY and in sync.
I'm developing an ASP.NET MVC5 web application. I have several variables I need to be communicating to the client side. Say "PageType", which I need to be tracked on the client side using Google Analytics. The intent may be multifarious, this is a simple application with the same purpose, for illustration.
I have a C# class which allows me to specify the various PageTypes and also switch between them for switch-casing, say:
public static class PageTypes {
public const String Home = "Home";
public const String AboutUs = "AboutUs";
public const String ContactUs = "ContactUs";
}
This is useful when we check dynamic values, like:
String pageType = ViewBag.PageType;
if(!String.IsNullOrWhiteSpace(pageType))
{
switch(pageType)
{
case PageTypes.Home:
// do something here
break;
case PageTypes.AboutUs:
// do something else here
break;
...
... or even when doing comparisons. I hope you get the point.
So, in a Razor View, I can output var pageType = '#ViewBag.PageType'; in a page in an inline script, and it will be present as var pageType = 'Home'; or some such as the case, in the inline javascript of the page.
Now, just as I did with the C# code, I want to be able to switch-case the page type inside my javascript.
// This part is dynamic so will be inline script of the razor view
var pageType = 'Home';
// This part is logic so can reside in an external script
// This part is just illustration, I will worry about naming and accessibility later
// This will be in a separate script and will be included, and will be referenced, thereby providing Intellisense within Visual Studio
var PageTypes = {
Home: 'Home',
AboutUs: 'AboutUs'
};
// I should be able to do this, in an external script
switch(pageType){
case PageTypes.Home:
alert('Home!');
break;
case PageTypes.AboutUs:
alert('Not Home');
break;
default:
alert('Nothing');
break;
}
It can be seen that if the static class PageTypes of C# is in sync with the var PageTypes in the JavaScript, it would be a boon to our developers, with the Intellisense and predefined values, so that they may avoid typos and incompatibilities of values.
What's the best way to achieve this, while gaining Intellisense in both C# and JavaScript?
I would prefer changes in C# affecting JS rather than the reverse. The switch statements need not be converted, only the constant values like what I have mentioned.
I think a script in the build process could be useful but is there any tool/plugin/alternate way to accomplish what I have mentioned?
If I have understand what the problem is, what you can do is:
set the pagetype in some variable inside your html markup, using the template engine (sorry, I don't work with .Net, don't know how to write it). In this example, I used the data-* in the body element
< body data-type="%some template engine syntax that output the page type%">
in your js, retrieve the data-type value (in the example, I used jquery)
var type = $("body").data("type");
define some functions, each one for your pagetype, inside an key/object variable / object
var PageTypeFunctions = {
home: function() {
},
aboutus: function() {
},
xpto: function() {
}
}
invoke the function from the
PageTypeFunctionstype;
With this approach, you won't have to deal with the PageTypes variable. You will only need one function for each pagetype. Of course you can do some check to validate if PageTypeFunctions[type] exists.
Related
I have a C# file that pulls various config settings from different config files. A JavaScript application I'm writing needs some of those settings to run. I'm having trouble bringing the C# variables into the js file. Not sure what is the best approach. They need to end up as a JS object on the page. Would adding them to a JSon object in the C# file & calling that from the JS file work? Not even sure how to do that to be honest. Any help appreciated.
You can just declare a global variable in one of your root pages (say, _layout.cshtml):
<script>
var settings = {
foo: #(IsFoo ? "true", "false"),
bar: #SomeNumber,
baz: "#ImportantString"
}
</script>
Or, if you're writing a Web API, you can just add a /settings endpoint you can query like so (Assuming you're using jQuery):
$.get("/settings", response => {
// Store the `response` in a global variable.
});
If it's an independent javascript project and .net is more of an API, you need to make an API call which will send you those config settings.
If your javascript is part of .net MVC application, i.e., you are adding your javascript through an tag at then end of a .cshtml/.aspx file, you can easily pass it through a global variable, but even then the C# code need to pass those values through Modal to the .cshtml/aspx file.
what you can do is:
var pageConfig = pageConfig || {};
pageconfig.settings = #Html.Raw(Model.Settings); // This model object is part of the C# code and my assumption is that Settings will have array of configurations.
This can also be done globally, depending upon how the C# code is written.
Assuming that when you say 'various config settings' you're referring to .NET's ApplicationSettings (defined in Visual Studio's Properties > Settings), we've done something similar, in a generic manner, as follows:
public void WriteSettings(TextWriter writer)
{
// Declare the nameSpace for the DLL you want to pull settings from
var nameSpace = "foo.Bar"
ApplicationSettingsBase properties = (System.Configuration.ApplicationSettingsBase)Activator.CreateInstance(nameSpace, string.Format("{0}.Properties.Settings", nameSpace)).Unwrap();
foreach (SettingsProperty property in properties.Properties)
{
writer.Write(string.Format("{0}=\"{1}\", property.Name, properties[property.Name]);
}
}
I've ignored a few issues in your case:
I've not bothered trying to deal with non-strings; consider looking at property.PropertyType and casting your values appropriately
I've wrapped this in a function that accepts a TextWriter; you can pass this method Response.Output
Calling Javascript functions running inside Rhino from Java is easy enough - that after all is why Rhino was created. The thing I am having trouble establishing is this:
Context: I have a Phonegap CLI (v 6.3.3) Android project (API 19+) where I do a great deal of processing via loadable JavaScript running inside rhino
A Phonegap plugin - which I am creating at the same time as the actual Phonegap app - contains class called Storage which provides public, static, methods such as readFromFile(String fileName), writeToFile(String fileName,String data) etc.
What I want to be able to do is to call Storage.readFromFile etc from my loaded JavaScript code in Rhino.
Just how this should be done is not too clear to me. From the searches I have done thus far it involves using ScriptableObject.putProperty to pass the Java class in question, Storage in my case to JavaScript. However, how this should be done and then how it should be used at the JS end leaves me rather confused.
I would be most grateful to anyone here who might be able to point me in the right direction
Given that Rhino has less than 100 followers here it should perhaps come as little surprise that this question was not answered. In the mean time I have managed to find the solution myself and it turns out to be very simple. I share it below for the benefit of anyone else running into this thread.
My Storage class is very simple. It goes something like this
public class Storage
{
public static boolean haveFile(){}
public static boolean readFromFile(String fname){}
...
}
When I call Javascript from Java via Rhino I simply pass a new instance of the Storage class as the last of my function parameters
Context rhino = Context.enter();
Object[] functionParams = new Object[] {"Other parameters",new Storage()};
rhino.setOptimizationLevel(-1);
try
{
Scriptable scope = rhino.initStandardObjects();
String rhinoLog = "var log = Packages.io.vec.ScriptAPI.log;";
String code = /*Javascript code here* as shown separately below/;
rhino.evaluateString(scope, rhinoLog + code, "ScriptAPI", 1, null);
Function function = (Function) scope.get("jsFunction", scope);
Object jsResult = function.call(rhino,scope,scope,functionParams);
}
where the Javascript code is
function jsFunction(a,s)
{
//a - or a,b,c etc - here will be the "other" parameters
//s - will be the instance of the Java side Storage class passed above
//now you can do things like
s.writeToFile('fileName','fileData');
var fd = s.readFromFile('fileName');
s.dropFile('fileName');
...
}
I previously run into the problems of data hiding under modularization in JavaScript. Please see the links below:
Module pattern- How to split the code for one module into different js files?
JavaScript - extract out function while keeping it private
To illustrate the problem, see the example below. My goal is to split my long js file into 2 files, but some functions need to access some private variables:
first.js:
(function(context) {
var parentPrivate = 'parentPrivate';
})(window.myGlobalNamespace);
second.js:
(function(context) {
this.childFunction = console.log('trying to access parent private field: ' + parentPriavte);
}(window.myGlobalNamespace.subNamspace);
Now this wouldn't work because child doesn't have access to parent. One solution is to make parentPrivate publicly visible, but that is unacceptable in my case.
Quoting #Louis who gave an answer for one of the previous questions:
"We can't have a field that's accessible by child but not to outside
public (i.e. protected). Is there any way to achieve that?"
If you want modularization (i.e. you want the child to be coded
separately from the parent), I do not believe this is possible in
JavaScript. It would be possible to have child and parent operate in
the same closure but then this would not be modular. This is true with
or without RequireJS.
The problem is that the parent and the child are not inside the same closure. Therefore I'm thinking, does it make sense to create a library that puts files into the same closure?
Something like:
concatenator.putIntoOneClosure(["public/js/first.js", "public/js/second.js"]);
Of course we can take in more arguments to specify namespaces etc. Note that it is not the same functionality we get from RequireJS. RequireJS achieves modularization while this concatenator focuses on data hiding under the condition of modularization.
So does any of the above make sense? Or am I missing out some important points? Any thoughts are welcomed.
If you need things available in two separate files, then you can't have true privacy... however, something similar to this may work for you:
first.js:
(function(context) {
var sharedProperties = {
sharedProp1: "This is shared"
};
function alertSharedProp1() {
alert (sharedProperties.sharedProp1)
}
window[context] = {
sharedProperties: sharedProperties,
alertSharedProp1: alertSharedProp1
};
})("myGlobalNamespace");
second.js:
(function(parent, context) {
// CHANGED: `this` doesn't do what you think it does here.
var childFunction = function() {
console.log('trying to access parent private field: ' + window.myGlobalNamespace.sharedProperties.sharedProp1);
};
window[parent][context] = {
childFunction: childFunction
};
}("myGlobalNamespace", "subNamspace"));
window.myGlobalNamespace.subNamspace.childFunction();
Edit detailed answer based on comments
What I did was to set up a source file that looked like this:
master.js
(function() {
##include: file1.js##
##include: file2.js##
}());
Then I wrote a script (in windows scripting, in my case) that read in master.js and then read through line by line looking for the ##include: filename.js## lines. When it found such a line it read in the include file and just dumped it out.
My particular needs were special since I was writing a browser plugin that needed to work in three different browsers and had to be wrapped up separately, yet for my own sanity I wanted separate files to work with.
What's the best way to avoid hardcoding URL's in JavaScript (primarily used when making AJAX calls)?
In the past:
Render JavaScript variable with result of #Url.Action or #Url.RouteUrl
Pass result of #Url.Action or #Url.RouteUrl to JavaScript in init/ctor.
Is there a better way?
It would be good to do something like this:
var url = $.routes("actionName", "controllerName") // or "routeName" for named routes
$.post(url, { id = 1 }, function() { //.. });
Which of course isn't really possible (JavaScript doesn't have direct access the to the ViewContext and thus doesn't have access to the route tables).
But i'm wondering if there's a way i can kind of setup my own "route table" for JavaScript, with only the ones i know it would need? (e.g i set it up in the View)
How do people handle this?
in-spite of injecting javascript in views i rather prefer - let HTML do its job and javascript do its. Below is the pattern.
For Links
/*A cssclass=ajaxlink is added to all those links which we want to ajaxify*/
//html in view
<a class='ajaxlink' href='#Url.Action("Action","Controller")'>I am An Ajax Link</a>
//generated clean html
<a class='ajaxlink' href='/controller/action'>I am An Ajax Link</a>
//Js
jQuery('.ajaxlink').live('click',function(e){
e.preventDefault(); /*Prevent default behavior of links*/
var url= $(e.target).attr('href');
/*
Now u have url, do post or get:
then append received data in some DOM element.
*/
});
//Controller
public ActionResult()
{
if(Request.IsAjax())
{
/*Return partial content*/
return View();
}
else
{
return View("SomeOther_View.cshtml");
/*
At this point you may reject this request or return full view
whatever you feel is okie.
*/
}
}
This way both type of users can be handled javascript enabled and javascript disabled.
Same can be done for forms.
Implementing a Javascript routing engine wouldn't be too difficult. First, serialize the Routes from C# to Javascript. Second, recreate the Url.Action method.
However, that's a bit overkill for any of the projects I've worked on. My team's projects have always rendered a common Javascript variable that holds all necessary URL's.
This approach ensures strongly-typed action methods and lends better to refactoring too.
This is easier said than achieved in practice, but your website should be fully functional with JavaScript turned off. When this is achieved, you should be able to add AJAX support to your website and re-use existing HREF attributes in your anchor tags or action attributes in your FORM tags. The website will be easier to maintain as you won't need to update links in your JavaScript files.
I've decided to implement my own UrlFactory, using ASP.NET helpers directly (Html/Url) in my code, now I don't have the src with me, I'll post'em tomorrow.
Pros on this: I can track each and every url easily and perform some rewriting in a centralized fashion.
Example of usage:
#{
string myAjaxUrl = UrlFactory.GetUrl (ActionName, ControllerName, new { query-params });
}
Then using 'em in javascript with
var jsUrl = '#myAjaxUrl';
Once you've defined your own Factory, you can hijack "important" urls (eg. for rewriting), and leave common to the Url helper implementation.
However for having this fully client side, there's an extra step of rendering a Js routing context, for accessing client side variables.
EDIT: As promised my very simple Url class builder:
public static class UrlFactory
{
public static string GetUrl(string Action, string Controller, object RouteValues)
{
UrlHelper Url = new UrlHelper(HttpContext.Current.Request.RequestContext);
return Url.Action(Action, Controller, RouteValues);
}
// Common URLS for Denied et similars.
public static string GetDeniedUrl(PEDUtenti Utente, object RouteValues)
{
return GetUrl(Utente, "Denied", "Errors", RouteValues);
}
public static string GetDeniedUrl(object RouteValues)
{
return GetUrl("Denied", "Errors", RouteValues);
}
}
Building a browsergame I came from PHP to JavaScript, which I now also want to use at the server side.
As I'm going to require Users to have JavaScript either way, I'm going to take extensive use of it. I want to use in in a object-oriented way though.
Considering MVC, Models will be used on both client and server side. Views are only used at the client side.
The interface is split into multiple parts: a main sidemenu, main content and some widgets. I'll take the part I've already done as example:
The menu is split into three categories with multiple entries. Each entry is a link with an attached action (like switching the content).
// menuview:
var self = new View();
var generalMenu = new MenuCategory('generalmenu')
.addEntry(new MenuEntry('overview', new Action()))
.addEntry(new MenuEntry('buildings'))
.addEntry(new MenuEntry('resources'))
// [..more categories..]
self.toData = function() {
return {
id: this.id,
cat: [generalMenu.toData(), infosMenu.toData(), userMenu.toData()]
};
};
At the moment View is a compositum with a toData() method to create data for the template parser(selfmade, simple but supporting iteration). And the actions get attached after creation. I use jQuery as framework:
self.show = function(callback) {
$tpl(this.tpl).parse(this.toData()).lang('main').toHTML(function(html) {
var el = $(html);
el.find('a').click(function (e) {
MenuEntry.actionHandler.execAction(e.target.id);
return false;
});
el.appendTo('#'+self.target);
callback && callback();
});
return this;
};
I have declared an actionhandler to avoid iterating over the links.
I'm not feeling well with this solution, it's not flexible enough. I'd like to treat a view like a real compositum, not with a lot of strange dependencies. Also, I have to reparse the whole View if I change a part. Well, in this example this is not obvious, because the menu wont change while runningtime, but other parts of the interface will.
Now, to finally get to my question: Is there a better solution?
Like having dom references spread over the view, each menuentry having it's own reference and directly attached action? If I'm not using templates anymore, what kind of flexiblity am I losing?
I decided to go without template parser. Each view stores it's node and is able to manipulate it directly if it gets informed to update the data.