Can I make a partial template that have readonly fields when used by show and not when used by edit and create? - grails

I've partial templates that are used by the show, edit and create form.
In the show-form I don't want them editable, it can be confusing for the user.
Is there a simple solution for this otherwise I need a different template for the show-form or... why use a template then.
I've tied this and created 2 scripts, one that disables and one that enables.
Script 1.
$(document).ready(function(){
$('.elements').attr('readonly',true);
$('.elements').prop('disabled',true);
});
Script 2.
$(document).ready(function(){
$('.elements').attr('readonly',false);
$('.elements').prop('disabled',false);
});
Then I stored those scripts in assets\javascript.
It worked good in show but both edit and create went read-only too.
It seems like everything that is put in this directory is automatically used in each form, because even though I removed the call from the forms, it was working.
Here I show where I originally added the script-call:
<asset:javascript src="myScript_1.js"/>
</body>
</html>

I was going to add it as a comment but then it started to get long and complicated to follow.
why store it in assets ?
simply add a function block to the templates that need it
_template1.gsp
<script>
$(document).ready(function(){
setReadOnly('${someDefinition}')
function setReadOnly(value) {
if (value==='READONLY') {
$('.elements').attr('readonly',true).prop('disabled',true);
}
}
})
</script>
If this function needs to be shared by a bunch of pages, you could add it to an assets / javascript file but rather than declare it in application.js call the js file <asset:javascript tags specificially on each page
and maybe a variable that you pass to template to say when it should be called
or put that above template1 as a master template and call in each of the other templates that needs the js file (So many options)
Now on the main controller doing action
def MyController {
def view() {
String mode='READONLY'
render view: 'index', model:[instance:params,mode:mode]
}
}
the in index.gsp
if js is there then it will pick up mode and set readonly for that controller action or pass that from one template to another, the controller does not have to define actual mode, the main master view page or index page could define mode too
Maybe you need to play/understand then implement you can't rush these things otherwise you will end up wasting time and rewriting
Just to ensure we are on the same page.
By default the application.js file in the javascript folder has a tree line enabled - this by default then reads in all js files. If you wish to manually call js in different places you will need to remove this line and declare each and every js file that you use like the other lines provided in that file.
So that is the price to pay (no more auto loading js files) until declared in application.js
But then most importantly as you have noticed these are global js functions and really nothing new should be going in there that hasn't got a function something() { } a function call.
They will then react when the actual function is called rather than how you had it which was open for call from any old page since it happened as documents opened regardless

Related

How to include one js script in all views except the landing page

I am converting css template into MVC application. Needless to say, I am just a beginner into webdesign. Template contains reference to one js script, which needs to be included on all pages except the landing page.
<script type="text/javascript" src="js/slider_func_innerpage.js"></script>
How to achieve it in MVC without creation of another master page? I tried to put this line in all views except of default view, but nothing happened. Ideally, I would like to handle this in my _Layout.cshtml file. Is it possible to set there some "if" condition, which would, if true, call partial view containing that single line I posted above? Or is there a more elegant way to load that js file into all views with the exception of the default one?
If you do not prefer to create another layout for landing page (Why not ? This seems to be the right way of doing it) , you might consider conditionally loading this js file in your layout.
So in the view for landing page, set a flag to the ViewBag dictionary
#{
ViewBag.IsLandingPage = true;
}
Now in the layout, check this flag and if it is not null and true, Exclude the inclusion of your js file
#{
if (ViewBag.IsLandingPage == null)
{
<script type="text/javascript" src="js/slider_func_innerpage.js"></script>
}
}

What is is causing the script block in the html head of a gsp to be overwritten

I have 2 grails views that both refer to the same layout:
index.gsp & custom.gsp
Both pages have a <script> block in the <head>. I would like to be able to go to the custom.gsp directly OR work with the content in a tab in index.gsp.
I'm including the custom.gsp content using:
<g:include controller='controllername' action='custom'/>
It appears as if the custom.gsp's <script> block is overwritting the index.gsp's <script> block. If I remove the include everything else on the index page operates normally. If I add the include the code in the <script> block of index.gsp is not executed and upon inspection does not appear to be there.
What is really happening here and why? How can I structure things differently so this doesn't happeN?
UPDATE
I've tried moving the code in custom.gsp out of a <script> block and into a separate javascript file. However; when the include happens now it still pulls the code in as a <script> block and replaces the old one.
I've tried moving the block of each one into the body instead and I've tried adding id attributes to each thinking maybe sitemesh might see it as a unique block then. Still no luck.
UPDATE #2
I tried adding this to the body of index.gsp:
<div>this is a script
<script type="application/javascript">
var xyz='xyz';
</script>
</div>
And the text this is a script is visible but the script is gone!
UPDATE #3
update #2 is wrong, the text is visible but the script is actually there too. I thought it was gone because I was observing the same behavior as when it was gone. However; the real issue is that the entire head of index.gsp was replaced with the included page's head so index.gsp lost the libraries it needed to execute the script code.
As commented by #joshua the view returned by this call:
<g:include controller='controllername' action='custom'/>
must be a fragment instead of a whole gsp view with html, head .. tags.
Think of this when including views using g:include, if you have HomeController and NewsController (check figures below), and you want to include NewsController's view with HomeController's view,
You have to define an action on your NewsController that will only return the content fragment you see on the figure. To do this you can keep the news.gsp view but refactor it in such a way you put the content section in a new template(content fragment). I hope you get what I mean, but leave a comment if something is not clear on my example.

