I have a PHP page that uses jQuery to let a user update a particular item without needing to refresh the page. It is an availability update where they can change their availability for an event to Yes, No, or Maybe. Each time they click on the link the appropriate jQuery function is called to send data to a separate PHP file (update_avail.php) and the appropriate data is returned.
Yes
Then when clicked the params are sent to a PHP file which returns back:
No
Then, if clicked again the PHP will return:
Maybe
It all works fine and I'm loving it.
BUT--
I also have a total count at the bottom of the page that is PHP code to count the total number of users that have selected Yes as their availability by simply using:
<?php count($event1_accepted); ?>
How can I make it so that if a user changes their availability it will also update the count without needing to refresh the page?
My thoughts so far are:
$var = 1;
while ($var > 0) {
count($day1_accepted);
$var = 0;
exit;
}
Then add a line to my 'update_avail.php' (which gets sent data from the jQuery function) to make $var = 1
Any help would be great. I would like to stress that my main strength is PHP, not jQuery, so a PHP solution would be preferred, but if necessary I can tackle some simple jQuery.
Thanks!
In the response from update_avail.php return a JSON object with both your replacement html and your new counter value.
Or to keep it simple, if they click "yes" incriment the counter, if they click No or maybe and their previous action wasn't No or Maybe decrease the counter.
Assuming your users are logged into the system I'd recommend having a status field in the user table, perhaps as an enum with "offline", "available", "busy", "unavailable" or something similar and use the query the number of available users whilst updating the users status.
If you were to do this you'd need to include in extend your methods containing session)start() and session_destroy() to change the availability of the user to available / offline respectively
The best way is the one suggested by Scuzzy with some improvements.
In your php, get the count from the database and return a JSON object like:
{ count: 123, html: 'Yes' }
In your page, in the ajax response you get the values and update the elements:
...
success: function(data) {
$("#linkPlaceholder").html(data.html);
$("#countPlaceholder").html(data.count);
}
...
Related
So i have an object thats draggable to multiple columns and I need to make it so that when it is dropped a variable in that object gets updated depending on what column that is, as another object manipulates that variable for display. i have been unable to get the (ondrop) event to work from the tag, and the dragula event listener's value doesnt pass me any information that would allow me to get the object. Is there anyway to force the event listener to pass the object instead of the html tags? or is there some method im missing?
I think you can update data on drop. I've done it like this:
var drake = dragula({...});
function updateMyObject(elementId, listId) {
// update the object here, for example:
if (listId === 'firstList') {
// use the element id to find the item in your object and update it
myDataObject.filter(function(x) {
return x.id === elementId;
})[0].propertyToUpdate = listId;
}
}
drake.on('drop', function(el, target, source, sibling) {
var elementId = el.id;
updateMyObject(el.id, target.id);
});
This pen may help. I'm mixing Dragula with Angular.js for the data modelling. The event needs to update the data model on drop.
http://codepen.io/chris22smith/pen/37459a002cbe6b6cd37aa5e927698fba
The only solution I have found (short from using a different drag'n'drop module) is to save the order when the user closes the page or goes to something else. Or since the drop event is catch-able, but does not seem to be able to give a class object, you can still detect when there was a change and have it update everything, however that is not the best work around as it is far more taxing on system resources than updating one variable.
Pass the order # as an attribute in your element so it's accessible in your drake.on function. With the oder# and the ID you should be able to figure out what to do with it and make the right updates on the backend.
I've just designed my first form in HTML and a PHP page to display the results. In the form the user inputs some codes in response to some questions, a bit like a multiple choice, so for example, these are "ABC". The PHP page displays the code to the user as a link, which when clicked will go to a bookmark (a link within the same page) with the ID #ABC. This was achieved with simple manipulation of the PHP variable as follows:
<?php
$code = "ABC"
$part1 = '<a href="mywebpage.php#';
$part2 = '">Go to this code</a>';
$string = $part1.$code.$part2;
echo $string;
?>
(i.e. Link in the page says "go to this code" and when clicked will go to section with bookmark ABC)
This all works fine, but I simply need to know if there is a way of error trapping so that if a bookmark does not exist for the code entered, a message can be displayed to the user instead? Can this be done using the PHP variable, or do I need to use JavaScript? One work around may be to search the web page for the ID "#ABC'. Is it possible to do this? Another option would be to store an array of valid codes on the server then query this before setting the bookmark, but I want to keep it as simple as possible. Any help appreciated, thanks.
What you call a "bookmark" we call a hash. And when you say "go to a bookmark" you mean a hash change. Hash changes do not make an additional request to the server, it is all handled on the client-side, therefore this must be done with JavaScript and not PHP.
So let's just do some simple JavaScript on hash change window.onhashchange that will search for an element with that ID and if it's not found alert something.
window.onhashchange = function(){
if(!document.getElementById(location.hash){
alert("not found");
}
}
I have this while loop, that basically loops through a lot of records in a database, and inserts the data in another:
$q = $con1->query($users1) or die(print_r($con2->errorInfo(),1));
while($row = $q->fetch(PDO::FETCH_ASSOC)){
$q = $con2->prepare($users2);
$q->execute(array($row['id'], $row['username'])) or die(print_r($con2-errorInfo(),1));
}
(The script has been shortened for easy reading - the correct one has a much longer array)
I would like to do this more graphical, and show a progress bar on how far it has went, instead of just seeing a page loading for a few minutes (there are ~20.000 rows in this one - I have tables with much more data)
I get that you could get the total number from the old database, and I could also easily put the current number into a variable like this:
$q = $con1->query($users1) or die(print_r($con2->errorInfo(),1));
$i = 0;
while($row = $q->fetch(PDO::FETCH_ASSOC)){
$q = $con2->prepare($users2);
$q->execute(array($row['id'], $row['username'])) or die(print_r($con2-errorInfo(),1));
$i++;
}
But now I need to actually fetch $i and display it - or something like it.
How is this "easily" done?
The code for the progress bar can either be in the same document as the while loop, or in another if easier.
You can do a "master" file that does an ajax to this first file to run a single query. You could get all the entry id's in this master file, and then pass it as a parameter to the second file that does a single query. Store these ids in a javascript array.
Create a function that does this, and when the first ajax is done, move to the second element of the id array, and do another ajax with a second parameter. That's how magento imports are done by the way :)
If you need further explanations, let me know, I tried my best to explain, but may have not been perfectly clear.
// you generate this javascript array using php.
// let's say you have all the ids that have to be processed in $Ids php array.
Ids = [<?php echo implode(',', $Ids); ?>];
function doAjax(i) {
$.ajax({ // using jquery for simplicity
'url': "ajax.php?id=" + Ids[i],
}).done(function(){
if ( i >= 0 ) {
// at the point you know you're at ((Ids.length-i)/(Ids.length) * 100) percent of the script
// so you can do something like this:
// $('.progressbar').css('width', ((Ids.length-i)/(Ids.length) * 100) + '%');
doAjax(i-1);
}
});
}
doAjax(Ids.length); // starting from the last entry
So, just to explain what this does. It starts by declaring a global javascript array that has all the ids that will need to be changed.
Then I declare a recursive ajax function, this way we can make sure that only one ajax runs at any single time (so the server doesn't blow up), and we can have a fairly accurate progress. This ajax function does the following:
Sends a request to ajax.php?id=xxx - where xxx is one of the ids in the javascript array.
In the file, we get the id ($_GET['id']), you take it from the old database, and insert it in the new one. This is only for one entry.
when the ajax is done, it goes to the done() function. Since we start the doAjax() function with the last element, we do the next iteration doAjax(i-1). Since we're going backwards in the array, we check if the key is positive. If it's not, the script will stop.
That's about it.
You can't. The php is first interpreted by the server and then send to the user as HTML-Code.
The only possibility would be creating a html-page and call the php-script with AJAX.
Sorry if I'm duplicating threads here, but I wasn't able to find an answer to this anywhere else on StackOverflow.
Basically what I'm trying to do is make a list in which variables entered in a form by a user can be kept. At the moment, I have the code which makes this possible, and functional, however the variables entered in the form only appear on the list after the user hits submit... As soon as I refresh the page or go to the page from somewhere else, the variables disappear. Is there any way I can stop this from happening?
Edit: here are the codes:
//Page 1
<?php
session_start();
$entries = array(
0 => $_POST['signup_username'],
1 => $_POST['signup_email'],
2 => $_POST['signup_city']);
$entries_unique = array_unique($entries);
$entries_unique_values = array_values($entries_unique);
echo "<a href='Page 2'>Link</a>";
$_SESSION['entries_unique_values'] = $entries_unique_values;
?>
//Page2
<?php
session_start();
$entries_unique_values = $_SESSION['entries_unique_values'];
foreach($entries_unique_values as $key => $value) {
$ValueReplace = $value;
echo "<br /><a href='http://example.com/members/?s=$ValueReplace'>" . $value . "</a><br/>";
}
?>
Your question is really quite vague. the answer depends on how much data you have to store, and fopr how long you need it to exsist.
By variable I assume you mean data the user has entered and that you want to put into a variable.
I also presume that the list of variables is created by php when the form is submitted.
Php will only create the variable list when the form is submitted as php is done entirely on the server, therefore you will not have or see the variables until the form is submitted.
if you wanted to be able to see the list as it is being created you could use javascript then once you have you php variables the javascript list isn't necesary.
each time you request a php page wheather it is the same one or not the server generates a totally new page, meaning all unhardcoded variables from previous pages will be lost unless you continually post the variables around the pages the server will have no memory of them.
You have a few viable options.
) keep passing the user created variables in POST or GET requests so each page has the necesary info to work with. Depending on the situation it might or might not be a good idea. If the data only needs to exsits for one or two pages then it is ok, but bad if you need the data to be accessable from any page on your web.
2.) start a session and store the variables in a session. Good if the data only needs to be around while the user is connected to the site. but will be lost if user close window or after a time.
3.) place a cookie. not a good idea but ok for simple data.
4.) create a mysql database and drop the variable info in there. great for permanent data. this is how i always complex user data.
just a few ideas for you to look into as it is difficult to see what you really mean. good luck.
use PHP session or store variable values in Cookies via JS or using PHP. It would be nice if you show your working codes :)
Your idea is fine, however you just need to add a little condition to your Page 1 that only set your SESSION values when POST is made, that way it will keep the values even if you refresh. Otherwise when you visit the page without a POST those values will be overwritten by blank values, which is what you are seeing now. You can modify it like
<?php
session_start();
if(isset($_POST["signup_username"]))
{
$entries = array(
0 => $_POST['signup_username'],
1 => $_POST['signup_email'],
2 => $_POST['signup_city']);
$entries_unique = array_unique($entries);
$entries_unique_values = array_values($entries_unique);
$_SESSION['entries_unique_values'] = $entries_unique_values;
}
echo "<a href='http://localhost/Calculator/form2.1.php'>Link</a>";
?>
You could use JavaScript and HTML5 local storage.
I currently have a page defined which displays some data in rows. At the end of each row, there is a view which shows a total which is extracted from mysql.
$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->setPoints($row['points_left'])
->setBacklog($row['backlog_ref'])
->setID($row['id'].'-points-left');
The view is defined with a template like this
<!-- points left -->
<div class='target points_left'>
<div class='sticky green'>
<div class='story'><?$backlog?></div>
<div id='<?$name?>' class='big_points big_point_margin'><?$pointsleft?></div>
</div>
</div>
<!-- end 0000-points-left -->
The data to populate the view is selected using a sql in the page which is looped through and the /lib/view/pointsleft.php code has set methods which are passed parameters from the page and update the fields in the template.
class View_PointsLeft extends View_Sticky {
function init(){
parent::init();
}
function setPoints($points){
$this->template->set('pointsleft',$points);
return $this;
}
function setBacklog($backlog){
$this->template->set('backlog',$backlog);
return $this;
}
function defaultTemplate(){
return array('view/scrumwall/pointsleft');
}
}
I want to update the database when something is changed on the page and also update the total view (to decrement the counter).
First, I'm wondering if i have approached this the wrong way (should each view should be self contained) - should i just pass the id field to the view, attach the relevant model to the view inside lib/view/pointsleft.php and call the set fields using the model values ?
Secondly, If i change it that way, does it then make it easier to update the view with a particular id when the database value is changed using ajax and if so , how do i do this ?
Thirdly - if i want to also trigger an update into the database based on an action on the client side javascript, where would i put this code e.g. in the non atk4 version of my code, i had a script called using $.post("update.php") which would update mysql. Where would i put such a script in ATK4 ?
Thanks in advance.
Update after answer from Romans
Man, ATK4 rocks ! - it does more than i expected and i was busy creating functions inside the view to populate each field name, so now having redone it using addModel,
the call from the page looks like this
$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->loadData($row['id']);
the templates/view looks like this
<div id='<?$name?>' class='target points_left'>
<div class='sticky green'>
<div class='story'><?$backlog_ref?></div>
<div class='big_points big_point_margin'><?$points_left?></div>
</div>
</div>
and the lib/view code looks like this
<?php
class View_PointsLeft extends View_Sticky {
function loadData($id){
$this->setModel('Story')->loadData($id);
}
function init(){
parent::init();
}
function defaultTemplate(){
return array('view/scrumwall/pointsleft');
}
}
Update after code example from Romans
After following the code example Romans provided, i now add the URL call using jquery selectors at the bottom of my page code and do some jiggery pokery to get the task and status from the id fields (not sure about using HTML5 only stufff using data-id so just set the normal id and extract from that). Previously the drop code was in my own univ.js script but i dont have access to the php variables from there so i move it into the page
$p->js(true)->_selector('.movable')->draggable();
$p->js(true)->_selector('.target')->droppable(array(
'drop'=>$this->js(null,'function(event,ui){'.
' target=$(this).attr("id").split("-");'.
' zone=target[2];'.
' sticky=$(ui.draggable).attr("id").split("-");'.
' task=sticky[1];'.
' switch (zone) {'.
' case "verify": newStatus="V";'.
' break;'.
' case "in": newStatus="P";'.
' break;'.
' case "to": newStatus="I";'.
' break;'.
' case "done": newStatus="D";'.
' break;'.
'}
$.univ().ajaxec({ 0:"'.$this->api->getDestinationURL().'",'.
'task: task, status: newStatus }); } ')
));
and i have a if block which looks like this in the page. I add Model_Task and load the values based on the GET parameter so i then also have more information including the story it relates to so i can also update the points if the status is now done.
if($_GET['task'] && $_GET['status'])
{
$new_status=$_GET['status'];
$task_id=$_GET['task'];
$t=$p->add('Model_Task')->loadData($task_id);
$old_status=$t->get('status');
$task_points=$t->get('points');
if ($new_status<>$old_status & ($new_status=='D' | $old_status=='D'))
{
$s=$p->add('Model_Story')->loadData($t->get('story_id'));
if ($old_status='D')
{
$s->set('points_left',$s->get('points_left')+$task_points);
} else {
$s->set('points_left',$s->get('points_left')-$task_points);
}
$s->update();
$story=$t->get('story_id');
}
$t->set('status',$new_status);
$t->update();
}
i can then calculate the new number of points and update the story with points left and update the task with the new_status by setting the model values and using update().
If i now move one of the draggables, it works but opens a new window showing again the whole page and reporting
Error in AJAXec response: SyntaxError: syntax error
I think opening the extra window is because of the error but the error is something to do with the response having all the html for the whole page. I dont actually want any reload from the ajax call unless the status is a particular one.
Also the last thing i need to do is only reload one view on the page for the particular story that was updated.
I've tried by creating an array and adding the short variables to it like this when the page is first loaded
$this->pl_arr[$row['id']]=$r->add('View_PointsLeft', 'pleft', 'pointsleft')
->loadData($row['id']);
and then in the if block while processing the GET, to recall it
$pleft=$this->pl_arr[$story];
$pleft->js()->reload()->execute();
but it fails with an error
Error in AJAXec response: SyntaxError: missing ; before statement
Fatal error: Call to a member function js() on a non-object in C:\wamp\www\paperless\page\scrumwall.php on line 247
Final update
The last error is caused because i didnt use for the id in the outer div of the whole view i wanted to update. Once i changed this it is no longer null.
So the first time the page is loaded, i store all the view names in an associative array in a loop as i put them on the page
$st = $p->add('Model_Story');
$result = $st->getRows();
foreach ($result as $row) {
if (is_array($row)) {
$r=$p->add('View_Scrumwall_StoryRow')
->setWorkspace('ws-'.$row['id']);
... other code here ...
$points_left[$row['id']]=$r->add('View_PointsLeft', null, 'pointsleft')
->loadData($row['id']);
}
and then have the if GET block like this
if($_GET['task'] && $_GET['status'])
{
$new_status=$_GET['status'];
$task_id=$_GET['task'];
$t=$p->add('Model_Task')->loadData($task_id);
$old_status=$t->get('status');
$task_points=$t->get('points');
if ($new_status<>$old_status && ($new_status=='D' || $old_status=='D'))
{
$s=$p->add('Model_Story')->loadData($t->get('story_id'));
if ($new_status=='D')
{
$s->set('points_left',$s->get('points_left')-$task_points);
} else {
$s->set('points_left',$s->get('points_left')+$task_points);
}
$s->update();
$story=$t->get('story_id');
//reload the points left sticky note for the story of the task
$js[]=$points_left[$story]->js()->reload();
}
$t->set('status',$new_status);
$t->update();
$js[]=$this->js()->reload();
$this->js(null,$js)->execute();
}
Note that if I only want to update one view on the page, i can just call that chaing that object with reload and execute e.g.
$pl->js()->reload()->execute
but if i want to update several views on the page, i need to put them in an array (here called js[]) and then call execute like this - you can also see an example of this in Roman's codepad example.
$js[]=$points_left[$story]->js()->reload();
$js[]=$this->js()->reload();
$this->js(null,$js)->execute();
Problem solved with ATK4 :)
Ok, for a cleaner answer, I've put together a sample:
http://codepad.agiletoolkit.org/dragaction.html
Probably example here answers the question better.
In your case, since you are working with models, it should be easier to set this up. For the performance I decided to use 2 Listers, but in theory you can also have each person and task as a view.
I'm storing associations in session (through memorize) in your case you would store them in database.
Your structure seem to be OK. If you use setModel() on it which would have "pointsleft" and "backlog" fields, those would be automatically filled in.
I don't see how setID is defined though, but you could extend setModel, call parent and then execute that too.
Another thing I noticed, is in your template the most top-level div should have id=''. This gives your view unique selector which js() uses by default.
The .post function you are looking for is univ()->ajaxec(). It sends data to the server, receives javascript and executes it, hence the name. It behaves similarly to the form.
$mybutton->js('click')->ajaxec($this->getDestinationURL(null,array('a'=>'b'));
if($_GET['a']){
// update database
$r->getElement('plfat')->js()->reload()->execute();
}
Usually to make your code universal, you can drop this above code inside the view, but instead of 'a' you should better use the name of the object, like this. This eliminates the need for a separate page handling update:
$this->mybutton->js('click')->ajaxec($this->getDestinationURL(null,
array($this->name=>'reload'));
if($_GET[$this->name]){
// update database
$this->js()->reload()->execute();
}
Update
To clarify the sequence of how it's executed:
The page is rendered into HTML sent to your browser.
Along with the page Javascript chains are sent. All of them which define 1st argument to js, such as js(true), js('click'). in my code i have js('click') so it's sent to browser.
User performs the action such as clicking on a button. This triggers ajaxec() function
ajaxec function performs AJAX request to the page with the arguments you specify there.
PHP again is executed, but this time it goes inside if() branch. A js() without argument is created and ->execute() sends javascript to the browser.
browser receives output of the js()...->execute() and evaluates it. In our case it contains reload() for some other element.
atk4_loader widget is used to perform reload of other part of the page it sends AJAX request to server
PHP is executed with cut_object argument. It re-initializes original page, but renders only one object selectively. Output for that object is sent back to the frontend.
PHP also re-generates JS chains like in #2 but only relevant to that object
Frontend's atk4_loader receives the code, replaces HTML of the element and re-evaluates the javascript.
back to #3
It sounds like a lot of actions. In reality that's 2 requests per click and you can eliminate one if you do reload right away. Note that you can also pass arguments to reload() which you can then fetch from "get". I don't fully understand what triggers the action in your original script, perhaps we can find this out in https://chat.stackoverflow.com/rooms/2966/agile-toolkit-atk4 ?