I'm trying to figure out what the best practice is for using jQuery in an MVC app. Specifically, I would like to know what I should do so that I don't clutter all my views with individual document.ready statements.
As an example:
I have the following Views:
/Views/Shared/_Layout.cshtml
/Views/Home/Index.cshtml
/Views/Home/_Dialog.cshtml
/Views/Home/_AnotherDialog.cshtml
I have a controller action that will render the Home/Index View, which uses the Layout and renders two partial views (or editor templates, display templates, etc.). This one controller action has rendered 4 or more views. Each view is using some jquery document.ready code.
Currently, I have the code at the bottom of each view:
// In Index
<script type="text/javascript">
$(function() {
$('#tabs').tabs()
});
</script>
// In _Dialog
<script type="text/javascript">
$(function() {
$('#some-dialog').dialog( ... );
});
</script>
I know this isn't a very good practice because it is already getting unmanageable in my small project. What are some good practices to follow when I have tons of pages that all need some jQuery / javascript initialization code separated across dozens of views?
You could do something along the lines of what Telerik do with their javascript registrar. Basically, make this registrar available in your view model. At the simplest level, all it has to do is keep track of strings added to it:
public class JavascriptRegistrar
{
private StringBuilder jsBuilder_ = new StringBuilder();
public Add(string js)
{
builder.Append(js).Append('\n');
}
public string ToString()
{
return "<script type=\"text/javascript\">" + jsBuilder_.ToString() + "\n</script>";
}
}
Your partial views will then add to this when rendering:
<h1>In my view!</h1>
#Model.Registrar.Add("$function() { /* ... */ }")
Finally, at the bottom of your main view, when you're done:
#Model.Registrar.ToString()
Which will write out all the javascript it has collected during rendering.
If the initialisation is specific to a view and you know it definitely won't be used outside that view, for example some page specific behaviour, then just leave it in the view!
There is nothing wrong with having script tags in all your views, as long as you aren't replicating js between views. I think people tend to misunderstand 'separation of concerns' in this case and think that simply means 'keep different languages away from each other at all costs'...that is wrong, clearly if some page initialisation logic/behaviour is specific to a page, then the html and js intrinsically 'concern' each other, therefore moving the js into a separate file is not really 'good practice', if anything it makes your code more difficult to understand.
I personally like to open up a View, and be able to see all the js and css that is specific to that page as soon as I open it, makes it nice and readable. However, obviously if code needs to be shared then you need to bust it out your view and get in your scripts folder whwere it can be referenced by anything!
EDIT
In your example above I see in your Index view you initialise your tabs. This is fine as it is, however, if you added tabs somewhere else in the project then it might be better to create your tabs using a .tabs class rather than #tabs id, and then in an external js file initialise all your tabs at once by calling $('.tabs').
Related
I am currently working on a web application that uses jquery 1.9.1 and jquery mobile 1.3.2 in visual studio(MVC4 template).I have many pages where i use a side panel.The side panel has almost same elements in it, so i decided to put it in a partial view,but the elements in the side panel are not exactly same in all the pages.
so i am thinking to put all the elements of side panel used in all the pages in a partial view and selectively render them according to the page, i am using.
so is there any way by which i can render only selective elements from a partial view?
Do you really need to do this using Javascript?.
Lets organize the ideas:
I have many pages where i use a side panel
What I would do here is to create a layout page that you could use on all the views where you require to add this side panel. So the views you want to render this layout on the final html output should include the following section:
#{
ViewBag.Title = "Roles";
Layout = "~/Views/Shared/_SidePanelLayout.cshtml";
}
so i am thinking to put all the elements of side panel used in all the pages in a partial view and selectively render
Ok I would do the same, however, the rendering of the options, I would not use javascript. I would prefer to send the html of the only required options by sending the models to the view. This is the process I would code:
1-Whenever you request a page that uses this side panel layout, you should fill a ViewBag property (or a model) to pass the page name. So lets say that you have your Home/Index view uses this layout, you can do something like this:
public ActionResult Index()
{
ViewBag.PageName = "Home-Index"; //you could use constants or enums as best practice
return View();
}
At some point on your _SidePanelLayout view, I would place a Render Action call. This render action will in fact, render the side panel view with the specific options for that page:
#{ Html.RenderAction("GetOptions", "SidePanelController", ViewBag.PageName); }
This means that you will require a SidePanelController class with the method that will return your SidePanelView (with the specific options for your the page that you requested):
public class SidePanelController: Controller
{
public ActionResult GetOptions(string pageName)
{
//you may want to change this List<string> for a list of objects that include
//the properties you need like url, name, tooltip, etc
List<string> menuOptions = new List<string>();
//Determine which options should be rendered
/* your code */
//return the view with the filtered options
return PartialView("_SidePanelView", menuOptions);
}
}
That way you would have your requirements fullfilled. It's a bit more complex than using javascript, but it's more robust solution.
Hope this helps.
I am trying to add multiple-tabs capability to my JSF(facelets)/Spring/ application, something like OpenBravo already has (http://wiki.openbravo.com/w/images/9/9c/WIK_PartsOfScreen.png, but their app is using different technologies - I guess - pure JavaScript for GUI): each document or report can be opened in separate JavaScript tabs.
Bascially it is simple - each document or report is separate JSF region and only one of them is made visible. Tabs are only visual effect.
The problem is communication with JSF backing beans (or Spring beans that sometimes can be employed in this role). E.e. is is simple for one tab interface: one can create the following bean:
class BankDocumentServiceBean {
BankDocument selectedDocument;
Long id;
void setId(Long id) {
if (selectedDocument.id!=id) {
setupBankDocument(id);
}
}
void setupBankDocument(Long id) {...}
BankDocument getBankDocument() {...}
...
}
And BankDocument JSF page can be something like this:
...
<f:metadata> <!-- page is called with parameter id and this code calls setId(...) -->
<f:viewParam name="id" value="#{bankDocumentServiceBean.id}"/>
</f:metadata>
<h:body>
...
<!-- p for Primefaces -->
<p:inputText id="amountTxt"
value="#{bankDocumentServiceBean.selectedDocument.amount}"
required="true" label="amountLbl"/>
<p:message for="amountTxt" />
...
Is this approach generally good?
The main question is - how this single-document approach can be extended to multiple tabes. JSF code can be included in the page dynamically, but the service bean remains the problem. Use of additional parameters in each call can be solution - e.g. BankDocumentServiceBean can contain not single BankDocument but List of BankDocuments and each opearation (e.g. getBankDocument) can have additional identifier for selection the right bean from the collection, i.e., the bean that is relevant for the tab with which the user interacts.
But maybe there is some better approach. E.g. maybe one page can be multiple instances of the old good single-document BankDocumentServiceBean. Maybe there are less coarse scopes (JSF scopes and Spring scopes) that are good for tabs, some kind of conversation scope?
Each tab is sitting in its own general panel (div) with its distinct id. Maybe JSF/Spring application can detect that request is coming from distinct panel/div and select instance of BankDocumentServiceBean accordingly?
I guess - solution with List and parameters will work, but maybe there is better architecture for this? Thanks!
Not a complete a solution but a good starting point for handling multiple browser tabs in a jsf web application:
A very good read in dealing with multiple tabs: http://myfaces.apache.org/orchestra/myfaces-orchestra-core/multiwindow.html
I also recommend Apache Codi scopes for this.
I'm attempting to render JavaScript from a Razor Partial Page into the bottom of a (layout) Page. In Including JavaScript at bottom of page, from Partial Views Becuzz specifies that using a #section {} might be helpful for this purpose, but such sections are not rendered from Partial Pages.
One could in theory circumvent the problem by rendering the section of JavaScript outside of the Partial Page, into the Page itself. However, this is not possible, given that I want to reference a html element inside the script, as follows:
#section ScriptTag
{
<script type="text/javascript">
var example = $('##Html.FieldIdFor(m => m.ExampleProperty)').val();
});
</script>
}
#Html.TextBoxFor(m => m.ExampleProperty)
How can I make this work?
You can only call RenderSection between two Views/Layouts that are directly related.
In this situation you would need to essentially redefine and render the section in your View in the middle.
See: http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx for a clearer explanation
I'm fairly new to the whole JQuery/javascript world, but I've managed to wack together a working jqgrid with a datepicker & custom control (used jquery auto complete) based on code samples i found on the net. I've added the code to a T4 template in my project as it'll probably act as a base/starting point for most pages. (Side note. I'm using asp.net MVC)
JFIDDLE: LINK
1.) I'd like to move the initDateEdit & initDateSearch to the same function (using a parameter, to disable/enable the showOn property) as they are basically similar.
2.) How would be the best way to set nonWorkingDates from outside the new function/file. same applies to the autocomplete_element (I'd like to specify the url)
Changing
"function nonWorkingDates(date)" to => "function nonWorkingDates(date, nonWorkingDates)"
isn't working, (guess it's got got something to do with how its gets called "beforeShowDay: nonWorkingDates")
Thanks in advance!
If you have a chunk of JS code like this:
<script type="text/javascript">
... code goes here ...
</script>
You simply copy the whole thing, eliminate the containing script tags, and save the raw code
... code goes here ...
to a file, which you then include with:
<script type="text/javascript" src="yourfile.js"></script>
However, since you're using jquery, you'll have to make sure that this above snippet is placed AFTER the script tag that loads up jquery, or you'll get a "no such function" syntax error.
I've been using JSON to handle AJAX functionality in my rails applications ever since I heard about it, because using RJS/rendering HTML "felt" wrong because it violated MVC. The first AJAX-heavy project I worked on ended up with 20-30 controller actions tied directly to specific UI-behaviors and my view code spread over controller actions, partials and rjs files. Using JSON allows you to keep view specific code in the view, and only talk to view agnostic/RESTful controller actions via AJAX to get needed data.
The one headache I've found from using pure JSON is that you have to 'render' HTML via JS, which in the case of AJAX that has to update DOM-heavy elements, can be a real pain. I end up with long string building code like
// ...ajax
success: function(records){
$(records).each(function(record){
var html = ('<div id="blah">' + record.attr +
etc +
')
})
}
where etc is 10-15 lines of dynamically constructing HTML based on record data. Besides of the annoyance, a more serious draw back to this approach is the duplication of the HTML structure (in the template and in the JS).* Is there a better practice for this approach?
(My motivation for finally reaching out is I am now tasked with updating HTML so complex it required two nested loops of Ruby code to render in the first place. Duplicating that in Javascript seems insane.)
One thing I've considered is loading static partial files directly from the file system, but this seems a bit much.
I like the idea of templating. In my experience it can really clean up that messy string manipulation!
There are many solutions, for example, check out John Resig's (creator of jQuery):
http://ejohn.org/blog/javascript-micro-templating/
I would go with creating an HTML structure that contains placeholders for the elements you'll need to update via AJAX. How much structure it applies will depend on what you're updating; if you know the number of elements you'll have ahead of time, it would be something to the effect of
<div id="article1">
<div id="article1Headline"></div>
<div id="article1Copy"></div>
<div id="article1AuthorInfo"></div>
</div>
<div id="article2">
<div id="article2Headline"></div>
<div id="article2Copy"></div>
<div id="article2AuthorInfo"></div>
</div>
You then write code that references the id of each element directly, and inserts into the .innerHTML property (or whatever syntactically more sugary way jquery has of doing same thing). IMHO, it's not really so terrible to have to assign the contents of each element, the part that you don't want to have to sprinkle through your AJAX functions is the HTML structure itself; in your app the content is volatile anyway.
However, it looks like you might have a list of an unknown number of elements, in that case it may be that you'd need to just put in a placeholder:
<div id="articleList"></div>
In that case I don't really see a way to avoid building the HTML structure in the javascript calls, but a reasonable amount of decomposition of your javascript should help that:
function addArticle( headline, copy, authorInfo, i ){
createDiv( "article" + i + "Headline", headline );
createDiv( "article" + i + "Copy", copy);
createDiv( "article" + i + "AuthorInfo", authorInfo );
}
(not working code of course, but you get the idea,)
You could use the load function in jQuery;
This loads the content of a page into a div like this:
$('#content').load("content/" + this.href.split('#')[1] + ".html", '', checkResponse);
Just make a dynamic view and you are good to go...
Just happened to find exactly what I was looking for: Jaml