Including a custom js file on rails_admin dashboard - javascript

I am trying to include a custom js for a custom field that depends on a google maps control, I don't want to mix html and js on a partial file, however, up to now, it appears to be the way to go.
I have checked on the wiki and the only reference about including a custom js is here but it doesn't work.
I only want to be able to organize my javascripts as usual (at assets/javascripts/) and be able to interact with my rails_admin form views. Anyone has any idea on how this should be handled?

To restate: It sounds like you are trying to keep javascript code out of view (html/erb) files.
I can recommend one way you may wish to try.
If you look at the assets/javascript directory you can see the generated javascript files that are created per controller when you are scaffolding a resource.
These files are great for keeping all the code related to the controller context in. there is another file named application.js which is great to keep global javascript routines in.
If you put tags/fields on the elements which you wish to select to bind a javascript method to you are able to keep the methods focused on finding and binding fields sharing the tag.
example:
field you wish to interact with:
Blah
your_controller_name.js:
using jquery you should be able to select the span by the data tag ( you could stored extra infor. you can then also bind methods to the span.
$("span[data-interesting='hi there']").click(function() { console.log('someone clicked the span'); })
You could use a selector that is more general and do something useful to all the matching items.
Good luck!
James.

Related

Web Page Javascript Objects

newbie question.
I've read some of the W3Schools, I also read a lot from other sources on the internet, however I can't find what I need, and I'm sure it's quite simple to you.
I'm using ASP.Net, and I want to add to my website, multiple items, which every item hold a picture, and some other information, including links. I'm pretty sure I don't need to write the code for every item in the HTML source, and I don't know exactly how to implement my this.
The basic idea is that my items will be imported from a Database that I create in visual studio, and I want to style my webpage so they would appear in a certain formation, I thought I might need to use Javascript or CSS for this, hope I'm not mistaken.
Javascript isn't some sort of magician that will render all your stuff on its own. However, you can use it to attach a template to every of your items.
What you have to do is :
Create a base HTML template for 1 of your item that can be applied to all of them
Create a Javascript function that will attach thoses CSS classes and HTML attributes to every element out of your DB (or you could use a templating frameork .. since there's a lot of them I'll let you look for it on Google. It's pretty easy to use)
On page load or whatever event you want to bind on, you call your function which will attach the CSS and HTML to every element out of your DB and will render it on your page
Enjoy what you've done.
I hope this helps. Good luck ! ;)

Formatting of JavaScript for MVC3 views

I have tried many different ways to organize the JavaScript that is specific for each View now, but I have not found any way that I feel comfortable with. It seems messy anyway. Probably (and hopefully) that is because I haven't been working with JavaScript very long, and there is a good way of doing this.
Currently what I'm doing is this:
In my Layout file, I have the a RenderSection for scripts in addition to RenderBody. This section contains all JavaScript relevant for each single view. The global script is tucked away in it's own file.
Inside this sections there is a lot of different part of JavaScript (for my biggest View currently there is about 600 lines of JavaScript):
Some definitions of variables and setting different settings (jQuery settings among others).
Hiding different DOM elements on the screen that will be made visible when the user interacts with the View later on.
A lot of jQuery code for different events linked to DOM elements ( click/keyup++ )
Some code refactored into methods because they are used by different jQuery events.
The things I don't like here are mainly two things:
All this code is put into one big codeblock and it's hard to find the parts of the script I'm looking for. Basically, it gets quite unmaintainable as the script grows.
The script is located in the same file as the View. I would like to put the script into a seperate file, but I can't since I use different parts of my Model, and some HtmlHelpers inside the script as well. For example, where I execute some $.post calls, I use #Url.Action('...') to link it to the correct action method to be sure that my script will continue to work even if I change my routing.
I also use the Model to decide if some elements should start out hidden or not like this (is this an ok way to make it start out hidden, or is there a better way? It seems kind of hacky even if I can't put my finger on it).:
code
#if( Model.SomeBoolValue ){
#:$("#DOMelementID").hide();
}
Some pointers to get me in the right direction would be highly appreciated. I need to get this better structured before I lose control over my own code.
I would recommend you taking a look at jQuery plugin authoring and organize your javascript code in terms of plugins that you will attach to your DOM elements.
As far as the #Url.Action('...') problem is concerned there are many ways to solve this issue and externalize your scripts into separate files. For example let's suppose that you are AJAXifying an existing form or ActionLink which already contains the url:
$('#myAnchor').click(function() {
// use this.href to fetch the url
$.post(this.href, function(result) {
});
return false;
});
Now if you simply wanted to send an AJAX request when a user clicks on a div for example, you could always use HTML5 data-* attributes (the same way ASP.NET MVC 3 unobtrusive AJAX and validation works) to define this url on the DOM element:
<div id="mydiv" data-url="#Url.Action("Some Action")">click me</div>
and now in a separate file
$('#mydiv').click(function() {
var url = $(this).data('url');
$.post(url, function(result) {
});
});
and if you follow my first advice about writing a jQuery plugin your code will look like this:
$('#mydiv').myplugin();
Now let's consider the following snippet:
#if(Model.SomeBoolValue) {
#:$("#DOMelementID").hide();
}
So from what it seems here you are using the view model properties to show/hide sections of your code. OK, here's what I may suggest you: JSON serialize this model into the view and then you can start passing values from it to your newly developed jQuery plugins:
<script type="text/javascript">
var model = #Html.Raw(Json.Serialize(Model));
// now configure configure your plugins, for example
$('#mysection').myplugin({ model: model });
</script>
Now inside your plugin you will have access to absolutely all properties of your view model.
And that's all that you will need to put in your view. All the other javascript will be of course in separate javascript files, properly organized in reusable jQuery plugins.
Yep, it can get tough.
Here's what we do, and works for us (in bold, because it may not work for you).
For each View/page, we work out what model properties are required by the JavaScript in order to make decisions (a.k.a "logic").
We also have a section in the Layout for the JavaScript.
We then set a single JavaScript property in the View/page, encapsulating these properties, something like this:
#section JavaScript {
<script type="text/javascript">
yn.yp = {
someBoolValue: #Model.SomeBoolValue,
someOtheProp: '#Model.SomeOtherProp'
}
</script>
}
yn = your namespace, tying the global namespace for your project/company.
yp = your page that your setting the JS property for.
Then in your external JS file:
$(function() {
if (yn.yp.someBoolValue) {
$("#elementid").hide();
}
});
This is also a very clean way to handle routing URL's to be used by client-side AJAX. Setup a property like yn.yp.urls, and set the URL's in there in the View, then the JS can access them easily and without any hard-coding whatsoever.
Overall, the goal here is to reduce server-side code in the embedded page JavaScript.
Set properties for whatever the JS needs to make decisions, then let the JS make the decisions itself.
Hope that makes sense.

