I've got some JavaScript in an ASP.NET page that looks like this:
var list = $get('<%=Topics.ClientID %>');
I have many functions written now where this syntax is used, and I would like to centralize my JavaScript and move this into an external JavaScript file. This breaks however, since 'Topics' cannot be found.
What is the best strategy for getting this to work? I assume I should pass the control/control information as a parameter to the function, but I can't seem to get the syntax to work. Any suggestions?
It's a common problem for ASP.NET JS development. As for me, I'm using same approach each time and it looks fine.
I'm used to OOP in Javascript, so most my JS external files look like:
function CouponManager()
{
}
And in .aspx code i do:
<script language="javascript">
var couponManager = new CouponManager();
</script>
If I need to pass some parameters I change the declaration of class to:
function CouponManager(params)
{
// .. stuff here
this.initialize = function(initParams)
{
// .. accessing initParams variable for server control IDs
};
this.initialize(params);
}
And from .aspx code I do the following:
<script language="javascript">
var couponManager = new CouponManager
({
txtCouponNameId = '<%= txtCouponName.ClientID %>',
txtCouponDescriptionId = '<%= txtCouponDescription.ClientID %>'
});
</script>
This approach allows me to separate JS from .aspx page and have all server control dependencies in a single tag.
You should create a javascript method from inside the usercontol which returns the client side element. Then in your other page/control, just access that method
In User Control
<script language="javascript">
function GetTopics() {
return = $get('<%=Topics.ClientID %>');
}
</script>
In other page/control
<script language="javascript">
var list = GetTopics();
</script>
Edit - The problem you are facing is you need Topics.ClientID where it doesn't exist. So the only real way to bridge that gap is to put it in a common place. If you really don't want to do that, you can try and select your element by some other criteria. If you are using jQuery, you could mark an element with a class of Topics then find it with $(".Topics").
if you know that you only have one server control called "Topics" per page, and you use naming conventions you can inherit from whatever the control Topics is (maybe it's a HiddenField? you don't specify) and override its ClientId getter to return its server id like this:
http://andreascode.wordpress.com/2009/04/27/tiny-drips-of-aspnet-juice/
then you can know in your javascript files that there will be a hidden field in the page with the id set to "Topics" and use that directly.
depending on your domain/situation this could either save you a lot of time or screw you over big time.
Related
I have a bunch of javascript inside my view and its getting quite large so I want to move it to a separate js file. The one issue I have is that I have this line:
var tags = <%= new JavaScriptSerializer().Serialize(Model.Tags) %>;
which I obviously can't just copy over since it has the server side asp.net-mvc tags. What is the recommended way to deal with that:
Keep this one function inside the aspx page and have the javascript from the seperate js file call that function?
Other??
What you could do is have a JavaScript object contain all the information you get from the controller and pass it through a javascript function which is located in the external javascript file. You can also pass other information through the options variable.
Example:
var options = {
tags: <%= new JavaScriptSerializer().Serialize(Model.Tags) %>
};
initPage(options);
Usage:
function initPage(options) {
console.log(options.tags);
}
This depends a lot on the way you want to use it and the level of repeating.
Some thoughts:
The tags are static. Then I guess then don't really need to be in the model. So you can move them into a new Controller action which outputs the scipt. You can call this action in the <script> part of your view.
The tags are changing very frequently (maybe even at every page load). Then there is no gain in moving this in a separate script file.
If you set up your javascript file with proper closures, you can expose a property (or better yet, a parameter to the object/method) with which to pass that information along when you call that function.
Your Javascript would need to be something like this:
var JsFile = (function() {
var tags;
// list all of your methods here.
return {
var setTags = function(_tags) {
tags = _tags;
}
};
})();
I have an app that uses HTML, and Coffeescript in the frontend. I recently made it possible to change the language thanks to i18next.
Now I have some buttons that change my window.userLang to the different languages, but the user has to refresh some elements of the app to see it translated.
My problem comes because I need the translations made without refreshing the HTML.
In the app, I use Craftyjs, so what I need to know is how can (if possible) from HTML file, call a function that it's defined in Craftyjs.
The function I want to call is: Crafty.scene("main").
Thanks all!
Create a global name space defined above where your scripts are included. Then in your javascript you can define the functions you need as fields on that namespace.
<script>
var MySweetWebApp = {};
</script>
<script src="..."></script>
... Inside JS file ....
MySweetWebApp.Crafty = { ... }
... From Anywhere ...
MySweetWebApp.Crafty.scene('main');
I understand that for performance reasons it is better to let the asset pipeline concatenate and minify all my javascript and send the whole lot with every page request. That's fair enough
However, a bunch of my javascript is things like binding specific behaviours to specific page elements - stuff like
$('button').click(function(e) { $('input.sel').val(this.name); }
and I would feel more comfortable if I knew that this code was being executed only on that page - not on evey other page which might coincidentally have elements with the same IDs or which matched the same selectors How do people deal with this?
I would rather not put all this stuff inline in elements, just because when it gets to be more than about two lines long, keeping javascript correctly indented inside an .html.erb file is more work than it needs to be
Here is what I do (based on some stackoverflow answers):
application_helper.rb
def body_page_name
[controller_name.classify.pluralize, action_name.classify].join
end
application.html.haml
%body{data: {page: body_page_name}}
application.js
$(function() {
var page = $("body").data("page");
if("object" === typeof window[page])
window[page].init();
});
And in appropriate js file there's an object called ControllerAction:
tickets.js
var TicketsShow = new function() {
var self = this;
self.init = function() {
// code which may call other functions in self
};
};
There's probably better way to do it, but this works for me
I'll describe what I currently do, just in case it gives anyone a better idea
1) I changed the 'body' tag in my application.html.erb to add the current controller and action as data- attributes
<body data-controller="<%= controller.controller_name %>"
data-action="<%= controller.action_name %>" >
2) I test this at the top of the relevant javascript
$(document).ready(function() {
if($('body').data('controller')=='stories') {
$('.story').click(function(e) {
var u=$(this).data('url');
u && (document.location=u);
});
}
});
I can't decide if I think this is a good idea or not
For page specific JavaScript, I typically do something like this:
Application Helper
In the application helper I create a class attribute (though you could just as well use a data attribute instead).
module ApplicationHelper
def body_attributes
controller = params[:controller].gsub('/', ' ')
action = params[:action]
version = #version ? "version_#{#version}" : nil
{
class: ([controller, action, version] - [nil]).join(' ')
}
end
end
Note I'm also adding a version string. This helps with Google content experiments, and makes A/B testing a breeze.
Application.html.haml
In my global layout file, I do something like this to insert the attributes on the body tag:
!!! 5
%html
%head
...
%body{body_attributes}
script.js
Now in my page specific script, I just check for the class attributes, like this:
$(function () {
if ($('body.pledge.new, body.pledge.create').length > 0) {
// do work here...
}
});
The advantage of this method is that getting the body by class is very quick. The script inside the conditional will not be executed at all on any page apart than the ones I choose, so minimal overhead, and I don't need to change my selectors throughout the code.
EDIT
Note that this answer is now 3 years old. You should be using client-side routing with a framework like React instead.
I'd add a class to the BODY tag, allowing you to identify each page, and therefore each control per page.
<body class='page1'>
JS:
$('.page1 button').click(function(e) { $('input.sel').val(this.name); }
I've done it and seen it done in several different ways:
Rigging up the mvc to be able to load a particular js file per page, named along the same lines as a controller file. Like: <controller-name>.js
Making a url parser in JS and then setting a global variable to the current page: UrlParams.currentView = 'dashboard'; and then saying if(UrlParams.currentView == 'dashboard') { //do specific js here }
Setting a unique identifier as the page class or ID and then targeting that with your JS selectors. $('#dashboard').xyz();
I've seen/read plenty advocating "unobtrusive" JavaScript contained in separate files. I'm preparing to combine all my JavaScript from three partial views into a single file that I will then reference somewhere in my master.
My question is: is there any type of JavaScript that should remain behind in the html? One example that seems to me may present a problem would be something like:
<script type="text/javascript">
$(document).ready(function () {
$('#newQuoteLink').click(function () {
$('#newQuoteLink').hide();
$('#newQuoteDiv').load('/Quote/Create/<%:Model.Book.BookID%>');
return false;
});
});
</script>
--in particular the
<%:Model.Book.BookID%>
Am I correct in assuming this script would not work if loaded from a separate file?
I mostly wanted to check if there were any caveats or other considerations prior to combining everything into this lone, separate file.
Thanks in advance.
Nope, promise to never ever hardcode url addresses that are route dependent like you did in your javascript file. It's bad, bad, bad. Did I say it's bad?
That's too much of a javascript in a view (it's a bandwidth waste). You could try a global javascript variable declaration your view:
<script type="text/javascript">
var quoteUrl = '<%: Url.Action("create", "quote", new { id = Model.Book.BookID }) %>';
</script>
and in your javascript file:
$(function () {
$('#newQuoteLink').click(function () {
$('#newQuoteLink').hide();
$('#newQuoteDiv').load(quoteUrl);
return false;
});
});
That's a path I wouldn't personally take. Still a script tag with a global javascript variable declaration in your view. Still a waste.
Things become even prettier like this (and it is at that moment that you realize the real power of unobtrusive javascript):
<%: Html.ActionLink("Foo Bar Beer Link Text", "create", "quote",
new { id = Model.Book.BookID }, new { id = "newQuoteLink" }) %>
and in your external javascript:
$(function () {
$('#newQuoteLink').click(function () {
$('#newQuoteLink').hide();
$('#newQuoteDiv').load(this.href);
return false;
});
});
Yep, you're right in that <%:Model.Book.BookID%> will not be visible to the script file. These things are part of the server side script that generates the HTML that is sent to the browser.
You can put all the bulk of the work in the script in a funciton which accepts the id as a param, and then in your html, from your .ready(..) call the function like doStuff("<%:Model.Book.BookID%>") etc.
Javascript experts: other caveats? I'll update when i think of some
Not sure if this is possible or even if I should do it, but I think it's quite interesting.
I have a javascript file which I'm referencing in a flat HTML page. I'd like to pass in a parameter or two via the path to the script. Like this;
<script src="/scripts/myJavascriptFile.js?config1=true" type="text/javascript"></script>
Not really sure if it can work but it would make my solution a little easier for others to take my script and implement (arguable).
Cheers,
Mike
I don't think that passing in variables via the src attribute is possible out of the box without some extra coding on your part (there is an article here if you are interested!). You could do the following though, which should provide the same functionality as you are looking for:
Define your "config" variables in a single script block on your HTML page:
<script type="text/javascript">
var config1 = true;
</script>
Reference your external JS file in a second script block:
<script src="/scripts/myJavascriptFile.js" type="text/javascript"></script>
Add this code to your external JS file to reference the "local" variable in your HTML:
var conf1 = window.config1;
if (conf1) {
// Do stuff
}
This is a variation on Matt's answer. I have a similar case where I need a jQuery file to use a value that is generated in the HTML (by Razor in this case). I write the value to a meta tag, generated as it is from the controller:
<meta name="sessionId" content="#ViewBag.SessionId">
and then read it in the jQuery file:
var sessionId = $("meta[name=sessionId]").attr("content");
It's not quite the same as passing it in by querystring, but useful if that information is considered "meta-information" of the HTML page.