How To make simple jQuery Ajax in cakephp 3.7

I have made all the functionality but i got some errors how how set url Path to ajax call i got error saying that missing {} I have made simple dropdown which can select country then select state AS follows as code : -
.ctp code -
<?php
echo $this->Form->control(‘country_name’,[
‘options’ => $country,
‘empty’ => ‘Select Country’,
‘id’ => ‘country’ ]
); ?>

///////////////////////////////
controller -:
//////////////////////////////
public function index(){
$country = $this->Countries->find(“all”)->extract(‘name’);
$this->set(compact(‘country’));

}
public function getStates(){

	$state = $this->States->find("all");//->extract('name');
	echo json_encode($state);
  echo json_encode($result);
   //$this->set('_serialize', ['state']);
}
public function getCities(){
	$city = $this->Cities->find()->extract('name');
	echo json_encode($city);
}

}
///////////////////////// JS code ////////////////////////
(document).ready(function(){ (’#country’).change(function(){
var id=$(this).val();
//var targeturl = ‘<?php echo Router::url(["controller"=>"homes","action"=>"getStates"])?>’; //if this url passed i got an error
var targeturl = ‘/homes/getStates’;

if(id=='-1'){
         $('#state').html('<option value="-1">Select State</option>');
}else{
         $("#divLoading").addClass('show');
	$('#state').html('<option value="-1">Select State</option>'); 	
		    $.ajax({
                             type:'post',
                             url: targeturl,                  
			     data:'id='+id+'&type=state',
			     dataType: 'json',
			     success:function(result){
					// $("#divLoading").removeClass('show');
					// $('#state').append(result);
					}
			});	
		}
		});

});

If my implementation is wrong as ajax calling cakephp Please let me know

1 Like

well, you are trying to send a json string followed by another json string…

I’ve taken the liberty to clean up your code a bit so others can read it more easily :slightly_smiling_face:

Your controller:

public function index(){
  $country = $this->Countries->find('all')->extract('name');
  $this->set(compact('country'));
}

public function getStates(){
  $state = $this->States->find("all");//->extract('name');
    
  return $this->response
    ->withType('application/json')
    ->withStringBody(json_encode([
      'state' => $state,
      'result' => $result
    ]));
}

public function getCities(){
  $city = $this->Cities->find()->extract('name');
  return $this->response
    ->withType('application/json')
    ->withStringBody(json_encode([
      'state' => $state,
      'result' => $result
    ]));
}

The form:

<?= $this->Form->control('country_name', [
  'options' => $country,	
  'empty' => 'Select Country',
  'id' => 'country'
]); ?>

Your JS code:

$(function(){
  $('#country').on('change', function() {
    var id = $(this).val();
    var targeturl = '<?= Router::url(["controller"=>"homes","action"=>"getStates"]); ?>';
    if(id == '-1'){
      $('#state').html(`<option value="-1">Select State</option>`);
    }else{
      $("#divLoading").addClass('show');
	    $('#state').html(`<option value="-1">Select State</option>`); 	
		  $.ajax({
              type:'post',
              url: targeturl,                  
			  data:'id='+id+'&type=state',
			  dataType: 'json',
			  success:function(result){
				  // $("#divLoading").removeClass('show');
				  // $('#state').append(result);
			  }
		  });	
	  }
	});
});

I don’t know whether using the return $this->response is the “proper” way to do it but it works for me (feel free to correct any mistakes I may have made)

1 Like

well your code is ok but i got the same error before i am trying
for url: targerurl // here is error which it cannot covert specific address when i check web developers tools. Thanks for help.

Then there seems to be something wrong with the route itself.
Could you show us the output Javascript as seen by the browser?

var targeturl = '<?= Router::url(["controller"=>"homes","action"=>"getStates"]); ?>';

should become something like:

var targeturl = 'www.mydomain.tld/home/getstates';

Dont forget that there’s a trailing ?> as well.
This is the entire variable value of targeturl:

<?= Router::url(["controller"=>"homes","action"=>"getStates"]); ?>

so it’ll become:

var targeturl = '<?= Router::url(["controller"=>"homes","action"=>"getStates"]); ?>';

Sitll not solved the problem. I think error from js.

does the JS get loaded from an external file? (eg. myajax.js)
If so, then we’ve found the problem.
If not, the problem is somewhere in your PHP.