A Better Django Admin ManyToMany Field Widget

I find the the Django Admin's default models.ManyToManyField widget to be cumbersome to use. It's the HTML select element and if you have a lot of Objects of the "other" model then it's quite impractical to actually find the "other" Objects you want to associate with "this" Object. And if you have a lot of objects of the "other" model it seems to even slows down the rendering of the Admin page.
I'm aware that I can build my own custom admin widget and apply it to my ManyToManyFields as I see fit, but are there any pre-built ones out there that I might use instead? In my dreams, I picture an auto-completing text input HTML widget. Is this even practical/possible to do in the Django admin framework?
Thanks.
Try using the filter_horizontal attribute on your admin class, for example:
class SomeModelAdmin(admin.ModelAdmin):
filter_horizontal = ('users',)
As mentioned in the documentation, "adding a ManyToManyField to this list will instead use a nifty unobtrusive JavaScript "filter" interface that allows searching within the options". filter_vertical does the same thing with a slightly different layout.
you could try using a raw id in the admin.
and the django docs:
http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.raw_id_fields
if you are looking for something with auto-complete you might want to look at this as a starting point http://code.djangoproject.com/wiki/AutoCompleteSolutions
and finally a very simplistic inline Example:
models.py
class SomeModel(models.Model):
users = models.ManyToMany(User)
admin.py:
class SomeModelAdmin(admin.ModelAdmin):
raw_id_fields = ("users",)
I haven't actually played with it but I found this promising looking library referenced elsewhere.
It appears to do exactly what I wanted. Rather than loading the entire list of related objects (regardless of how many there are!) and presenting you with a picker to select a few of them, as filter_horizontal does, it presents a search/filter box and uses typeahead/autocomplete calls to retrieve results dynamically. This is great for the case where you have maybe 5000 users and want to pick 3 or 4 of them without waiting for 5k <option> elements to download and render.
This is an old question, but I want to add an answer here for people who find this just like I did: this situation is exactly what Django inline admins are for. Specifically, I use TabularInlines with raw id fields for many-to-many relations that have too many choices.
https://docs.djangoproject.com/en/2.1/ref/contrib/admin/#django.contrib.admin.TabularInline
You can try using Inline model as such -
class ManyToManyInline(TabularInline):
model = MyModel.many_to_many_field_name.through
raw_id_fields = ("render_raw_id_using_this",)
#register(MyModel)
class YourAdminClass(AnyBaseClass):
exclude = ("many_to_many_field_name",)
inlines = (ManyToManyInline,)
Now there is another issue I faced, finding "render_raw_id_using_this" field name.
So, I moved to shell and tried finding fields in through model as such -
In [1]: MyModel.many_to_many_field_name.through._meta.fields
Out [1]: (<django.db.models.fields.AutoField: id>, <django.db.models.fields.related.ForeignKey: fieldname1>, <django.db.models.fields.related.ForeignKey: fieldname2>)
So, I replaced render_raw_id_using_this with fieldname1
Similarly, you can use these field names to render raw id instead of drop down list in Inline model.

