Following the simple example from Jquery: Accordion Example
Using a remoteLink function from grails (AJAX) the information is pulled back from the controller and sent back to the GSP, which works fine. I do however want this data to be placed within a Accordion container... click here for screenshot of current functionality.
(Event Create Page rendering form template) _form - GSP:
<g:remoteLink controller="event" action="showContacts" method="GET" update="divContactList">Show Contacts!</g:remoteLink>
<div id="divContactList">
<g:render template="contactListAjax" model="[contactList: contactList]" />
</div>
<script type="text/javascript">
$(document).ready(function()
{
alert("Checking if I'm ready :)");
$( "#accordion" ).accordion({
header: 'h3',
collapsible: true
});
});
</script>
_contactListAjax - GSP Template
<div id="accordion">
<g:each in="${contactList}" status = "i" var="contact">
<h3>${contact.contactForename}</h3>
<div><p>${contact.email}</p></div>
</g:each>
</div>
Not quite sure what I'm doing wrong here, as I'm encasing the data with the div with an id accordion, yet doesn't load. Please refer to screenshot link above to see what is currently happening.
UPDATE
Event (Standard Generated CRUD, only showing where the relivant imports are) create - GSP
<link rel="stylesheet" href="${resource(dir: 'css', file: 'jquery.ui.accordion.css')}"/>
<g:javascript src="jquery-1.7.1.min.js"></g:javascript>
<g:javascript src="jquery-ui.min.js"></g:javascript>
Try replacing:
$(function() {
$( "#accordion" ).accordion({
collapsible: true
});
})
with
$( "#accordion" ).accordion({
collapsible: true
});
Edit:
You can't use an <r:script /> tag after the request is finished. The server has already laid out all of the resources. Change it to a standard javascript tag. Also the inclusion of your javascript and css files should be elsewhere on the page.
I solved this... (well Hack & Slash for now... not the best, but only viable solution for now)
var whatever is the stored HTML generated by the AJAX and placed into the divContactList on the update function. The Accordion is deleted and then rebuilt (not the best approach I realise)
var whatever = $('#divContactList').html();
$('#accordion').append(whatever)
.accordion('destroy').accordion({
collapsible: true,
active: false
});
Related
BACKGROUND: I'm trying to use .load() to build a site from several
pages, where a user can click a button to load the next page's
content using .load().
PROBLEM: The .load() only works on the first click (loads
content from pagetwo.html), then doesn't load any
content for pages after that.
CODE:
My start page markup looks like this:
<head>
<script src="script.js"></script>
</head>
<body>
<div class="content">
<p>This is page 1.</p>
<button id="pageone">next</button>
</div>
</body>
Then my pages 2-5 contain only partial HTML, to be loaded
into div.content:
Page 2:
<p>This is page 2.</p>
<button id="pagetwo">next</button>
Page 3:
<p>This is page 3.</p>
<button id="pagethree">next</button>
Etc.
My jQuery script.js is loaded only once, with the full start page html:
$(document).ready(function() {
//load pages
$('button#pageone').click(function () {
$("div.content").load("page-two.html");
});
$('button#pagetwo').click(function () {
$("div.content").load("page-three.html");
});
$('button#pagethree').click(function () {
$("div.content").load("page-four.html");
});
});
Any ideas on why only the first .load() request works?
The ID #pagetwo and on onwards are newly introduced elements to the DOM after the first initial $(document).ready(). Thus, you'd have to access it differently there after:
$(document).on('click', 'button#pageone', function() {
$("div.content").load("page-two.html");
});
$(document).on('click', 'button#pagetwo', function() {
$("div.content").load("page-three.html");
});
$(document).on('click', 'button#pagethree', function() {
$("div.content").load("page-four.html");
});
The .on() handler will listen to any new elements through the entire document (As it starts at root) at any one stage. Your initial code was looking for elements that were present on just page load.
To read more about it, See the documentation about the .on() handler regarding new elements at "Direct and delegated events".
I am building an application that uses the Bootstrap Collapse component to render a sequence of panels, all of which will initially be in the collapsed state.
Since the page may contain many such panels and each of them may contain a large amount of content, it seems appropriate to populate these panels on demand, by executing an AJAX call when the user expands any panel.
The dynamic content of the page (including the markup for the panels) is rendered using AngularJS, and I assume it's possible to configure Angular to bind to an event on the panel elements, that results in their content being lazy loaded when they expand.
Unfortunately, after looking at the AngularJS docs and the available tutorials, I can't see how best to tackle this. Can anyone throw any light on it?
Thanks in advance,
Tim
This is way old, but the question might still come up now and then. I now find this to be the most suitable solution without polluting your controllers:
(myDirective loading its content via AJAX right after its creation.)
<accordion>
<accordion-group
heading=""
ng-repeat="foo in bar"
ng-init="status = {load: false}"
ng-click="status.load = true">
<myDirective ng-if="status.load"></myDirective>
</accordion-group>
</accordion>
each element created by ng-repeat gets its own $scope, so clicking ab accordion-group will result in only the respective directive being loaded.
edit:
depending on latency and the size of the data that's to be lazy loaded, you might consider using ng-mouseover instead of ng-click. That way loading starts some 100ms before the user opens the accordion which can reduce 'sluggishness' of your UI. Obviously there's the downside of occasionally loading content of groups that are never actually clicked.
#Tim Coulter, I've created something following the idea of #Stewie.
It can definitely be improved, but I guess it's a good starting point.
I've created a small directive to bind the click event of the accordion's panel. When the click event is fired, I passed the panel template via the panel-template= attribute and it updates the main-template which is used inside the panel.
It makes reference to 2 html files (panel1.html and panel2.html) that contains the content of the each panel.
I would recommend to create a service to fetch these files via AJAX - just the way you wanted.
On the code below I created a service called dataService for this purpose and you should bind it to the click event - so files are loaded on demand when the user clicks on it.
Note the the mainTemplate is a common panel to all accordions, so when it changes the all the accordions will have the same content, BUT I am assuming you want to display only one panel at time, right ?!
Anyway as I said before the logic can be improved to fix these little 'gotchas', but I believe the core functionality is there to start with. :)
<!doctype html>
<html ng-app="myApp">
<head>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="http://code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script>
var myApp = angular.module('myApp', []);
myApp.controller('AccordionDemoCtrl', ['$scope', 'dataService', function ($scope, dataService) {
$scope.oneAtATime = true;
$scope.mainTemplate = '';
$scope.groups = [
{
id: "001",
title: "Dynamic Group Header - 1",
content: "Dynamic Group Body - 1",
template: "panel1.html"
},
{
id: "002",
title: "Dynamic Group Header - 2",
content: "Dynamic Group Body - 2",
template: "panel2.html"
}
];
}]);
myApp.factory('dataService', [ '$http', function($http){
return {
getData: function() {
return // you AJAX content data here;
}
}
}]);
myApp.directive('accordionToggle', [function () {
return {
restrict: 'C',
scope: {
mainTemplate: '=',
panelTemplate: '#'
},
link: function (scope, element, iAttrs) {
element.bind('click', function(e){
scope.mainTemplate = scope.panelTemplate;
scope.$apply();
});
}
};
}]);
</script>
</head>
<body ng-controller="AccordionDemoCtrl">
<div class="accordion" id="accordionParent">
<div class="accordion-group" ng-repeat="group in groups" >
<div class="accordion-heading">
<a class="accordion-toggle" main-template="$parent.mainTemplate" panel-template="{{ group.template }}" data-toggle="collapse" data-parent="#accordionParent" href="#collapse{{ $parent.group.id }}">
Collapsible Group Item {{ $parent.group.id }}
</a>
</div>
<div id="collapse{{ group.id }}" class="accordion-body collapse">
<div class="accordion-inner">
<div class="include-example" ng-include="mainTemplate"></div>
</div>
</div>
</div>
</div>
</body>
</html>
Could any one give an example, how to use Jquery in Controller Page. MVC3 -ASP.NET(How To put various tags like )
I want to show a simple alert before rendering a view in Controller.
Thank you.
Hari Gillala
Normally scripts are part of the views. Controllers shouldn't be tied to javascript. So inside a view you use the <script> tag where you put javascript. So for example if you wanted to show an alert just before rendering a view you could put the following in the <head> section of this view:
<script type="text/javascript">
alert('simple alert');
</script>
As far as jQuery is concerned, it usually is used to manipulate the DOM so you would wrap all DOM manipulation functions in a document.ready (unless you include this script tag at the end, just before closing the <body>):
<script type="text/javascript">
$(function() {
// ... put your jQuery code here
});
</script>
If you are talking about rendering partial views with AJAX that's another matter. You could have a link on some page that is pointing to a controller action:
#Html.ActionLink("click me", "someAction", null, new { id = "mylink" })
and a div container somewhere on the page:
<div id="result"></div>
Now you could unobtrusively AJAXify this link and inject the resulting HTML into the div:
$(function() {
$('#mylink').click(function() {
$('#result').load(this.href, function() {
alert('AJAX request finished => displaying results in the div');
});
return false;
});
});
I have to load accordion panels into one of my pages using AJAX and I'm finding that JQuery is 'accordionizing' all of the panels defined in the HTML file, but none of the panels loaded via Javascript. One other little quirk: I'm doing this on a nested accordion - the accordion within an accordion. It's accordion inception, if you will.
I checked other Stack Overflow questions and the JQuery Forum and I found that most of them are about resizing panels after loading data. The closest question I found was here, but it doesn't answer my question because trying to destroy and then re-accordion-ize my JS-loaded panels does not work.
This is the relevant section of my html head section:
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/themes/flick/jquery-ui.css" type="text/css" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$(".accordion").accordion({
collapsible: true,
icons: false,
autoHeight: false,
active: false
});
});
</script>
This is the relevant section of my html body section:
<div class="accordion">
<h3 id="acc1">First panel title</h3>
<div>First panel content</div>
<h3 id="acc2">Second panel title</h3>
<div class="accordion" id="ajaxresults-aliaslist">This is where the nested accordion goes</div>
<h3 id="acc3">Third panel title</h3>
<div>Thirdpanel content</div>
</div>
<script type="text/javascript">
$("#ajaxresults-aliaslist").load("/loadaliaslist/");
</script>
When javascript loads "/loadaliaslist/", it received a message with the following content:
<h3>1st panel within a panel title</h3>
<div>1st panel within a panel content</div>
<h3>2nd panel within a panel title</h3>
<div>2nd panel within a panel content</div>
<h3>3rd panel within a panel title</h3>
<div>3rd panel within a panel content</div>
I know that the content above is being passed to the div because the content appears un-accordionized when I load the page. Instead of an accordion within an accordion, I just get a bunch of boring content sitting at the first level of the dream sequence. I've got to go down a level... Wait, where was I?
Right, one more thing: I have tried two things that did not work:
- I tried putting both the load and the accordion scripts at the bottom of the page (load first), hoping that the order would matter. (noobish? not sure...)
- I tried adding a script at the end to destroy and recreate the panels like so:
$("#ajaxresults-aliaslist").accordion('destroy').accordion();
That's all. I really hope someone out there is the Leonardo DiCaprio of accordions, and can help rescue me from my predicament. Much appreciated!
Untested but try something like this...
The issue is most likely due to the fact that the event handlers aren't being attached to the inserted content (via Ajax).
$(function() {
var config = {
collapsible: true,
icons: false,
autoHeight: false,
active: false
}
$(".accordion").live('load', function(){
$(this).accordion(config);
}).accordion(config);
});
EDIT: Changed the code a bit (still untested).
EDIT FROM OP: SOLUTION FOUND
After playing around more, I found a way to solve this problem as long as AJAX is loading the new data immediately on pageload. Solution was to call the accordion function, only once, immediately after the load. As in the solution below:
<script type="text/javascript">
$("#ajaxresults-aliaslist").load("/loadaliaslist/",
function(response, status, xhr) {
if (status == "error") {
var msg = "Sorry but there was an <strong>error</strong>: ";
$("#error").html(msg + xhr.status + " " + xhr.statusText);
}
/* All panels are accordionized immediately after loading the content */
$(".accordion").accordion({
collapsible: true,
icons: false,
autoHeight: false,
active: false
});
});
</script>
A word of caution: This will only work if all of the accordion content is loaded immediately. If there are pieces of content loaded after the first accor
I suppose that you want to reset the accordion control. Try to do this:
$('#accordion')[0].innerHTML = "";
After that fill the control with your HTML using the append.
My html document looks like this:
<html>
<head> .. load jquery and other stuff </head>
<body>
<div id="cool_container">
<div class="cool">.. no script friendly markup ..</div>
</div>
<a id="cool_link">Link</a>
<script>
function installStuff(){
$('.cool').coolPlugin();
$('#cool_link').click(function(){
$('#cool_container').load('/anothercooldiv.html');
});
}
$(document).load(function(){ installStuff(); });
</script>
</body>
</html>
Of course, /anothercooldiv.html gives another <div class="cool"> .. etc ...</div> fragment.
So what's the best way to turn the fresh cool div into a coolPlugin without breaking everything (and writing some nasty hacks) ?
It'd would be great to be able to either:
Call installStuff with a default jQuery context '#cool_container', so I could call something like:
$.doThisInContext(function(){installStuff();}, $('#cool_container');
In the load callback.
Or, have an equivalent of 'live' (that would solve the problem of links if cool contains links), but on an element existence, that I could use like that in my function installStuff:
$('.cool').exists(function(what){ what.coolPlugin() };
Then the coolPlugin would be installed on all cool elements now and in the future.
I'd suggest the .livequery() plugin for this still:
$(function() {
$('.cool').livequery(function() {
$(this).coolPlugin();
});
$('#cool_link').click(function(){
$('#cool_container').load('/anothercooldiv.html');
});
});
The important bit:
$('.cool').livequery(function() {
$(this).coolPlugin();
});
Will run for every current and future .cool element as they're added, running the plugin on each.
Applying the plugin to the newly ajax loaded content shouldn't be too tricky:
$('#cool_container').load('/anothercooldiv.html', function() {
$(this).coolPlugin();
});