Solved

CodeIgniter, Ajax and Knockout.js

Posted on 2014-12-30
20
720 Views
Last Modified: 2015-01-02
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
0
Comment
Question by:Marco Gasi
  • 10
  • 9
20 Comments
 
LVL 108

Expert Comment

by:Ray Paseur
ID: 40524822
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
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525109
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
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525128
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!
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525138
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? :-)
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525146
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.
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525164
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?
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525175
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...
0
 
LVL 42

Accepted Solution

by:
Rob Jurd, EE MVE earned 500 total points
ID: 40525210
So far I have a couple of questions because I can't see where you'd use knockout so far.  I'm also unsure why you have two controllers?  My understanding is you should only have the one controller for each site you do.

There are obviously a few ways to do this.  You have a list of categories and on selection, you want to load the relevant subcategories.

In an MVC framework (codeigniter), I would create the View for showing the products with a generic approach.  In other words, this view has the bootstrap layout with areas for loading content dynamically.  One drawback from this is that your products may not be indexed by search engines so you'd need to give some more thought to what static html you include along with other SEO principles, e.g. sitemap etc.
Traditionally, with a pure PHP page, you would select a category, page reloads with the relevant subcategories.  With every change in the category, the page refreshes to show the relevant subcategories.  Now as you know, AJAX allows this to happen with the same PHP traditional model but without reloading the page.  However, the coding can become messy while you parse the ajax response and build your content dynamically.

In just looking at your categories and subcategories markup in the corresponding View:

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

Open in new window


js
$(function() {
	var model = {
		subcategories: ko.observableArray([])
	}
	ko.applyBindings(model);
	
	$('#category_list').on('change', function() {
		$.ajax({
		....
		success: function() {
			model.subcategory(array_of_subcategories);
		}
	});
	
});

Open in new window

0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525224
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', ...]);
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525289
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. :-)
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525293
Not a problem! And happy New year!
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525301
Happy New Year to you too and to Ray and all other experts of this wonderful world (EE, I mean...)!
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525328
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...)
0
 
LVL 30

Author Closing Comment

by:Marco Gasi
ID: 40525331
Hppy New Year again
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525871
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.
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525873
Perfectly clear. Thank you again and again :-)
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40525905
My pleasure and hi from 2015!  You must be almost in 2015 now eh?
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40525954
less than four hours and I'll be in: you live in the future!
0
 
LVL 30

Author Comment

by:Marco Gasi
ID: 40527354
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: http://www.experts-exchange.com/Programming/Languages/Scripting/PHP/Q_28589380.html
Cheers
Marco
0
 
LVL 42

Expert Comment

by:Rob Jurd, EE MVE
ID: 40527397
Good idea to stick with what you know and build one step at a time :)  I've commented in your new question
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

There are a couple ways to attach a JavaScript function to dynamically created elements. You can make a new script for each element as it’s created or you can use delegation. Delegation allows a single script that is added at page creation to mat…
PROBLEM: The other day I was working on adding an ajax request to a webpage that already had a dialog box on the page.  The dialog box was using relative positioning to be positioned next to a form field I had on the page.  Everything was working…
The viewer will learn how to dynamically set the form action using jQuery.
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now