Welcome Guest, Not a member yet? Register   Sign In
Trying to get jQuery/Ajax to work with CI
#1

[eluser]SSgt Dodger USMC[/eluser]
I have a problem trying to get CodeIgniter and jQuery to produce ajax functionality. I have been coding all day, learning jQuery, and generally getting my butt kicked. Let me break down the situation, and hopefully someone will have the courage to help me.

I have a trouble ticket system that displays many tickets on a page... each ticket is nested inside of a multitude of divs like so:
Code:
<div class="eachticketwrapper" id="ticket-362">
   <div class="actionlog">
        &lt;form action="&lt;?= base_url();?&gt;admin/updateticket/362" method="post" id="362" name="362"&gt;
           <ul class="displayinline">
           <p>Action Log:
           <span class="actionlog-362">
                &lt;?php echo $actionlog; ?&gt;
            </span>
            </p>
   </div> &lt;!--actionlog--&gt;
            <p>
        &lt;textarea name="actionlogbox362" cols="100" rows="2" id="actionlogbox362" style="" &gt;&lt;/textarea>
        </p>
            <div class="finalbuttons">
                &lt;?php echo form_open('admin/updateticket/'.'362'); ?&gt;
            <li>&lt;?php
                            $attrib = "class='updatebutton-362' id='updatebutton-362'";
                            echo form_submit("RapidTask",'Update',$attrib); //setup the button, and set permissions. ?&gt;</li>
                &lt;?php echo form_close(); // close the form. ?&gt;
             </div> &lt;!--finalbuttons--&gt;
</div> &lt;!--eachticketwrapper--&gt;

When run, the $actionlog should resemble something like the following:

Code:
worker - 2009-06-25 18:15:23 - Received and Acknowledges Ticket.
worker - 2009-06-25 18:15:23 - Changed Status to In Progress
worker - 2009-06-25 18:15:46 - Changed Priority to High
worker - 2009-06-25 18:15:46 - Changed Category to Network Connection Problem
worker - 2009-06-25 18:17:16 - did something

And then there is a textarea and an update button following it.

Here is the contents of my supplementary jQuery file:

Code:
$(document).ready(function() {
    $('.updatebutton-362').click(function()
        {
            var id = $(this).attr("id").split("-"); // Split the id value at the hyphen, and grab the ticketnum.
            $('.actionlog-'+id[1]).load('http://localhost/ci/index.php/ajaxtestc/');  // do something...
            return false; // return false so default click behavior is not followed.
        });
});

the ajaxtestc controller looks like this:

Code:
function index()
    {
        $data['actionlog'] = $this->m_tickets->actionLogPull(362);
        $this->load->vars($data);
        $this->load->view('content/ajaxtestform');
    }
And the m_tickets model looks like this:

Code:
function actionLogPull($requestedNum=NULL)
    {
        $this->db->where('ticketnum', $requestedNum); // Grab only the status for the ticket requested $requestednum=NULL
        $requestedTicket = $this->db->get('tickets'); // set the initial ticket info to a variable
        $returnvalue = $requestedTicket->row('actionlog');
        return $returnvalue;
    }


Now... here is what I WANT. I want to click the update button, have it take what ever the worker has typed into the textarea, add it to the end of the existing log in the database, and refresh the actionlog on the screen.

I can't come up with a clear way to do this. Can anyone shed some light on how I could start this process?

Any help is greatly appreciated, and thanks in advance.
#2

[eluser]slowgary[/eluser]
I believe the .load() function loads an entirely new page, whereas you'd probably be better using something like .post(), especially since CI deals mainly with post data. Also, since you're going to be printing multiple 'eachticketwrapper's to the screen, you should tailor your JavaScript so that you only need to call it once, instead of once per wrapper (I think that's what you're doing now by using the class 'updatebutton-362'). I'd probably also put the event on the form submit, not on the button click since a user can submit a form without clicking the submit button (by pressing enter). So first change your classes to just 'updateform' or something, since a class should be reused, and you can still get the ticket number from the unique ID:
Code:
&lt;form action='/admin/updateticket/362' class='updateform' id='updateform-362' method='post'&gt; //without javascript, this form should still work
<div>
     &lt;input type='text' name='blah_blah'/&gt;  //whatever form inputs you need
     &lt;input type='text' name='blah_blah2'/&gt;

     &lt;input type='submit' value='update'/&gt;