bundles based on areas in MVC

I want to bundle one js file for each area and I am able to do it. But, the problem is to refer .
I dont want to refer all areas js in _Layout.cshtml because it is going to load all areas js files.
I can change js based on the area but our _layout will load only once from there it is only ajax calls.
Which is the right place to refer the js area wise in my case ?
this is what sections are for:
_Layout.cshtml
<head>
#RenderSection("head", false)
...
</head>
the false parameter means not required, that is what you want in this case.
then in your child view
#section head{
#Bundle.Scripts.Add("my scripts location");
}
note that the children get rendered first in MVC framework, so if there is javascript in this view it will start to be executed before the layout. So always use Document Load event or Window Ready , self executing code will fail if it is dependent on the scripts referenced in the Layout
or you can try something like this:
store the area you are in somewhere in your ViewModel , I'll use the ViewBag just for the example. Then create a separate bundle of scripts for each area whereever you register your bundles . Then you can do something like
#Bundle.Scripts.Add("/scripts/" + ViewBag.Area + "/myscripts");

How to disable Backbone code to call an underscore template in non-essential pages?

I have an app that uses Backbone on the client side and Rails on the back end.
My Backbone code calls an underscore template in this manner:
var todo = Backbone.View.extend({
.
template : _.template( $('#todo_rows').html() ),
.
}
However, there's only one page on the site which would need to call this template, and the Backbone js loads on every page. That means that there will be a JS error on the other pages which don't have the "#todo_rows" underscore template defined.
One solution would be to create a blank "#todo_rows" template section for all the other pages, but this strikes me as a major hack and not the best way to deal with this problem.
What is the best way to disable the code to call a template in Backbone? It needs to be disabled if you are not on the home page OR if you are not logged in.
Underscore's template is happy with receiving an empty string as argument. As $('#todo_rows').html() returns null if the #todo_rows element does not exist, you can easily avoid the JS error by using the or-operator idiom:
template : _.template( $('#todo_rows').html() || '' ),
which means "use an empty string as dummy template if the real one is not available".
Another, Rails-specific workaround for this problem (and a neat approach in general) is to add the ejs gem to your project's Gemfile, which allows you to put each template in a separate .jst.ejs file in app/assets and have the Asset Pipleline compile it to a JST object that can be accessed by your view to get the template content:
templates/todo_rows.jst.ejs
<% // Your template (content of former "#todo_rows") here %>
todo_rows_view.js
//= require templates/todo_rows
var todo = Backbone.View.extend({
template : _.template(JST['templates/todo_rows']),
// ...
}
This avoids the roundtrip to the DOM and makes it possible to nicely organize the templates into multiple files, but has the drawback that the template string will be sent as part of the JavaScript code for every page including it.

How to separate javascript libraries and calls in the Rails 3.1 asset pipeline

I'm trying to get the hang of the whole asset pipeline thing, and read the guide and several tutorials about them. But one thing that doesn't become quite clear is wether I should view my javascript asset files as a library or a place to put code that is actually run i.e. $(document).ready. Because by default all the javascript files are included, and it would be weird to have several $(document).ready's in there, not to mention that you don't want the $(document).ready function for every page to be run in the first place. What would be the way to go at this? Use my asset files as a library and put actual calls in my views (ugly)? Or is there a better way to do this?
I too ran into this issue. In a large project you can have somebody put code into document ready to, for example, add a click function to each li within a div with class container.
Now we could all argue that the above code would be too generic and of course may affect li tags in other parts of the application, but the bigger the project, the more likely it is that you will run into a conflict like this leading to unexpected behaviour.
I for one am uncomfortable with a whole bunch of document ready functions running for each and every page loaded. My solution is not necessarily the perfect one, but it's one that I have taken up and will share with you.
In the body tag of each page I add data elements signifying the controller and the action. I then have one document ready script that looks for a class named after the controller with the name Ready appended e.g. HomeReady. It will then call a method on this class (presuming it exists) named after the action. So in your asset coffee file you could write:
class #HomeReady
#index: ->
alert("Hello")
#show: ->
alert("Goodbye")
This allows control right down to the action level. When I came across your question I decided to package this solution into a gem as I have already used it in several projects. You can find it at: https://github.com/intrica/rails_document_ready
If you absolutely don't want a certain piece of initialization code to be run unless the current page is a specific controller/action, then you can try adding an empty element on the page with an id built from that info like "posts_index" using these two helpers:
"#{controller_name}_#{action_name}"
Then in your javascript you can wrap the code inside an if statement that checks for the existence of an element with the appropriate id.
edit: Here's an example of the js partial that I mentioned in the comments.
show.html.haml
= render 'map'
map.html.erb (I normally use haml but it's easier to write js in erb)
<script src='http://www.google.com/jsapi' type='text/javascript'></script>
<script type='text/javascript'>
...
</script>
It's probably not as clean as it could be and it doesn't get the benefits of being part of the asset pipeline but I don't mind because it's only something that gets included on a specific page.

Resources