Filtering data using ajax observefield - cakephp

I ve tried to implemented filtering data (list base on selected category) using dropdown with observefield and ajax pagination. I use session to remember selected category in order to keep pagination.
Here is my code (Cakephp 1.2)
In view :
echo $form->select('Category.id', $categories, null, array('id' => 'categories'),'Filter by Categories')
echo $ajax->observeField('categories' ,array('url' =>'update_category','update' => 'collectionDiv'));
In Controller:
if(!empty($this->data['Category']['id']))
{
$cat_id=$this->data['Category']['id'];
$filters=array('Collection.category_id' => $cat_id);
$this->set('collections', $this->paginate('Collection', $filters));
$this->Session->write($this->name.'.$cat_id', $category_id);
}
else
{
$cat_id=$this->Session->read($this->name.'.cat_id');
$filters=array('Collection.category_id' => $cat_id);
$this->set('collections', $this->paginate('Collection'));
}
The filter work as I wanted but the problem is when I select empty value('Filter by Category) it still remember last category session so I can't back to the default list (All record list without filter).
I've tried to make some condition but still not success. Is there another way? Please I appreciate your help. thank
hermawan

Perhaps I don't understand the question, but it looks to me like it might be worth changing:
else
{
$cat_id=$this->Session->read($this->name.'.cat_id');
$filters=array('Collection.category_id' => $cat_id);
$this->set('collections', $this->paginate('Collection'));
}
to:
else
{
$this->set('collections', $this->paginate('Collection',array()));
}
In effect your code appears to be doing this anyway. Check what the URL is at the top of the browser window after it has returned. Does it still contain pagination directives from the previous query?
You might want to review http://book.cakephp.org/view/167/AJAX-Pagination and make sure you've 'ticked all the boxes'.

I got it, It work as I hope now. I my self explain the condition and solution.
When I select category from combobox, then it render the page the code is :
If ($this->data['Category']['id']) {
$cat_id=$this->data['Category']['id'];
$this->Session->write($this->name.'.category_id', $category_id);
// I will use this session next when I click page number
$filters=array('Collection.art_type_id' => $category_id);
$this->set('collections', $this->paginate('Collection', $filters));
}else{
//if clik page number, next or whatever, $this->data['Category']['id'] will empty so
// use session to remember the category so the page will display next page or prev
// page with the proper category, But the problem is, when I set category fillter to
// "All Category" it will dislpay last category taked from the session. To prevent it
// I used $this->passedArgs['page']. When I clik the page paginator it will return
// current number page, but it will be empty if we click dropdown.
if (!empty($this->passedArgs['page']) {
$cat_id=$this->Session->read($this->name.'.category_id');
$filters=array('Collection.category_id' => $cat_id);
$this->set('collections', $this->paginate('Collection',$filters));
}else{
$this->set('collections', $this->paginate('Collection'));
}
}
From this case, I think that observefield will not send passedArg as we get from url such from ajax link or html->link. I hope this will usefull to anybody

Related

Showing the new row in react-table on the current page

I have been playing with ReactTable v7 for a while and have encountered the following problem: when the table is sorted and uses paginator sometimes adding (or editing) a row causes it to be outside the current page.
You can see the problem here:
https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/material-UI-kitchen-sink
Sort the table by First Name
Press add
Enter a record with First Name "zzzzz"
The record is added but is currently hidden which confuses users.
Is there a "standard" way to fix the issue? Am I missing something?
In v6 I have done a workaround for it:
React.useEffect(() => {
if (editedElementId && reactTable) {
const { data } = reactTable.props;
if (data && data.length > 0) {
const internal = reactTable.getResolvedState();
let position = -1;
internal.sortedData.forEach((row, i) => {
if (row._original.id === editedElementId) position = i;
});
if (position >= 0) {
const pageNumber = Math.floor(position / pageSize);
setPage(pageNumber);
} else {
alert.info("Element not visible");
}
}
}
}, [editedElementId]);
...
<ReactTable
ref={(r) => {setReactTable(r);}}
...
But maybe there is a bulit-in way to achieve it?
There is not currently a way to only sort the elements which are currently being displayed, no.
React Table v7's useSortBy hook sorts the entirety of the input data array, so sorting by First Name descending (A->Z) naturally places 'ZZZZZZ' at the end of the list, which will be hidden due to pagination. The v7 way of doing it would probably be similar to what you are doing already, using the exposed properties from the useTable hook instead of reactTable.resolvedState() and etc. Another way to do it would be to write your own custom sortBy method and only sort the items [0...n] where n is the number of currently displayed items.
As a side note, since the autoResetSortBy property is true by default in the example you linked, the step of sorting on First Name is irrelevant -- since the function which adds a new user to the list mutates the data array, the sorting method is reset. That function appends the new user to the end of the list, so it will always be on a new page, even if the "Rows per page" option is set to "All". Both issues can be fixed by setting autoResetSortBy to false, and changing the pageSize in addUserHandler.

Redirect user to the second last page in CakePHP

I have a model class Event which has the following actions in question: view and delete.
The deletion can only happen from the view action. Getting to the view action is possible from two places that are:
events/dashboard
and
calendar/view_calendar
the latter takes three parameters: user_id, month and year so that would for example be
calendar/view_calendar/120/5/2014
So depending on which action user got to for example events/view/1400, he has to be redirected accordingly.
The referer does not work as it redirects to the events/view with the id of event which has already been deleted.
Any help or guidance is much appreciated.
Put a second Parameter to the view, like
events/view/1400/0
for dashboard
and
events/view/1400/1
for view_calendar
and pass these params to your delete action, like
events/delete/1400/1
In your delete function you can you use the usual redirect:
if($secondParam == 1) {
$this->redirect(array('action' => 'view_calendar'));
} else {
if($secondParam == 0) {
$this->redirect(array('action' => 'dashboard'));
}
}
You can save somewhere the information about the referrer when you land in events/view and the pass it to events/delete.
You can use a named parameter or a query string so your link to the delete action becomes appear something like
events/delete/1400/referrer:dashboard
or
events/delete/1400/referrer:view_calendar
or
events/delete/1400/?referrer=dashboard

Getting current page in CakePHP pagination

On one of my webpage, I show a list of customer details with default pagination [Paginator helper]. Each customer row has a corresponding 'edit' button which when clicked shows a DIV populated with that customer information. [This DIV has textboxes etc. where I can change customer info] After re-entering/modifying customer details, when I click on "Save" button, 2 ajax calls are made..
First one to save the edited record
The second one Updates the customer Listing on the same page with modified information
My problem : If I am on page 3 and edit one customer record, the 2nd ajax call refreshes the list but does not go to the page 3. It starts from page 1.
Please help.
You can get your current page here:
<?php echo $this->Paginator->counter(array(
'format' => ('{:page}')
)); ?>
This is part of the solution. :)
You can get your current page here:
$this->request->params['named']['page']
Did you try to use the link from Paginator? It maintains the page # to the controller.
Something like
// same parameters as $this->Html->link
<?php echo $this->Paginator->link( ... ) ?>
Check also the reference documentation for the Paginator Helper
I use jquery / javascript to store the page on a cookie and then read it from the controller.
// -------------------------------------------------------------------------
// on action click remember current page
$('td.actions').click( function( event )
{
var page = $('li.active a').html();
var name = window.location.href.split('-').join('_').split('/').pop();
$.cookie.raw = true;
$.cookie( 'CakeCookie[' + name + '_page]', page, { expires: 365, path: '/' } );
});

CakePHP user session not updating but database yes

I'm developing with cakePhP and I have the following problem:
When a user logs in with his name and password to the account system that I've created, he can save items (images) as favorites. This is saved in a text field into the database. What is saved is the image ID.
The saving process works perfectly, the user clicks on the images and they're added to that field (it actually saves all the IDs as a text array that I process later).
The problem comes when removing images. When the user does it (I'll post the code below), the images is removed correctly from the database (I go to PHP MyAdmin and I see it). This means that the array that holds the favorite images IDs is updated instantly. However, when I reload that array from the website, it hasn't been updated. It's like it's stored in the caché or something. Then, if the user logs out and logs in again, then he can see the correct one. The thing is that I have other things in my website that work in a similar way and they all get updated instantly, so I can't see why this doesn't.
This is the code that I use to remove the ID from the database:
function remove_favorite($pictureID) {
$this->User->id = $this->Auth->User('id'); //We get the ID of the current user
$favoritesArray = $this->User->deleteFavoritePicture($this->User->id, $pictureID); //This function retrieves the array (string) of pictures from the user's table, and deletes all the images with the ID passed as parameter, returning the updated array (string)
$fields = array('images_favorites' => $favoritesArray, 'modified' => true); //We indicate the field that we're going to update in the users table
//We save the new string that doesn't contain the deleted image anymore
if($this->User->save($fields, false, array('images_favorites'))) {
$this->Session->setFlash(__('The image has been removed from your favorites', true));
} else {
$this->Session->setFlash(__('Error removing image from favorites, please try again', true));
}
$this->redirect(array('action' => 'manage_favorites',$this->User->id));
}
This is how the deleteFavoritePicture function looks like:
function deleteFavoritePicture($userID, $pictureID) {
$userInfo = $this->find("id = $userID");
$favoritePicturesString = $userInfo['User']['images_favorites'];
$favoritePicturesArray = explode(",", $favoritePicturesString); //Array
$i = 0;
while ($i < count($favoritePicturesArray)) {
//We remove from the array the images which ID is the one we receive to delete
if ($favoritePicturesArray[$i] == $pictureID) unset($favoritePicturesArray[$i]);
$i++;
}
$favoritePicturesString = implode(",", $favoritePicturesArray); //String
return ($favoritePicturesString);
}
That's it. Does anyone now what can be going on? Thanks so much in advance for any clue!
EDIT
Ok, I think I found something that may give a clue of what's going on here:
This is the code for the manage_favorites action:
function manage_favorites($id) {
//$user = $this->User->find("id = $id");
$user = $this->Auth->user();
$this->set('user', $user);
}
That is the action that is called for the page when a user wants to modify his favorites. The same action is called once the user removes a favorite. Here's the thing:
If I use the $id parameter in the manage_favorites function and the $user = $this->User->find("id = $id"); line (the one quoted now), then the problem does not exist! This is how I used to have it. HOWEVER, I had to change it because it was a big security flaw, since the user id ($id) was a visible parameter who anyone could change, and then access other users accounts. What I did was changing the way I obtain the user array of favorite images, using the following line: $user = $this->Auth->user();. This is how I have it now (well, and also without the $id parameter in the function header), so the user information (including the favorites array) comes from the Auth component, instead directly from the database.
So, the problem is clear: when the user deletes a favorite, it's doing it on the array in the database. WHen I show the result of that operation, the array I'm retrieving is not the one in the DB, it's the one in the session. That's why it's not showing the changes.
How can I avoid this without using a non-secure method like the one I had before?
When you save, the array passed to the save method of the model should look like this:
[User] => array(
[field] => value,
[field2] => value2,
...
)
In your example, you clearly haven't added the [User] key.
Also, is your modified field actually the default Cake modified field? That is, the DATETIME field which changes to the current time when the row is updated?
Lastly, maybe you have debugging set to 2 in config.php. try changing this to 0 (as in production) and see if caching persists.
Hope some of the points I have mentioned above will solve your problem. Please let me know!
There could be two things wrong with this.
What does your deleteFavoritePicture method look like? There could be something being done wrong there.
You're passing false as the second parameter to the User::save method, which means that you don't want to validate. Unless there is a SQL error, then this will return true even if it doesn't validate properly, I believe. Try changing this false to true and see if your results differ.

ExtJS: Added grid rows wont de-highlight

When adding a rows to a grid, and then clicking on it, it gets selected (and highlighted). Then, clicking elsewhere but the new row remains highlighted (so now there are to highlighted rows).
Please, does anyone know what the problem could be? How to make it behave normally, i.e. clicking a row deselects (de-highlights) the other one?
After I reload the page (so the new row is not new anymore), everything works as expected.
Edit: Here's the code for adding rows:
var rec = new store.recordType({
test: 'test'
});
store.add(rec);
Edit 2: The problem seems to be listful: true. If false, it works! But I need it to be true so I'm looking at this further... It looks like as if the IDs went somehow wrong... If the ID would change (I first create the record and then the server returns proper ID, that would also confuse the row selector, no?)
(Note, correct as ExtJS 3.3.1)
First of all, this is my quick and dirty hack. Coincidentally I have my CheckboxSelectionModel extended in my system:-
Kore.ux.grid.CheckboxSelectionModel = Ext.extend(Ext.grid.CheckboxSelectionModel, {
clearSelections : function(fast){
if(this.isLocked()){
return;
}
if(fast !== true){
var ds = this.grid.store,
s = this.selections;
s.each(function(r){
//Hack, ds.indexOfId(r.id) is not correct.
//Inherited problem from Store.reader.realize function
this.deselectRow(ds.indexOf(r));
//this.deselectRow(ds.indexOfId(r.id));
}, this);
s.clear();
}else{
this.selections.clear();
}
this.last = false;
}
});
And this is the place where the clearSelections fails. They try to deselect rows by using ds.indexOfId(r.id) and it will returns -1 because we do not have the index defined remapped.
And this is why we can't find the id:-
http://imageshack.us/photo/my-images/864/ssstore.gif/
Note that the first item in the image is not properly "remapped". This is because we have a problem in the "reMap" function in our Ext.data.Store, read as follow:-
// remap record ids in MixedCollection after records have been realized. #see Store#onCreateRecords, #see DataReader#realize
reMap : function(record) {
if (Ext.isArray(record)) {
for (var i = 0, len = record.length; i < len; i++) {
this.reMap(record[i]);
}
} else {
delete this.data.map[record._phid];
this.data.map[record.id] = record;
var index = this.data.keys.indexOf(record._phid);
this.data.keys.splice(index, 1, record.id);
delete record._phid;
}
}
Apparently, this method fails to get fired (or buggy). Traced further up, this method is called by Ext.data.Store.onCreateRecords
....
this.reader.realize(rs, data);
this.reMap(rs);
....
It does look fine on the first look, but when I trace rs and data, these data magically set to undefined after this.reader.realize function, and hence reMap could not map the phantom record back to the normal record.
I don't know what is wrong with this function, and I don't know how should I overwrite this function in my JsonReader. If any of you happen to be free, do help us trace up further for the culprit that causes this problem
Cheers
Lionel
Looks like to have multi select enabled for you grid. You can configure the selection model of the grid by using the Ext.grid.RowSelectionModel.
Set your selection model to single select by configuring the sm (selection model) in grid panel as show below:
sm: new Ext.grid.RowSelectionModel({singleSelect:true})
Update:
Try reloading the grid using the load method or loadData method of the grid's store. Are you updating the grid on the client side? then maybe you can use loadData method. If you are using to get data from remote.. you can use load method. I use load method to update my grid with new records (after some user actions like add,refresh etc). Or you could simply reload as follows:
grid.getStore().reload();

Resources