How to use AJAX in Joomla component to load the State field based on the country selected? - joomla

Inside a component's view, I have something like this:
<?php echo TestcompHelperFind::loadStates(...); ?>
<?php echo TestcompHelperFind::loadCounties(...); ?>
The above static functions load <select> dropdowns with the state names and countries respectively.
The class TestcompHelperFind is located in the file /administrator/components/com_testcomp/helpers/find.php.
How do I load States dropdown list based on the country selected using AJAX? I'm not sure what url I should provide in the ajax function.

On the client, you will need a function that watches the country select for changes, and when it happens calls the appropriate url with a callback that will populate the counties select.
On the server, you need to output the select content.
Since you have the html output already working, let's use this approach. As an alternative you could have your server method return a json object and use the javascript to parse it and populate the select. But let's stick to html communication, i.e. the server returns the html contents of the select.
1. On the server
1.a. Output the counties select
We only need to return the result of the TestcompHelperFind::loadCounties(...); to the ajax call. This is achieved easily writing a new method in the component's controller, i.e. the controller.php in the root of the component folder or one of the sub-controllers if appropriate. It's up to you to place it in a meaningful spot.
Inside the controller simply add a new public task such as
class SomethingController extends JController
{
public function getCountiesHTML() {
$input = JFactory::getApplication()->input;
$country = $input->getCMD('filter_country');
// load helper if necessary, then:
echo TestcompHelperFind::loadCounties($country);
exit; // this will stop Joomla processing, and not output template modules etc.
}
Please note the exit; at the end, this will make Joomla output only the component's output (our echo) and not the whole template/modules etc.
1.b Add an ID to the country and county selects so that it will be possible to manipulate them on the client; I'll assume filter_country and filter_county ;
2. On the client
you will want to invoke the url
index.php?option=com_something&action=getCountiesHTML&filter_country=UK
when the country select is changed. It will also need to cancel any pending requests to avoid overlapping messages. To keep things simple, let's assume you use a library to handle Ajax, I'll write an example for jQuery:
<script>
var xhr;
jQuery(function($) {
$('#filter_country').change(function(){
var filterCountry = $('#filter_country').val();
if (xhr && xhr.abort) {xhr.abort();xhr=false;}
xhr = jQuery.ajax(
url: 'index.php',
data: 'option=com_something&task=getCountiesHTML&filter_country='+filterCountry,
success: function(data){
jQuery('#filter_county').replaceWith(data);
}
);
});
});
</script>
For cancelling the previous request, please see a dedicated answer such as this one.

Related

Serialize Laravel Query Builder

I would like to be able to construct a query using laravel, and serialize it into a url string.
This would allow me to create routes which would unserialize a query builder, run the query, and make a view which displays the database results.
For example, to implement a button which refreshes a list of posts made by kryo:
http://example.com/ajax/posts.php?name=kryo&order_by=created_at&order_type=desc
Posts.php would simply be a route which unserializes, validates, and runs the query in the url params, and provides the results to a view.
Perhaps this is not useful in general, but I would personally find it handy specifically for ajax requests. If anyone knows how to implement this as a laravel plugin of some nature, that would be fantastic.
I'll try to give you a basic idea:
In Laravel you have to create a route to make a request to a function/method, so at first you need to create a route which will be listening for the ajax request, for example:
Route::get('/ajax/posts', array('uses' => 'PostController#index', 'as' => 'showPosts'));
Now, create a link in the view which points to this route, to create a link you may try this:
$url = to_route('showPosts');
If you use something like this:
<a class='ajaxPost' href="{{ $url }}?name=kryo&order_by=created_at&order_type=desc">Get Posts</a>
It'll create a ink to that route. So, make sure you are able to pass that $url to your JavaScript or manually you can write the url using /ajax/posts?name=.... Once you done creating the link then you need to create your JavaScript handler for this link (maybe using click event) then handle the click event from your handler, make ajax request, if it's jQuery then it could be something like this:
$('.ajaxPost').on('clcik', function(e){
e.preventDefault();
var url = $(this).attar('href');
$.getJSON(url, function(response){
$.each(response, function(key, value){
// loop... you may use $(this) or value
});
});
});
In your PostController controller class, create the index method:
class PostController extends BaseController {
public function index()
{
$name = Input::get('name');
$order_by = Input::get('order_by');
$created_at = Input::get('created_at');
$order_type = Input::get('order_type');
$posts = Post::whereName($name)->orderBy($order_by, $order_type)->get();
if(Request::ajax()) {
return Response::json($posts);
}
else {
// return a view for non ajax
}
}
}
If you want to send a rendered view from the server side to your JavaScript handler as HTML then change the getJson to get and instead of return Response::json($posts); use
return View::make('viewname')->with('posts', $posts);
In this case make sure that, your view doesn't extends the master layout. This may not be what you need but it gives you the idea how you can implement it.

How do I auto fill field values in a section of a form that is loaded via ajax in Laravel 4?

I have a section of a form that dynamically loads different sets of fields based on the user's selection in a control. I'm using a javascript event handler to detect when the selection changes, and using AJAX (with HTML payload) to pull in the proper set of fields.
I would like to be able to use Laravel's Form::getValueAttribute() method to automatically fill in the form fields' values in both the static and dynamic form parts. However, the partial view that is loaded by my AJAX call does not have the same instance of the Form class as the view with my main Form, so I can't simply call getValueAttribute() in the partial.
My thought is to make the AJAX call a POST, and serialize the necessary data (a subset of Input::old() or the model data depending whether the page is loaded as the result of validation errors, or an UPDATE request) to send along with the POST so that the HTML fragment I get back has the values set properly.
Is this the best way to get what I want? If so, does Laravel have any tools to help with the serialization of form data? If not, what might be a better approach?
I've found an approach I like better. When the view is loaded normally I use AJAX as usual to load the partial. But when the view is loaded for a validation post-back or for editing, I use Laravel's Views' nest method to nest the partial view containing the proper fields directly into the response. The partial then has access to all the Input and error data I need. The user is still able to change the field set as usual but I put up a confirm prompt for them if they have already set some values in a field set they previously selected. If they decide to proceed anyway, the field set is cleared and a new field set is brought in via AJAX as usual.
My code looks something like this:
Controller:
public function newThing() {
if ( Request::session()->has('errors') ) {
// this is a validation post-back
return View::make('thing')
->nest('fields', 'fields_partial');
} else {
// just a normal unfilled form
return View::make('thing');
}
}
public function editThing() {
return View::make('thing')
->nest('fields', 'fields_partial');
}
View: thing.blade.php (just a snip of it)
...
<form>
...
<select id="picker">...</select>
<div class="sub-fields">
{{ isset($fields) ? $fields : '' }}
</div>
...
</form>
...
<script>
$('#picker').change(function() {
// if any .sub-fields inputs have been changed, get confirmation from the user
// if user confirms, do ajax stuff to replace .sub-fields contents with new field set
// otherwise cancel the change
});
</script>

JavaScript code in view issue in Laravel

I put JavaScript code in a view file name product/js.blade.php, and include it in another view like
{{ HTML::script('product.js') }}
I did it because I want to do something in JavaScript with Laravel function, for example
var $path = '{{ URL::action("CartController#postAjax") }}';
Actually everything is work, but browser throw a warning message, I want to ask how to fix it if possible.
Resource interpreted as Script but transferred with MIME type text/html
Firstly, putting your Javascript code in a Blade view is risky. Javascript might contain strings by accident that are also Blade syntax and you definitely don't want that to be interpreted.
Secondly, this is also the reason for the browser warning message you get:
Laravel thinks your Javascript is a normal webpage, because you've put it into a Blade view, and therefore it's sent with this header...
Content-Type: text/html
If you name your file product.js and instead of putting it in your view folder you drop it into your javascript asset folder, it will have the correct header:
Content-Type: application/javascript
.. and the warning message will be gone.
EDIT:
If you want to pass values to Javascript from Laravel, use this approach:
Insert this into your view:
<script type="text/javascript">
var myPath = '{{ URL::action("CartController#postAjax") }}';
</script>
And then use the variable in your external script.
Just make sure that CartController#postAjax returns the content type of javascript and you should be good to go. Something like this:
#CartController.php
protected function postAjax() {
....
$contents = a whole bunch of javascript code;
$response = Response::make($contents, '200');
$response->header('Content-Type', 'application/javascript');
....
}
I'm not sure if this is what you're asking for, but here is a way to map ajax requests to laravel controller methods pretty easily, without having to mix up your scripts, which is usually not the best way to do things.
I use these kinds of calls to load views via ajax into a dashboard app.The code looks something like this.
AJAX REQUEST (using jquery, but anything you use to send ajax will work)
$.ajax({
//send post ajax request to laravel
type:'post',
//no need for a full URL. Also note that /ajax/ can be /anything/.
url: '/ajax/get-contact-form',
//let's send some data over too.
data: ajaxdata,
//our laravel view is going to come in as html
dataType:'html'
}).done(function(data){
//clear out any html where the form is going to appear, then append the new view.
$('.dashboard-right').empty().append(data);
});
LARAVEL ROUTES.PHP
Route::post('/ajax/get-contact-form', 'YourController#method_you_want');
CONTROLLER
public function method_you_want(){
if (Request::ajax())
{
$data = Input::get('ajaxdata');
return View::make('forms.contact')->with('data', $data);
}
I hope this helps you... This controller method just calls a view, but you can use the same method to access any controller function you might need.
This method returns no errors, and is generally much less risky than putting JS in your views, which are really meant more for page layouts and not any heavy scripting / calculation.
public function getWebServices() {
$content = View::make("_javascript.webService", $data);
return (new Response($content, 200))->header('Content-Type', "text/javascript");
}
return the above in a method of your controller
and write your javascript code in your webService view inside _javascript folder.
Instead of loading get datas via ajax, I create js blade with that specific data and base64_encode it, then in my js code, I decode and use it.

Codeigniter: jquery not passing its 'load' value to the controller

The issue is what I say in the title. The parameter of the index selected in the first dropdown box is not sent to the controller. Therefore the controller cannot pass any value to the model etc. If I harcode saying $pais_id = 1 and send that to the Model it works, so this means, the issue is in the controller not getting it from the jquery.
VIEW
<script type="text/javascript">
//jquery code for source list
$(document).ready(function(){
$('#country').change(function() {
if ($(this).val()!='') {
$("#source").load("/CI-3/controllers/control_form.php",{pais_id: $(this).val()});
}
});
}); // end of country and city function
</script>
The problem must be there because I don't visualize the process:
Jquery detects the changing in the select dropdown list and fetches the selected id. Alright, but what happens next ? it sends it to the controller, yes, and? the controller forwards it to the model, the model does a sql search and returns an array back to the controller and the controller forwards it to the view, but, how does #source in the line above get affected after all that?, so it will not modify itself
$source['source'] = $this->model_form->get_source($pais_id);
should be
$data['source'] = $this->model_form->get_source($pais_id);
in controller. third parameter in view is if it's returned or echo'd. all values are passed in the 2nd parameter as an array.

Symfony 2.0 updating select options with JS?

I've been googling for hours but surprisingly I didn't find any topic on that subject.
I have the following Form
class propertyType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('city')
->add('district', 'entity', array('class'=>'FlatShanghaidefaultBundle:district',
'property'=>'name',
'query_builder' => function ($repository) {
$qb = $repository->createQueryBuilder('district');
$qb->add('where', 'city = :city');
$qb->setParameter('city', 1);
return $qb;
}
public function getName()
{
return 'property';
}
}
When the user choose a City in the form, I want the options of district to be dynamically updated and limited to that city. With Ajax or JS?
What would be the best practice? Do you know a tutorial on that topic?
If someone can put me on the right tracks, that would help a lot..
Thanks!
The query builder will not solve your problem, you can remove it altogether.
That query is run when the form gets built, once you have it on your browser you need to use javascript to populate the options.
You can have the options stored in a javascript variable, or pull them from the server as needed with ajax (you will need a controller to handle these ajax requests).
You will probably want to use some jquery plugin to handle the cascading logic between the select elements, there are a couple available:
I use this one, but it seems to be offline: http://devlicio.us/blogs/mike_nichols/archive/2008/05/25/jquery-cascade-cascading-values-from-forms.aspx
And there is this one, which I never used really: http://code.google.com/p/jquery-cascade/
There is also at least this Bundle I know of: https://github.com/genemu/GenemuFormBundle, which has ajax field types available for several jquery plugins. This may save you writing the ajax part to handle the data, as it comes built in (it's probably easier to implement the controller your self anyway). I haven't tried this one, and I don't know if it has cascading support.
Jbm is right about the query builder. And his approach is perfecly valid.
Another option could be to dispense the cascade select in favor of an autocomplete field.
Assuming that you save the countries, cities and districts as entities and have a relation between them, you do not even need to save what city/country has been selected because you can just call:
$district->getCity()->getCountry();
I have implemented a similar thing for country/city selection and will link here to the the main involved files.
First, create a custom form type to encapsulate all form stuff, it contains a hidden field to store the selected id and a text field to serve as input for the autocomplete logic:
https://github.com/roomthirteen/Room13GeoBundle/blob/master/Form/LocationFieldType.php
Then theme the form type:
https://github.com/roomthirteen/Room13GeoBundle/blob/master/Resources/views/Form/fields.html.twig
The url of the autocomplete source is passed as data attribute so no JS will be smutching the html code.
Last but not least, the JS functions have to be implemented:
https://github.com/roomthirteen/Room13GeoBundle/blob/master/Resources/public/jquery.ui.location-autocomplete.js
The result can be seen in the image below, see that for clarity the country name will be displayed in braces behind the city name:
--
I favor this solution much more that using cascade selects because the actual value can be selected in one step.
cheers
I'm doing this myself on a form.
I change a field (a product) and the units in which the quantity can be measured are updated.
I am using a macro with parameters to adapt it more easily.
The macro :
{% macro javascript_filter_unit(event, selector) %}
<script>
$(function(){
$('#usersection')
.on('{{ event }}', '{{ selector }}', function(e){
e.preventDefault();
if (!$(this).val()) return;
$.ajax({
$parent: $(this).closest('.child_collection'),
url: $(this).attr('data-url'),
type: "get",
dataType: "json",
data: {'id' : $(this).val(), 'repo': $(this).attr('data-repo'), parameter: $(this).attr('data-parameter')},
success: function (result) {
if (result['success'])
{
var units = result['units'];
this.$parent.find('.unit').eq(0).html(units);
}
}
});
})
});
</script>
{% endmacro %}
The ajax returns an array : array('success' => $value, 'units' => $html). You use the $html code and put it in place of the select you want to change.
Of course the javascript code of the ajax call need to be modfied to match your fields.
You call the macro like you would normally do:
{% import ':Model/Macros:_macros.html.twig' as macros %}
{{ macros.javascript_filter_unit('change', '.unitTrigger') }}
So I have two arguments : the event, often a change of a select. and a selector, the one whose change triggers the ajax call.
I hope that helps.

Resources