Well js is working. Also worked ajax call but when it reaches url then it gives an error as above image shows. I have also try cookbook reference for building URL but same error occurred. What else I can do

You did not answer my question.
Does the JS get loaded from an external file?
Just because the JS itself works doesn’t mean something might be wrong.

My suspicion is that you are using an external file (eg. webroot/js/myajax.js) which causes this issue as CakePHP just serves the JS file as is (so using CakePHP functionality in it won’t work).

yes, i am added external js.

Ah yes, then it’s “normal” that you get this issue.

When using an “external” JS file, the file get’s served as-is.
It won’t go through CakePHP’s engine and therefore, you’ll get this error.
Like I’ve already said, I’m not aware of a solution for this :\

I got around it myself by adding this into the head tag and then using that variable in my script (since it’ll be scoped to the entire window) but that might not work nicely for what you’re doing:

var matomoHost = '<?= h($this->matomoUrl); ?>';
var matomoId = '<?= h($this->matomoId); ?>';
1 Like

although , i am tried the js code inside script tags in the ctp file. then it won’t work then i have put simple code var targeturl = ‘homes/getStates’; then it’s worked.but at result time get an errror.

There is a workaround.
When using external JS, you can always mention the variable in an in-line script tag.

For example:

In your views:

<script>
var targetUrlOnCountryChange = <?= Router::url(["controller"=>"homes","action"=>"getStates"]); ?>
</script>

And then in your external JS

$.ajax({
          type:'post',
          url: targetUrlOnCountryChange,

This way you are not worried about changing the JS files incase you or your colleague happen to change the route style.

The error screenshot.
It simply says that PHP tag hasn’t been intrepreted correctly. Which is correct as @FinlayDaG33k said, Cake (infact any other framework in the world) does not ‘compile’ static content. JS, CSS, Images, etc. However, you can create an action which serves LIKE a js file by using Routing File Extensions

Git Good :slight_smile:

1 Like

I actually use this solution (using an in-line script tag) on my website to load some matomo variables.
It’s not very beautiful but it does the trick for me.
However, when you have a lot of these ajax scripts with these routes, it might become very dirty very quickly :\

An alternative way on doing it with a form, however, is to have your form have an action and using that action in the jQuery:

<?= $this->Form->control('country_name', [
  'url' => ["controller"=>"homes","action"=>"getStates"],
  'options' => $country,	
  'empty' => 'Select Country',
  'id' => 'country'
]); ?>
$('#country').on('change', function(e) {
  e.preventDefault();
  var targetUrl = $(this).attr('action');

  // The rest of your function
});
1 Like

Thank you both karmicdice, FinlayDaG33k of you for replying my post. well i did the successfully ajax call but my select

state

was not show any states instead of the shows me countries which i get from ajax response. how can i get option field that response from previous selection of the country.:slightly_smiling_face:
//////////////////////////////// ctp code



			<?php $count = 1;
			foreach ($country as $key => $country) { 
				$options[] = ['text' => $country->name, 'value' => $country->id];}		     
				echo $this->Form->select('country_name',
					$options,[ 
						'empty'   => 'Select Country',
						'id'      => 'country',
						'class'=>'form-control'
					]); ?>
				</div>
			</div>
			<div class="col-sm-4">
				<div class="form-group">
					
					<?php 

					echo $this->Form->select('state_name',
						$options,
						[
							'empty'   => 'Select State',
							'id'      => 'state',
							'class'=>'form-control'
						]);?>
					</div>
				</div>
				<div class="col-sm-4">
					<div class="form-group">
						<?php 
						echo $this->Form->select('city_name',
							[ 
								'empty'   => 'Select City',
								'id'      => 'city',
								'class'=>'form-control'
							]);?>
						</div>
					</div>
				</div>
				<?php $this->Form->end(); ?>
			</div>
		</div>

//// js code

$(‘document’).ready(function(){
$(‘#country’).change(function(){

					var searchkey = $(this).val();
					searchName( searchkey );
				});
				function searchName( keyword ){
					var data = keyword;
					alert(data);
					$.ajax({
						method: 'get',
						url : "<?php echo $this->Url->build( [ 'controller' => 'homes', 'action' => 'statess' ] ); ?>",
						data: {country_id:data},
						success: function( response )
						{     
                		                    $('#state').append(response);
                                                     }
                                      });
				};
			});
	</script>