91

I have a .csHtml-razor file with a javascript function that uses the @Url.Content C# function inside for the ajax URL.
I want to move that function to a .js file referenced from my view.

The problem is that javascript doesn't "know" the @ symbol and doesn't parse the the C# code.
Is there a way to reference .js files from view with "@" symbol?

gdoron
  • 147,333
  • 58
  • 291
  • 367

9 Answers9

87

You could use HTML5 data-* attributes. Let's suppose that you want to perform some action when some DOM element such as a div is clicked. So:

<div id="foo" data-url="@Url.Content("~/foobar")">Click me</div>

and then in your separate javascript file you could work unobtrusively with the DOM:

$('#foo').click(function() {
    var url = $(this).data('url');
    // do something with this url
});

This way you could have a pure separation between markup and script without you ever needing any server side tags in your javascript files.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • Thanks. I think for multiple values can be a headache. Firstly I thought it can to solve my problem but after giving that a try I found so many referrals in my code and decide to find another way. – QMaster Dec 31 '18 at 18:21
19

Well I've just found a razor engine on nuget that does it! Meaning solves @ syntax!
It's name is RazorJS.

The Nuget package


2016 Update:
The package wasn't updated for 5 years, and the project site link is dead. I do not recommend people to use this library anymore.

gdoron
  • 147,333
  • 58
  • 291
  • 367
11

One way to tackle the problem is:

Adding a partial view with the javascript functions to the view.
This way you can use the @ symbol and all your javascript functions are separated from the view.

gdoron
  • 147,333
  • 58
  • 291
  • 367
9

You have two options:

  • Use the value as a parameter in the function and wire-up in the view
  • Create a namespace (instead of public level variable which is considered bad practice in JS) and set this value at the top of the page and then use it in your js

For example:

 var MyCompany = 
 {
   MyProject: {
                  MyVariable:""
               }
  };

And then in your view, set it:

MyCompany.MyProject.MyVariable = @....

UPDATE

You might wonder none is any good because of coupling, well it is true, you are coupling js and view. That is why scripts must be oblivious to the location they are running in so it is a symptom of non-optimum organization of files.

Anyway there is a third option to create a view engine and run the js files against the razor and send the results back. This is cleaner but much slower so not recommended either.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • 1
    +1 I do the first method. Call an initialization function from your view passing in all of the data you require. – Richard Dalton Oct 26 '11 at 12:05
  • Thanks, the second approach isn't a bad coupling between the view and the js file? Do you have a better way? – gdoron Oct 26 '11 at 12:13
  • Both are coupling. There is no way to uncouple unless you write a view engine to process js files before rendering. See my updates. – Aliostad Oct 26 '11 at 12:15
  • what about this way that I suggested? http://stackoverflow.com/questions/7902213/asp-net-mvc-razor-symbol-in-js-file/7903619#7903619 – gdoron Oct 26 '11 at 13:55
  • Sounds OK. My +1. Either partial or full view, does not change anything. It is all coupling view with js. – Aliostad Oct 26 '11 at 14:17
  • I think I've found the solution for it : http://stackoverflow.com/questions/7902213/asp-net-mvc-razor-symbol-in-js-file/7914123#7914123 what do you think? – gdoron Oct 27 '11 at 09:39
7

In order to get the @ variable into your .js file you'll have to use a global variable and set the value of that variable from the mvc view that is making use of that .js file.

JavaScript file:

var myValue;

function myFunc() {
  alert(myValue);
}

MVC View file:

<script language="text/javascript">
    myValue = @myValueFromModel;
</script>

Just be sure that any calls to your function happen AFTER the value has been set by the view.

Joel Etherton
  • 37,325
  • 10
  • 89
  • 104
  • 1
    what about passing directly myValue as a parameter to the function ? I prefer to have explicit calling with param than relying on globals, however I'm not so keen on javascript so I may be missing some important aspect of having a global. – BigMike Oct 26 '11 at 12:32
  • @BigMike: Certainly acceptable. The method I put forth above simply makes the variable available to multiple functions simultaneously, but using parameters is just as good. – Joel Etherton Oct 26 '11 at 12:51
  • thanks for clarification. It would be great to have some sort of js object version of the model automatically (via Attributes in model class will be great), this will just add a pattern and a fully namespace aware variable solution. I guess the big problem would be how to inject the code in the generated html sent to the browser (directly of via – BigMike Oct 26 '11 at 13:10
  • @BigMike: This should be fairly easy to accomplish by using the DataContractJsonSerializer class (http://msdn.microsoft.com/en-us/library/system.runtime.serialization.json.datacontractjsonserializer.aspx). It would create a JSON version of your class that could be parsed into the view. – Joel Etherton Oct 26 '11 at 13:19
5

I recently blogged about this topic: Generating External JavaScript Files Using Partial Razor Views.

My solution is to use a custom attribute (ExternalJavaScriptFileAttribute) which renders a partial Razor view as is and then returns it without the surrounding <script> tags. That makes it a valid external JavaScript file.

Marius Schulz
  • 15,976
  • 12
  • 63
  • 97
5

Probably this is not the right approach. Considering separation of concerns. You should have a data injector on your JavaScript class and which is in most cases data is JSON.

Create a JS file in your script folder and add this reference to your View

<script src="@Url.Content("~/Scripts/yourJsFile.js")" type="text/javascript"></script>

Now, consider a JavaScript literal class in your yourJsFile.js:

var contentSetter = {
    allData: {},
    loadData: function (data) {
        contentSetter.allData = eval('(' + data + ')');
    },
    setContentA: function () {
        $("#contentA").html(allData.contentAData);
    },
    setContentB: function () {
        $("#contentB").html(allData.contentAData);
    }
};

Also declare a class

public class ContentData
{
    public string ContentDataA { get; set }
    public string ContentDataB { get; set }
}

Now, from your Action do this:

public ActionResult Index() {
    var contentData = new ContentData();
    contentData.ContentDataA = "Hello";
    contentData.ContentDataB = "World";
    ViewData.Add("contentData", contentData);
}

And from your view:

<div id="contentA"></div>
<div id="contentB"></div>

<script type="text/javascript">
    contentSetter.loadData('@Json.Encode((ContentData) ViewData["contentData"])');
    contentSetter.setContentA();
    contentSetter.setContentB();
</script>
Abdul Munim
  • 18,869
  • 8
  • 52
  • 61
  • Nice! but I think it's too complicated... what do you think about my answer? http://stackoverflow.com/questions/7902213/asp-net-mvc-razor-symbol-in-js-file/7903619#7903619. by the way you have copy paste error in setContentB you wrote contentAData. thank! – gdoron Oct 26 '11 at 14:01
  • 1
    @gdoron Believe me, managing JS classes and considering separation is a life saver when you have a huge project – Abdul Munim Oct 26 '11 at 14:04
2

I usually wrap JS needing access to model properties, in functions and then pass the @something in the view. For example

<script type="text/javascript">
function MyFunction(somethingPresentInTheView) {
  alert(somethingPresentInTheView);
}
</script>

in the view I add function invocation via (just an example):

<input type='button' onclick="MyFunction('@Model.PropertyNeeded')" />
BigMike
  • 6,683
  • 1
  • 23
  • 24
1

I think you are stuck with having to put that JS code in the View. The Razor parser, as far as I know, won't look at .js files, thus anything you have which uses @ won't work. PLus, as you have spotted, Javascript itself does not like this @ character hanging around for no reason, other then, say, in a string.

Jason Evans
  • 28,906
  • 14
  • 90
  • 154