Link to home
Start Free TrialLog in
Avatar of Marco Gasi
Marco GasiFlag for Spain

asked on

CodeIgniter, Ajax and Knockout.js

Hi all.
Maybe it's  more than I can chew, but here I am.

Following pieces of code works well together (source just a bit modified here: http://phpsblog.agustinvillalba.com/adding-ajax-codeigniter-jquery/

The goal in following code is the get a select filled up accordingly to another select selected option.

CategoryList.php
<?php
class CategoryList extends CI_Controller
{

	public function __construct()
	{
		parent::__construct();
		$this->load->model( 'categories_model' );
	}

	public function index()
	{
		
	}

	public function list_products()
	{
		//Get all the categories in the system
		$data["categories"] = $this->categories_model->get_categories();
		//Loading the view for the record edition
		$this->load->view( 'templates/header', $data );
		$this->load->view( 'list_products', $data );
		$this->load->view( 'templates/footer' );
	}
}

Open in new window


SubcategoryList.php
<?php

class SubcategoryList extends CI_Controller
{

	public function __construct()
	{
		parent::__construct();
		$this->load->model( 'subcategories_model' );
	}

	public function index()
	{
		
	}

	public function list_dropdown()
	{
		$cat_id = $this->input->post( 'cat_id' ); //Read the category id sent by POST
		$subcategories = $this->subcategories_model->get_subcategories_by_category( $cat_id ); //Get the matches list for that tournament from the DB
		$data["subcategories"] = $subcategories;
		$this->load->view( 'list_dropdown_view', $data );
	}

}

Open in new window


Categories_model.php
<?php

class Categories_model extends CI_Model
{

	public function __construct()
	{
		$this->load->database();
	}

	public function get_categories()
	{
		$query = $this->db->get( 'categories' );
		return $query->result_array();
	}

}

Open in new window


Subcategories_model.php
<?php

class Subcategories_model extends CI_Model
{

	public function __construct()
	{
		$this->load->database();
	}

	public function get_subcategories_by_category( $catid )
	{
		$query = $this->db->get_where( 'subcategories', array( 'category_id' => $catid ) );
		return $query->result_array();
	}

}

Open in new window


First view: list_products.php
<div class="slider-wrapper">
</div>
<div class="container marketing">
	<!-- Three columns of text below the carousel -->
	<div class="row">
		<div class="col-lg-12">

			<strong>Categories:</strong>
			<select id="category_list">
				<?php
				foreach ( $categories as $category )
				{
					echo "<option value='{$category["category_id"]}'>{$category["category_name"]}</option>";
				}
				?>
			</select>
			<br />
			<strong>Match:</strong>
			<select id="subcategory_list">
			</select>

		</div>
	</div>
</div>

Open in new window


Second view list_dropdown_view.php
<?php
if(empty($subcategories))
{
    echo "<option value=''>No available subcategory</option>";
}
else
{
	foreach ( $subcategories as $subcategory )
	{
			echo "<option value='{$subcategory["subcategory_id"]}'>{$subcategory["subcategory_name"]}</option>";
	}
}

Open in new window


Finally the javascript:
$(document).ready(function()
{
	var subcategories = [];
	$(document).on('change', 'select#category_list', function()
	{
		var cat_id = $('select#category_list').val(); //Get the id of the tournament selected in the list
		$.ajax(
		{
			type: 'POST',
			url: baseurl+'subcategorylist/list_dropdown', //We are going to make the request to the method "list_dropdown" in the match controller
			data: 'cat_id='+cat_id, //POST parameter to be sent with the tournament id
			success: function(resp) 
			{ //When the request is successfully completed, this function will be executed
			//Activate and fill in the matches list
			 subcategories = resp;
				$('select#subcategory_list').html(resp); //With the ".html()" method we include the html code returned by AJAX into the matches list
			}
		});
	});
	$('select#category_list').trigger('change');
});

Open in new window


Both views are wrapped within header and footer:

header:
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="">
    <meta name="author" content="">
<!--    <link rel="icon" href="../../favicon.ico">-->

    <title>1Stop-HomeShop</title>

    <!-- Bootstrap core CSS -->
    <link href="<?php echo base_url('assets/css/bootstrap.min.css')?>" rel="stylesheet">

    <!-- Just for debugging purposes. Don't actually copy these 2 lines! -->
    <!--[if lt IE 9]><script src="<?php //echo base_url('js/ie8-responsive-file-warning.js')?>"></script><![endif]-->
    <script src="<?php echo base_url('assets/js/ie-emulation-modes-warning.js')?>"></script>

    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!--[if lt IE 9]>
      <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
      <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
    <![endif]-->

    <!-- Custom styles for this template -->
    <link href="<?php echo base_url('assets/css/carousel.css')?>" rel="stylesheet">
    <link href="<?php echo base_url('assets/css/frac_reset.css')?>" rel="stylesheet">
    <link href="<?php echo base_url('assets/css/frac_style.css')?>" rel="stylesheet">
    <link href="<?php echo base_url('assets/css/fractionslider.css')?>" rel="stylesheet">
    <link href="<?php echo base_url('assets/css/app.css')?>" rel="stylesheet">
  </head>
<!-- NAVBAR
================================================== -->
  <body>
    <div class="navbar-wrapper">
      <div class="header-container">

        <nav class="navbar navbar-inverse navbar-static-top" role="navigation">
          <div class="container">
						<img src="<?php echo base_url('assets/images/logomio.png')?>" alt="1Stop-HomeShop" />
              <ul class="nav navbar-nav pull-right">
                <li class="active"><a href="#">Home</a></li>
                <li class="dropdown">
									<a href="#" class="dropdown-toggle" data-toggle="dropdown">Furniture <span class="caret"></span></a>
                  <ul class="dropdown-menu" role="menu">
                    <li><a href="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                  </ul>
								</li>
                <li class="dropdown">
									<a href="#" class="dropdown-toggle" data-toggle="dropdown">Kitchen Ware <span class="caret"></span></a>
                  <ul class="dropdown-menu" role="menu">
                    <li><a href="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                  </ul>
								</li>
                <li class="dropdown">
									<a href="#" class="dropdown-toggle" data-toggle="dropdown">Lightning <span class="caret"></span></a>
                  <ul class="dropdown-menu" role="menu">
                    <li><a href="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                  </ul>
								</li>
                <li class="dropdown">
									<a href="#" class="dropdown-toggle" data-toggle="dropdown">Textiles <span class="caret"></span></a>
                  <ul class="dropdown-menu" role="menu">
                    <li><a href="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                  </ul>
								</li>
                <li><a href="#contact">Contact</a></li>
              </ul>
						<!--{__NAV__}-->
          </div>
        </nav>

      </div>
    </div>

Open in new window


footer:
    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script src="<?php echo base_url('assets/js/bootstrap.min.js')?>"></script>
    <script src="<?php echo base_url('assets/js/docs.min.js')?>"></script>
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src="<?php echo base_url('assets/js/ie10-viewport-bug-workaround.js')?>"></script>
    <script src="<?php echo base_url('assets/js/jquery.fractionslider.min.js')?>"></script>
    <script src="<?php echo base_url('assets/js/fractionslider_init.js')?>"></script>
    <script src="<?php echo base_url('assets/js/knockout.js')?>"></script>
		<script type="text/javascript">
			var baseurl = "<?php print base_url(); ?>";
    <script src="<?php echo base_url('assets/js/test.js')?>"></script>
  </body>
</html>

Open in new window


Finally, the routes.php
$route['default_controller'] = "welcome";
$route['404_override'] = '';
$route['categorylist/(:any)'] = 'categorylist/list_products';
$route['subcategorylist/(:any)'] = 'subcategorylist/list_dropdown';

Open in new window


What's the problem, now? I'd like to use knockout.js and I have two distinc questions about:
1. I have the doubt using knockout.js (or similar javascript framework) within a php framework ibe useless or at lease less useful than using it without a php framework: after all, the above code get the same result, maybe a better result in a whole perspective: more robust, more secure (it's  server side), a bit less elegant, maybe, but concise and clear. So the question is: does it make sense to try to integrate a javascript framework with a php frame work and more specifically knockout.js with CodeIgniter? If the answer is 'No', the second question can just be skipped.

2. The second question is: how to implement knockout.js logic? I tried to modify the list_product.php code this way:
<div class="slider-wrapper">
</div>
<div class="container marketing">
	<!-- Three columns of text below the carousel -->
	<div class="row">
		<div class="col-lg-12">

			<strong>Categories:</strong>
			<select id="category_list">
				<?php
				foreach ( $categories as $category )
				{
					echo "<option value='{$category["category_id"]}'>{$category["category_name"]}</option>";
				}
				?>
			</select>
			<br />
			<strong>Match:</strong>
			<select id="subcategory_list" data-bind="options: subcategories">
		</div>
	</div>
</div>

Open in new window


and the javascript this way:
$(document).ready(function()
{
	var subcategories = [];
	var subcat = {
			subcategories: ko.observableArray([])
	};	

Open in new window


But I can't fighure out how to modify views and I'm just blocked here.

I know this is can be a hard question and I apologize if it's too theoretical or confused: I'm confused me too :-)

As usual, thanks in advance to all.

As a side note: can I win something  for the most long question posted at EE? lol
Avatar of Ray Paseur
Ray Paseur
Flag of United States of America image

Marco: No points for this, because it's not an answer, but we've been colleagues long enough for me to feel OK making a suggestion.  Ditch CodeIgniter and learn about Laravel.  It's an amazing framework.  Fast, powerful, fluent, easy to use, with many of the "convention over configuration" practices of Ruby on Rails, but in a PHP environment.  I've been on a Laravel project for a couple of months and I'm in awe of the framework.

Plus, they teach it online at Harvard University!
https://www.youtube.com/watch?v=XwhZ4xX7Qmc

Give yourself one hour to watch the Harvard video and you'll "get it" and you'll be able to start developing in Laravel.

All the best, and Happy New Year, ~Ray
Avatar of Marco Gasi

ASKER

Hi Ray, How do you do? It's a long time we don't meet here at EE: I confess I'm too busy to stay here mutch time, but usually when I post a comment, later or soon you post a comment you too :-)
I heard about Laravel and I yet read something about from you here at EE. Only a question: I need to install composer or such a software in the server or I just have to use them in my computer and then I can just upload the ite to the server? Working on shared server usually makes difficult install extra-software...
Happy New Year to you too
Marco
Hi Marco / Ray

I've also heard good things about Laravel but couldn't get it working (on ubuntu mind you). But the principles are the same (mvc) but it's php as much as codeigniter except it does require being installed along with its own package manager so you may have trouble with your host (as I found out recently trying to get node.js installed on my host)

Codeigniter is also better documented at this point in time. I found their reference material terrible compared with other frameworks and that makes a huge difference when you run into issues.

There are multiple frameworks out there that all do a good job. You just have to pick one that suits your needs and skill level. Keep it simple!
Hi Rob. Your words are conforting, since I'd just setup a project succeeding in having within CodeIgniter the same things I had in another framework. I'm also installing Laravel in my machine (Windows) and I get in some trouble: overall, I have the impresion, as you confirm, that to get it working you need a your server where you can install and uninstall and configure what you want when you want. Anyway, I just love to learn, so I'll study it.

But to come back to my question, what about the relationship between CodeIgniter and Knockout.js? And what about the code? :-)
I'll open a discussion on it as it would be good to get Ray's input on it given he's got it working.

As for your code:
Firstly, yes you can use knockout with codeigniter and most other mvc frameworks but it's more about why you would. It depends on your project.
It depends on the content you're showing. If your views are mainly static or with minimal dynamic content then you may not need knockout.
On the other hand, if you find that you want an heavily driven ajax page or your view becomes messy with php code with loops displaying data etc, then it's time to use knockout as it separates the logic from the markup. Your markup is cleaner and it's easier to understand what's going on.  I think that's how you got here in the first place right? Because one of your php pages was so messy?
Sure, Rob. It's up to you! ;-) I was terribly fascinated by your knockout example and I felt in love with it. I only thought that with the MVC pattern, logic and look are yet separated, but with a lot of dynamic content, i'd end with a lot od messy code again for sure!
So, the answer to my first question is 'Yes'. Now, can you give me some suggestion about the code above? Don't want you wtire my code, only a little suggestion...
ASKER CERTIFIED SOLUTION
Avatar of Rob
Rob
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I should also add you could do the above without knockout, however you would have to handle the modifying / recreation of the markup when new data (subcategories) are selected. e.g.

$.ajax({
...
success: function (data) {
    $.each(data.categories, function(i, el) { 
        $('#subcategories').append("<option value="+el.id+">"+el.text+"</option");
    });
}
});

Open in new window


Simple example above but hopefully you can see that with knockout, the markup is already in your view, without it, your markup is in your javascript and in my opinion a messy way and confusing way to create your content.  It also marks it harder to apply styles on the fly.  As an example, you can test the way your page looks with subcategories once a category has been selected by simply adding dummy data to the subcategory: ko.observableArray(['subcat1','subcat2', ...]);
Wow. Thank you Rob. Give me the time to digest all (my wife requires my active collaboration to prepare the celebration of 2015 so I'll have to stay away from my beloved pc) and I'll come back to let you know. Thank you vary mutch for your effort in helping me. :-)
Not a problem! And happy New year!
Happy New Year to you too and to Ray and all other experts of this wonderful world (EE, I mean...)!
Digested yet - my wife is busy with other things ;-)

I can't see where you'd use knockout so far
I placed the observable code and the markup with data-bind but I was trying to guess how to make the actual bind: the missing code was
		success: function() {
			model.subcategory(array_of_subcategories);
		}

Open in new window


I'm also unsure why you have two controllers?
I trivially followed the linked tutorial: I have to learn a lot about MVC: only one controller per site, uh? I'll try to get it working later...
Anyway, I think to have understood the point o I can give you the points :-) On to the next question (I'm sure I'll have a lot to do...)
Hppy New Year again
Just to revise my one controller comment as you can obviously have more than one, however they would want to be logically separated if you did. E.g. authentication controller that just manages everything to do with authentication. Your home controller (for want of another name) to provide functions for the content of your site.
So getting categories and sub categories would be in the same controller as they both related to content for your site.
Perfectly clear. Thank you again and again :-)
My pleasure and hi from 2015!  You must be almost in 2015 now eh?
less than four hours and I'll be in: you live in the future!
Hi Rob. Now I'm in 2015 me too.
Idecided to defer my knockout.js training because I'm not intelligent enough to learn both mvc and knockout at the same time realizing a project with a three weeks deadline: I'll be happy if I'll do a good job with CodeIgniter and a bit of my old messy code ;-)
And here a question which has no comments yet: https://www.experts-exchange.com/questions/28589380/CodeIgniter-dynamic-menu-and-routes.html
Cheers
Marco
Good idea to stick with what you know and build one step at a time :)  I've commented in your new question