Custom View Engine to solve the Javascript/PartialView Issue?

I have seen many questions raised around PartialViews and Javascript: the problem is a PartialView that requires Javascript, e.g. a view that renders a jqGrid:
The partial View needs a <div id="myGrid"></div>
and then some script:
<script>
$(document).ready(function(){
$('#myGrid').jqGrid( { // config params go here
});
}
</script>
The issue is how to include the PartialView without littering the page with inline tags and multiple $(document).ready tags.
We would also like to club the results from multiple RenderPartial calls into a single document.Ready() call.
And lastly we have the issue of the Javascript library files such as JQuery and JQGrid.js which should ideally be included at the bottom of the page (right before the $.ready block) and ideally only included when the appropriate PartialViews are used on the page.
In scouring the WWW it does not appear that anyone has solved this issue. A potential way might be to implement a custom View Engine. I was wondering if anyone had any alternative suggestions I may have missed?
This is a good question and it is something my team struggled with when JQuery was first released. One colleague wrote a page base class that combined all of the document ready calls into one, but it was a complete waste of time and our client's money.
There is no need to combine the $(document).ready() calls into one as they will all be called, one after the other in the order that they appear on the page. this is due to the multi-cast delegate nature of the method and it won't have a significant affect on performance. You might find your page slightly more maintainable, but maintainability is seldom an issue with jQuery as it has such concise syntax.
Could you expand on the reasons for wanting to combine them? I find a lot of developers are perfectionists and want their markup to be absolutely perfect. Rather, I find that when it is good enough for the client, when it performs adequately and displays properly, then my time is better spent delivering the next requirement. I have wasted a lot of time in the past formatting HTML that no-one will ever look at.
Any script that you want to appear at the bottom of the page should go inside the ClientScriptManager.RegisterStartupScript Method as it renders at the bottom of the page.
http://msdn.microsoft.com/en-us/library/z9h4dk8y.aspx
Edit Just noticed that your question was specific to ASP.NET MVC. My answer is more of an ASP.NET answer but in terms of the rendered html, most of my comments are still relevant. Multiple document.ready functions are not a problem.
The standard jQuery approach is to write a single script that will add behaviour to multiple elements. So, add a class to the divs that you want to contain a grid and call a function on each one:
<script language="text/javascript">
$(document).ready(function(){
$('.myGridClass').each(function(){
$(this).jqGrid( {
// config params can be determined from
//attributes added to the div element
var url = $(this).attr("data-url");
});
});
}
</script>
You only need to add this script once on your page and in your partial views you just have:
<div class="myGridClass" data-url="http://whatever-url-to-be-used"></div>
Notice the data-url attribute. This is HTML5 syntax, which will fail HTML 4 validation. It will still work in HTML 4 browsers. It only matters if you have to run your pages through html validators. And I can see you already know about HTML5
Not pretty but as regards your last point can you not send the appropriate tags as a ViewData dictionary in the action that returns the partial?

What is the best way to organize unobtrusive JavaScript across multiple pages?

I love jQuery but am running into a problem with larger site and multiple pages. My problem is that each page has unique requirements and I need to know the best way to tell jQuery which pages to activate certain things. For example, some forms need the Validator plug-in and some don't, some tables use DataTables plug-in and some don't, etc.
Now I guess I could build complex logic (switch statements) into my application JavaScript file that fire different actions depending on what page they are on, but that just seems smelly. What is the Best Practice here?
UPDATE: There have been lots of good ideas on this question but not quite what I'm looking for. Let me rephrase the question in a more general way.
Currently I am using Rails and its Prototype helpers to build my AJAX components, but I want to move to UJS. How do I tell jQuery which links/buttons to make AJAX and which to avoid? And, given that I can differentiate the that are supposed to have AJAX, how do I give each link its own parameters (method, update, etc.) like I could with the helpers?
I mean besides building a huge page of specific jQuery selectors targeting each individual link/button. :)
jQuery plugins usually are activated upon DOM elements, for example:
$("#element").myPlugin();
If the element doesn't exist on the page, plugins usually behave safely by not activating the plugin.
If your plugin doesn't follow this structure I would suggest doing something like this:
if($("#element").length) $("#element").myPlugin();
A good practice is to have code that is required by all pages in one file and to have specific javascript files for pages that require specific functionality. It sounds as though this is what you are doing anyway, so we have a good basis to build upon.
There are numerous ways in which you could build in what pages need what files, but remember that in normal circumstances, javascript files are cached by the browser such that those files need only downloading once.
In light of this comment
Yes, but what if you have multiple
pages with the same DOM elements? For
example, my validator() plugin is set
up like $('form').validate(), but
sometimes I don't want it to act on
all the forms on the page, only some
of them. What do I do in this case?
I suggest coming up with a convention by which to label elements common across pages that require certain jQuery plugins "attached" to them. For example, if you have a <form> element on a number of different pages that requires a validator() plugin, but there is more than one <form> element on any one particular page (and not all <form> elements should have the validator() plugin), then I suggest using a CSS class to distinguish the <form> elements that do need the plugin.
<!-- HTML -->
<!-- need to apply plugin to this -->
<form class="validator"> ... </form>
<!-- but not to this -->
<form> ... </form>
<script type="text/javascript">
// jQuery Code (in a separate file)
$(function() {
$('form.validator').validator();
});
</script>
That way, the plugin will be applied only to those <form> elements matching the selector.
EDIT:
I'm not sure how the helpers in rails work, but you can pass data to an event handler in jQuery using the data parameter of the bind() method for any data that is not directly part of an <a> element itself (such as an attribute like href). If some of the links require AJAX, then it may make sense to label those with a CSS class and store the URL in the href for the element. Then in your jQuery code that can be used on any pages that have links that make requests through AJAX, you could do something like
<a class="ajax-link" href="/get/someData.php">Data retrieved through AJAX</a>
Standard link with no AJAX
<script type="text/javascript">
$('a.ajax-link').bind('click',ajaxRequest);
function ajaxRequest(e) {
e.preventDefault();
$.get(e.target.href, function(data) {
$('#loadDiv').html(data);
});
}
</script>
the link will work as per a normal link when a user has JavaScript disabled, but will make an AJAX request for data when JavaScript is enabled. I've used a named function here, ajaxRequest as it can make it easier to debug the script (and perhaps also reuse), but you could use an anonymous function if you prefer.
Found nice custom solution
http://topsecretproject.finitestatemachine.com/2010/04/how-to-organizing-javascript-in-ruby-on-rails/
One thing I always do is add an id to the body tag of each page, where the id is the name of the page (<body id="contact">). That makes both javascript and css selection very easy.
For example:
<!-- HTML -->
<!-- need to apply plugin to these forms -->
<form> ... </form> on the about.php page
<!-- but not to this -->
<form> ... </form> on the index.php page
<script type="text/javascript">
// jQuery Code (in a separate file)
$(function() {
$('body#about form').validator();
});
</script>
You could pass key value pairs to your main JavaScript module informing which plugins should be imported. This technique is used by scriptaculous.
<script type="text/javascript" src="scriptaculous.js?load=effects,dragdrop">
Another approach is to define in server side which media files are needed for each form/page, so the html can be rendered automatically with links to the scripts that are going to be used. Django has a good implementation of this technique:
class CalendarWidget(forms.TextInput):
class Media:
css = {
'all': ('pretty.css',)
}
js = ('animations.js', 'actions.js')

Categories

Resources