0

Apologies if I'm using the wrong terminology, I'm fairly new to this technology.

I have a view which we'll call MainView.cshtml:

<div>
  @RenderPartial("_Partial");
</div>

<script type="text/javascript">
 $(document).ready(function() {
    someFunctionDefinedIn_Partial();
 }
</script>

The _Partial view has javascript that defines a function (in this case someFunctionDefinedIn_Partial.

function someFunctionDefinedIn_Partial()
{
  // This uses the "@" syntax to access C# variables, which can be done in .cshtml
  alert("Hello. This variable is from C#: @ClassInCSharp.Variable");
}

When I run the code above I'm told in the chrome console that the function someFunctionDefinedIn_Partial doesn't exist. I'm guessing this is because javascript in a partial doesn't make its way out to the container.

I looked around a bit and found that I can extract the javascript to its own .js file and reference that as shown in this SO post: How to render JavaScript into MasterLayout section from partial view?

However, the post above suggests extracting javascript into its own separate file .js. If I do this, then I am using "raw" javascript, so I can't use the @ notation to reference variables from my C#.

Is it possible to somehow include javascript in my MainView.cshtml which also has access to the @ syntax so I can use C# methods/variables? If not, is it possible to create an external javascript file, but have it be a .cshtml file so I can use the "@" syntax?

Victor Chelaru
  • 4,491
  • 3
  • 35
  • 49
  • You can use Razor @ notation to output variables for JS. – evolutionxbox Dec 01 '17 at 17:25
  • Is the variable you're accessing in C# available in your MainView? Or is it only accessible to the partial? – mhodges Dec 01 '17 at 17:27
  • In this case the variable is assumed to be accessible to both. – Victor Chelaru Dec 01 '17 at 17:28
  • @VictorChelaru Oh, well then just pull the javascript from the partial out into MainView.cshtml. In general, partials should not contain logic that depends on specific variables available in the environment. The entire idea of a partial is that it can be loaded anywhere. So if you expect a C# variable in your partial, it may be available one place you include it, and unavailable in another, causing an error. – mhodges Dec 01 '17 at 17:30
  • @VictorChelaru It is also worth mentioning that pulling the javascript out of your partial and into your MainView.cshtml means that you don't have to pollute the global javascript scope with functions. You can define them inside of the document.ready function in your MainView, and then they are local to that function. – mhodges Dec 01 '17 at 17:31
  • Unfortunately this isn't a good solution for me. The partial is used in a lot of views, and there could be quite a bit of JS. If I were to move it out to the main view then I might have tons of duplicate JS code. DRY – Victor Chelaru Dec 01 '17 at 17:32

2 Answers2

1

Can I use Javascript (with @ syntax) from partial into the main view?

Yes, but the order of javascript operations is very important. Strictly speaking, the following javascript code should error out:

console.log(myvariable);
var myvariable = "hello world!";

I'm using a variable before it's defined. That is the similar problem you are having.

Would I recommend using Javascript directly inside a view?

Definitely almost exclusively never. The way most javascript/css/html is written today is tightly coupled enough. Adding a coupling to C# seems like a giant code smell. My personal preference is to add data to html elements and have the javascript only coupled to my html. So instead of :

<script>
var myIds = [@(string.Join(Model.People.Select(p => Id.ToString().ToArray(), ",")];
</script>

Which is just insanely ugly and hard to debug, I apply my values to the html presentation that is representing my object:

<div data-id="@model.Id" class="person">
  Person Name: @Html.DisplayFor(m => m.name)
</div>

<script>
var myIds = [];
$('.person').each((i,p) => myIds.push($(p).data("Id"));
</script>

Now I can't really make a mistake about making sure the Id's in my array are the same ones on the page, everything is encapsulated together.

The other code smell is using @() methods in a view for logic. For example, I see a lot of:

@foreach(var person in Model.persons) 
{
  if (person.Age >= 21)
  {
    <div>Adult</div>
  }
  else
  {
    <div>Child</div>
  }
}

To me, that is logic in a view. My definition of a ViewModel is a model that represents all the necessary data-points (even those derived from logic). So I would do something like:

public class PersonVM
{
  public DateTime BornOn { get; set; }
  public bool IsAdult { get { return /*BornOnLogic*/; } ]
}

So in my view, I don't understand why anyone would need to use any C# directly within Javascript.

I would also recommend reading Philip Walton's (Google Engineer) -Decoupling Your HTML, CSS, and JavaScript.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
  • Not cluttering your DOM would be one reason. Sending entire models through (especially to angularjs) is extremely beneficial. Your data-id example can be tampered with by the user as well. They can change those values in the DOM before the JS runs, allowing access to edit/delete/etc. users with IDs they are not supposed to. Using an obfuscated IIFE that takes in the values from razor directly is much more secure. Not to say that this should be done *every* time, but there definitely are legitimate use cases – mhodges Dec 01 '17 at 17:40
  • Yes it was in fact an order of. I had defined my JS in the main view prior to the Html.RenderPartial. If I move the main view JS to be after the RenderPartial call, all works well. – Victor Chelaru Dec 01 '17 at 17:46
  • @mhodges cluttering the DOM sure, but that argument is a wash when it comes to maintainability and re-sure VS javascript in a view. As for sending a model, *security* argument, nothing from the client can be assume to be clean regardless of how it's sent down to the client. No technology (raw javascript, knockout, redux, react, angular) is secure in terms of keeping a model valid. [The National Institute of Standards and Technology (NIST) in the United States specifically recommends against this practice](https://en.wikipedia.org/wiki/Security_through_obscurity) – Erik Philips Dec 01 '17 at 17:51
  • On a side-note, building your JavaScript data from your views is an old way of thinking about building views. Post-angular, react, etc. it is exactly the opposite. You should build your views from your JavaScript data. It is much faster, much more secure, and much more reliable. Never trust your HTML, it is way too easy to manipulate. – mhodges Dec 01 '17 at 17:51
  • @ErikPhilips I don't disagree. I just said *more* secure, not airtight. Of course, server-side validation should always be implemented because one can never trust the client. I'm just saying, it makes it much more difficult to tamper with – mhodges Dec 01 '17 at 17:55
  • 1
    @mhodges *much more?* How hard is it to pause javascript execution and either change a dom element or change script in chrome memory? About the same amount of effort (i've done both), they're both ridiculously easy. Changing a json resposne is also brain dead easy. – Erik Philips Dec 01 '17 at 17:58
  • @ErikPhilips "How hard is it to pause javascript execution and change a script...?" Well, on minified and obfuscated JavaScript... actually pretty challenging. Maybe you haven't tried to exploit systems that do things properly on the front end? – mhodges Dec 01 '17 at 18:01
  • @ErikPhilips Side note - from your article (which is what I mentioned above) "The WHATWG is currently working on the Web Components specification, which will allow developers to bundle HTML, CSS, and JavaScript together as individual components or modules that are encapsulated from the rest of the page. When that spec is implemented in the majority of browsers, a lot of the suggestions I’ve given in this article will become less critical" – mhodges Dec 01 '17 at 18:02
  • @mhodges no, can't say that I've attempting any exploits. Not my job, not my hobby. I'll just say it again for everyone else out there, Security through obscurity is never a good choice. – Erik Philips Dec 01 '17 at 18:05
  • @ErikPhilips Again, I am not asserting that - I am just saying it's *more* secure than doing nothing at all. The more security measures you put into place, the more secure a system is. If you solely rely on your server-side validation to thwart every front-end exploit, you may miss something. If you do both, that is the most secure thing to do. I don't think there is an argument against that – mhodges Dec 01 '17 at 18:13
0

I don't recommend you to include JS in a Partial view, as it can be added many times in a given view. Take a look at link

I suggest you to move the partials JS to the main view.

UPDATE

If you don't have any other option, consider that Razor cannot be used in plain JS: Link

What you can do is to extract the C# values needed in your JS and send them when invoking the JS. So you could have:

function someFunctionDefinedIn_Partial(variableValue)
{
  // This uses the "@" syntax to access C# variables, which can be done in .cshtml
  alert("Hello. This variable is from C#: " + variableValue);
}

And invoke it from the partial view:

someFunctionDefinedIn_Partial(@ClassInCSharp.Variable);
mhodges
  • 10,938
  • 2
  • 28
  • 46
Santiago
  • 51
  • 6