</div>
&lt;/form&gt;
Then your JavaScript would look like this:
Code:
$(document).ready(function(){
     $('.updateform').each(function(){
          var current_form = $(this);
          current_form.bind('submit', function(event){
               event.preventDefault();  //prevents the form submission
               $.post(
                    current_form.attr('action'),  //url that the ajax call goes to
                    {
                         myfield1 : current_form.find("input[name='blah_blah']").val(),  //post variables to send with the call
                         otherfield2 : current_form.find("input[name='blah_blah2']").val(),
                         ajax : true,  //the controller can use this to know if it's ajax or not
                    },
                    function(data){  //callback function after ajax success
                      alert(data.apples);  //do something with the return data
                      $('#somediv').html(data.oranges);  //do something with the return data
                    },
                    'json');  //I recommend getting your databack as JSON, allows you to access return data as written above
          });
     };
});

THEN, you'd have something like this in your admin controller:
Code:
function updateticket($ticket_num)
{
     //do something with $ticket_num
     $this->Your_model->do_something($ticket_num);

     if($this->input->post('ajax'))
     {
          //this is how you return data back to in json format
          return json_encode(array('apples' => 'granny_smith', 'oranges' => 'mmmm...'));
     }
     else  //normal, non-ajax form submission code
     {
          //do something
     }
}

I hope this helps, good luck.
#3

[eluser]SSgt Dodger USMC[/eluser]
First off, Thanks slowgary. Your post gave me a ton of ideas to go with.

I played for a few hours, and this is what ended up working for me:

jQuery:
Code:
$('.updatebutton-362').click(function()
    {
        var id = $(this).attr("id").split("-"); // Split the id value at the hyphen, and grab the ticketnum.
        var ticnum = id[1]; // Set the ticket number from the split value.
        var ajax = true; // Set the ajax variable to true. This is to do ajax checking in the controller.
        
        // Check to see if the textarea is empty.
        if($("#actionlogbox"+ticnum).val() == "")
            { // If empty, return nothing.
            return false; // die!
            }
        else
        { // There is something typed in the textarea, and should be placed into the ticket.
            $.post("http://localhost/test/index.php/ajaxtasks/actionLog", // This is the URL of the code you want to run when clicked.
            {
                updateText: $("#actionlogbox"+ticnum).val(), // Set the variable for the typed in text.
                ticketNum: ticnum // set the variable for the ticket number.
            },
            function(data)  // this is what to run if the ajax update is successful.
                {
                    $('#actionlog-'+ticnum).html(data); // update the screens info on the actionlog.
                    $("#actionlogbox"+ticnum).val(''); // empty the textarea.
                });
            return false;  // stop the button from working normally.
        } // End else
    }); // End click function.

the controller:

Code:
function actionLog()
    {
        $updateString = "<br />".$this->input->post('updateText',TRUE); // make the BR and set the passed variable to this field.
        $ticketNum = $this->input->post('ticketNum',TRUE); // Grab the ticketnumber from POST
        $newActionLog = $this->m_tickets->actionLogUpdate($ticketNum,$updateString); // Go update the database!
        echo $newActionLog;
    }

and the model functions:

Code:
function actionLogPull($ticketNum=NULL)
    {
        $this->db->where('ticketnum', $ticketNum); // Grab only the status for the ticket requested $requestednum=NULL
        $requestedticket = $this->db->get('tickets'); // set the initial ticket info to a variable
        return $requestedticket->row('actionlog'); // Send the existing actionlog back to the calling function.
    }

    function actionLogUpdate($ticketNum=NULL,$updateString=NULL)
    {
        $oldLogString = $this->actionLogPull($ticketNum); // grab the current ActionLog from the ticket.
        $newLogString = $oldLogString.$updateString; // create a string to update the ticket with.
        $this->db->where('ticketnum', $ticketNum); // Set update scope to the requested ticket only.
        $this->db->update('tickets', array('actionlog' => $newLogString)); // Add the new string to the database.
        return $newLogString; // Send the new string back to the calling function
    }

If anyone can point out a better way to accomplish this, please let me know. But for now... solved!
#4

[eluser]slowgary[/eluser]
Nice. The only question I have is are there going to be multiple 'updatebutton's per page? I see you're still attaching the event listener to a specific element, so if 'updatebutton-362' is the only one, then you're okay. Otherwise you really should write your JavaScript so that it runs once and works for all forms on the page.
#5

[eluser]slowgary[/eluser]
Another thing I forgot... you're STILL attaching the event to the button's click. This is not a good way to do it. There are several ways to submit a form besides clicking on the submit button. You can simply hit enter on any input element, and even if you don't have inputs in your form, a user might tab to the submit buttons and hit the spacebar, in which case your AJAX would not execute and would seem broken to the user. You should always attach the event listener for forms to the form itself, listening for the 'submit' event. So instead of:
Code:
$('.updatebutton-362').click(function(){
});
It should be something like:
Code:
$('.updateform-362').bind('submit', function(){
});
You shouldn't assume anyone's going to click your button.
#6

[eluser]SSgt Dodger USMC[/eluser]
Hi slowgary!

I tried your suggestion about using the .bind command, but for the life of me I could not get that to work. No matter where I placed the event.preventDefault() the submit function would still execute the form action, and an entire page refresh. Here is the jQuery after my mods:

Code:
$('.updateform').each(function()
    {
        
        var current_form = $(this);
        var id = $(this).attr("id").split("-"); // Split the id value at the hyphen, and grab the ticketnum.
        var ticnum = id[1]; // Set the ticket number from the split value.
        var ajax = true; // Set the ajax variable to true. This is to do ajax checking in the controller.
        current_form.bind('submit', function(event){
            event.preventDefault();  //prevents the form submission

            // Check to see if the textarea is empty.
            if($("#actionlogbox"+ticnum).val() == "")
                { // If empty, return nothing.
                return false; // die!
                }
            else
                { // There is something typed in the textarea, and should be placed into the ticket.
                    $.post("http://localhost/test/index.php/ajaxtasks/actionLog", // This is the URL of the code you want to run when clicked.
                    {
                        updateText: $("#actionlogbox"+ticnum).val(), // Set the variable for the typed in text.
                        ticketNum: ticnum, // set the variable for the ticket number.
                        isajax: ajax // set the method to 'ajax'.
                    },
                    function(data)  // this is what to run if the ajax update is successful.
                        {
                            $('#actionlog-'+ticnum).html(data); // update the screens info on the actionlog.
                            $("#actionlogbox"+ticnum).val(''); // empty the textarea.
                        });
                    return false;  // stop the button from working normally.
                } // End else
            }); // End click function.
     }); // End .each function

Just for clarity sake, my form consists of 4 dropdowns, a textarea (the one we are messing with here) and an update button. When I had the .click function defined earlier, a space-bar-tap on the button would produce the same results as the click. And I was unable to submit with the enter button since it was a text area.

I want this thing to be as best-practices as possible, and I agree that the form submit action is the better-designed option, but this doesn't work for me.

You have been very helpful slowgary, and I thank you again. Any further guidance is greatly appreciated!
#7

[eluser]slowgary[/eluser]
Hmmm... so the above doesn't work? It looks okay. The bind command is just a more generalized way to attach event handlers. These are functionally equivalent:
Code:
.bind('click', function(){
});
.click(function(){
});
And so are these:
Code:
.bind('submit', function(){
});
.submit(function(){
});
But in either case, as long as you pass that anonymous function an identifier for the event, you should be able to call the preventDefault() method on that event like so:
Code:
.submit(function(e){
     e.preventDefault();
});
And preventDefault() is not even a jQuery method, it's native JavaScript so it should work in any modern browser. Do you have this code live anywhere for examination?
#8

[eluser]ggoforth[/eluser]
[quote author="slowgary" date="1246224293"]Hmmm... so the above doesn't work? It looks okay. The bind command is just a more generalized way to attach event handlers. These are functionally equivalent:
Code:
.bind('click', function(){
});
.click(function(){
});
And so are these:
Code:
.bind('submit', function(){
});
.submit(function(){
});
But in either case, as long as you pass that anonymous function an identifier for the event, you should be able to call the preventDefault() method on that event like so:
Code:
.submit(function(e){
     e.preventDefault();
});
And preventDefault() is not even a jQuery method, it's native JavaScript so it should work in any modern browser. Do you have this code live anywhere for examination?[/quote]

I have always used return(false); inside my submit functions, but I prefer to attach them to an id, so something like:

Code:
$('#my_form_id').submit(function(){
   //do something
   return(false);
});
This works for me, but I don't use the other methods much, so I wouldn't know what to tell you about those.

Greg




Theme © iAndrew 2016 - Forum software by © MyBB