I am using Yii2 with Pjax for index/gridview listing. using pjax pagination , search all working fine without postback to server.
My problem starts now,
suppose i am on page number 2, i have clicked on edit record of that 2nd page list, i reach to update view, i have done changes and saved, now i am redirected to view , now i clicked on index link from breadcrumbs.
i want to reach to page number 2 of index rather then 1st page.
Traditional process for this is get refereeing page params an append that in breadcrumbs.
But is there any simple approach to this problem where i can write few lines of code and its applied to every where in backend?
Thanks for reading.
For remembering grid filter, pages i use yii2-grid-view-state
If you need to store page only, isn't it quite easy to pass page param into your view url (<model/view>) like <model>/view?id=<id>&page=<page>?
in your index.php view, edit your ActionColumn as follow:
[
'class' => 'yii\grid\ActionColumn',
'urlCreator' => function ($action, $model, $key, $index) {
return \yii\helpers\Url::to([$action, 'id' => $model->id, 'page' => Yii::$app->request->getQueryParam('page', null)]);
},
],
As you can see, I'm getting page param from request url and pass it to models' action buttons (to all buttons, but in your question it would be enough for view button of course)
And when you click to view model, in our Controller we need to get that page value and pass it to our view.php view (in order to place it in breadcrumbs).
Our ModelController:
public function actionView($id, $page = null)
{
return $this->render('view', [
'model' => $this->findModel($id),
'page' => $page,
]);
}
And finally view.php view will get the page value, and populate the index url (if not null):
/* #var $page int */
$this->title = $model->name;
$this->params['breadcrumbs'][] = ['label' => 'Index', 'url' => ['index', 'page' => $page]];
So when you press the Index breadcrumb, it will open the page, where you entered from.
Some advantages againts session implementation (#user1764431 solution):
Each of your tab can return to it's own last page
Simple and stupid solution
Some disadvantages:
If you need to store some filter params, url could stretch very long
Just add following Code in every controller of actionIndex() rest all things will take care
$searchModel = new CentervideosSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
/*Code insertion block begin*/
$params = Yii::$app->request->queryParams;
if (count($params) <= 1)
{
$params = Yii::$app->session['customerparams'];
if(isset(Yii::$app->session['customerparams']['page']))
$_GET['page'] = Yii::$app->session['customerparams']['page'];
if(isset(Yii::$app->session['customerparams']['per-page']))
$_GET['per-page'] = Yii::$app->session['customerparams']['per-page'];
}
else
{
Yii::$app->session['customerparams'] = $params;
}
$dataProvider = $searchModel->search($params);
/*Code insertion block Ends*/
Related
We got the need to display a blog posts page that display X posts - first post is displayed as a header and the rest are in 2 columns. The page has a show more button at the bottom that fetches the next page posts using ajax and adding them at the bottom.
Is it possible to get X+1 items for the subsequent pages?
Any hint, even in code are welcome since we use a sourced version of orchard installation.
So before cluttering the comments above this is my proposed solution.
I think there was a slight misunderstanding about changing the controller action which I'd like to clarify (I hope I understood everything correctly now):
Orchard.Blogs | BlogController | Item Action
public ActionResult Item(int blogId, PagerParameters pagerParameters) {
// This is all original code
Pager pager = new Pager(_siteService.GetSiteSettings(), pagerParameters);
var blogPart = _blogService.Get(blogId, VersionOptions.Published).As<BlogPart>();
if (blogPart == null)
return HttpNotFound();
if (!_services.Authorizer.Authorize(Orchard.Core.Contents.Permissions.ViewContent, blogPart, T("Cannot view content"))) {
return new HttpUnauthorizedResult();
}
// This is the actual change:
// Use the pagerParameters provided, otherwise fall back to the blog settings
pager.PageSize = pagerParameters.PageSize.HasValue ? pager.PageSize : blogPart.PostsPerPage;
// This is all original code again
_feedManager.Register(blogPart, _services.ContentManager.GetItemMetadata(blogPart).DisplayText);
var blogPosts = _blogPostService.Get(blogPart, pager.GetStartIndex(), pager.PageSize) // Your new page size will be used
.Select(b => _services.ContentManager.BuildDisplay(b, "Summary"));
dynamic blog = _services.ContentManager.BuildDisplay(blogPart);
var list = Shape.List();
list.AddRange(blogPosts);
blog.Content.Add(Shape.Parts_Blogs_BlogPost_List(ContentItems: list), "5");
var totalItemCount = _blogPostService.PostCount(blogPart);
blog.Content.Add(Shape.Pager(pager).TotalItemCount(totalItemCount), "Content:after");
return new ShapeResult(this, blog);
}
So the change is very subtle, but this way I would configure the blogs default pageSize to 7 items and for every subsequent Ajax-Request I'd provide a "pageSize"-Parameter with the desired size.
For SEO purposes I need to remove the first page number from the URL. i.e I have the following:
example.com/pages/view/1 and example.com/pages/view the two URLs points to the same contents of the view action. I want to make the pagination free from 1 in the URL. i.e first Page link and Page Number 1 should be linked to pages/view.
I tried to deal with the $pagination object like the following:
$pages = new Pagination(['totalCount' => $books['booksCount'], 'pageParam' => 'start', 'defaultPageSize' => 10,]);
$pagingLinks = $pages->getLinks();
$pagingLinks['first'] = '/';
$pages->links = $pagingLinks;
However, the last line causing error:
Setting read-only property: yii\data\Pagination::links
So I have a problem to modify the links property. Is there any other solution to get this task done?!
According to docs you should set yii\data\Pagination::forcePageParam to false by passing it in Pagination constructor
$pages = new Pagination([
'totalCount' => $books['booksCount'],
'pageParam' => 'start',
'defaultPageSize' => 10,
'forcePageParam' => false,
]);
The above answer may works for direct use of Pagination but remain an issue if it was used from another widget such as ListView.
I found the solution from a comment on an issue report on Yii2 repository on github
The solution is just define proper route in config/web.php. Suppose here we have a controller called Suras and we use the ListView widget on its action's view called view. So placing rule array with defaults has value 'page' => 1 will prevent adding the page parameter to the link's URL of the first page. Also notice the first rule 'view/<id:\d+>/1' => 'Error404', is placed in-order to prevent any access to the first page using page=1 parameter, for example, trying to access mysite.com/view/20/1 will invoke 404 error, because there is no controller called Error404.
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'rules' => [
'view/<id:\d+>/1' => 'Error404',
['pattern' => 'view/<id:\d+>/<page:\d+>', 'route' => 'suras/view', 'defaults' => ['page' => 1]],
'view/<id:\d+>/<page:\d+>' => 'suras/view',
'view/<id:\d+>' => 'suras/view',
],
],
],
My code looks like below. I am feching the json records from the server side using structs action. it is returning the records fine and i could able to see the table with data. pagination links created fine. when i click next and datasource is called on each click of any link on the pagination. if i click on colum header also, the datasource is being called.
my questions are:
1)When datasource is being called. because i am seeing sometime called and some times not. like when i got from 1page to 2page, datasource is called fine. when i go back to previous pages by clicking 'prev' link, datasource is being called. but after that if i click again on next to go to 2nd page, datasource is not being called. when exactly datasource is called and how many times it will called. is it for every link in the pagination calls datasource?
2)If my datasource returns 100 recods of data and my records per page is set to 25, then do i see the 4 pages. I am confused here with server side pagination and datasource calls.
datasource is not called for each page link and next or prev link clicks? if not, when datasource is called? please explain me. I know how many total records are there in the begining and my requirement is showing 25 records per page when ever user clicks on page number or next or prev links. i have the capability to bring corresponding 25 records based on the page number from server side.
3)how to capture the 'next' and 'prev' clicks on the pagination.
my requirement is to dynamically fetch the json data using datasource from the server whenever user click on page number links or next or prev.
Please help me out with this. I am new to YUI. I have to User YUI 2 only since we are already using it.
<div id="dynamicdata"></div>
<script type="text/javascript">
YAHOO.example.DynamicData = function() {
var myColumnDefs = [ // sortable:true enables sorting
{key:"PIN", label:"PIN", sortable:true},
{key:"CODE", label:"CODE", sortable:true}
];
// Customize request sent to server to be able to set total # of records
var generateRequest = function(oState, oSelf) {
// Get states or use defaults
oState = oState || { pagination: null, sortedBy: null };
var sort = (oState.sortedBy) ? oState.sortedBy.key : "PIN";
var dir = (oState.sortedBy && oState.sortedBy.dir === YAHOO.widget.DataTable.CLASS_DESC) ? "desc" : "asc";
var startIndex = (oState.pagination) ? oState.pagination.recordOffset : 0;
var results = (oState.pagination) ? oState.pagination.rowsPerPage : 25;
var total = YAHOO.util.Dom.get("total").value *1;
// Validate input
if(!YAHOO.lang.isNumber(total) || total < 0 || total > 200) {
YAHOO.util.Dom.get("total").value = 0;
total = 0;
alert("Total must be between 0 and 200.");
}
// Build custom request
return "sort=" + sort +
"&dir=" + dir +
"&startIndex=" + startIndex +
"&results=" + (startIndex + results) +
"&total=" + total;
};
// DataTable configuration
var myConfigs = {
generateRequest: generateRequest,
initialRequest: generateRequest(), // Initial request for first page of data
dynamicData: true, // Enables dynamic server-driven data
sortedBy : {key:"PIN", dir:YAHOO.widget.DataTable.CLASS_ASC}, // Sets UI initial sort arrow
paginator: new YAHOO.widget.Paginator({ rowsPerPage:10 }) // Enables pagination
};
var myDataSource = new YAHOO.util.DataSource("<%=request.getContextPath()%>/results.do?startIndex="+localStartIndex+"&rowsPerPage="+rowsPerPage);
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSON;
myDataSource.responseSchema = {
resultsList: "data",
fields: [
{key:"SSN"},
{key:"PIN"}
]
}
var myDataTable = new YAHOO.widget.DataTable("dynamicdata", myColumnDefs, myDataSource, myConfigs);
// DataTable instance
var myDataTable = new YAHOO.widget.DataTable("dynamicdata", myColumnDefs, myDataSource, myConfigs);
// Update totalRecords on the fly with values from server
myDataTable.doBeforeLoadData = function(oRequest, oResponse, oPayload) {
oPayload.totalRecords = 200;
return oPayload;
};
return {
ds: myDataSource,
dt: myDataTable
};
}();
As far as I remember, the DataTable will always ask for fresh data whenever it changes pages or sorts by a different column. It doesn't cache previous requests nor does it keep track what it has asked. If you don't see requests arriving on the server side it might be because of caching, but not in DataTable or DataSource but by the browser itself, which is a matter of issuing the proper headers on the server to tell the browser not to cache.
If I am not mistaken, that this is supported by the behavior you describe. The first page is requested twice, once when you first draw the table, once again when you return from page 2. All other pages are never requested twice. Why? Because the first time around the URL formed is slighty different from the URL when you return to it. The browser cache only knows about URLs.
I am struggling with the paginator in Cakephp 2.0. While I am trying to migrate my application to 2.0 I cant find any solution to jump directly to the last page. In 1.3 it was quiet to do that from outside like this:
echo $this->Html->link(__('Flights'), array('controller' => 'flights',
'action' => 'index','page' => 'last'));
but this little trick putting 'page:last' in does not work anymore in 2.0. Of course there is a Paginator function called last, but this would only help if I would be already inside the app. My Problem is to access from an outside link directly the last page of the paginator.
This is the simple way:
echo $this->Paginator->last('Any text');
Other way to get the number of the last page is:
echo $this->Paginator->counter(array('format' => '{:pages}'));
Then you can use it to generate your link.
For more info:
http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::last
Shortly after creating a bounty for this question I found the solution to MY problem using CakePHP 2.2.4. I was trying to accomplish the same task but instead using version 2.2.4 instead instead of 2.0. Basically if I had a link that looked like http://www.domain.com/articles/page:last that the controller's pagination method would know what page to go to and display the correct results (articles) for that page. For example, if I have 110 articles and the pagination limit is set to 25, by going to that URL it would display page 5 of 5, showing records 101-110. I also wanted the same capability if I go to “page:first”.
I needed to change my library file lib/Cake/Controller/Component/PaginatorComponent.php.
I changed
if (intval($page) < 1) {
$page = 1;
}
To
if ((intval($page) < 1 && $page != "last") || $page == "first") {
$page = 1;
}
I also added
if($page == "last"){
$page = $pageCount;
}
After the line
$pageCount = intval(ceil($count / $limit));
Christian Waschke, with this solution, you can use the same link helper exactly how you wrote it in your question. For me, the link helper looked like this
<?php echo $this->Html->link('Go to Last Page', array('controller' => 'articles', 'action' => 'index', 'page' => 'last')); ?>
You can 'calculate' the last page yourself if 'last' is passed as the page number;
I would discourage making modifications in the CakePHP library files as this will make it hard to perform upgrades in the future.
Basically, the PaginatorHelper uses viewVars that are calculated and set by the PaginatorComponent, as seen here: https://github.com/cakephp/cakephp/blob/master/lib/Cake/Controller/Component/PaginatorComponent.php#L212
You can replicate this in your action; for example:
public function index()
{
if (!empty($this->request->params['named']['page'])) {
switch($this->request->params['named']['page']) {
case 'first':
// replace the 'last' with actual number of the first page
$this->request->params['named']['page'] = 1;
break;
case 'last':
// calculate the last page
$limit = 10; // your limit here
$count = $this->Flight->find('count');
$pageCount = intval(ceil($count / $limit));
// replace the 'last' with actual number of the last page
$this->request->params['named']['page'] = $pageCount;
break;
}
}
// then, paginate as usual
$this->set('data', $this->paginate('Flight'));
}
To improve this, this logic should be moved to a separate method, or to a behavior. However; as seen above, it is not required to make modifications in the PaginatorComponent!
Also note that the 'find(count)' in my example does not take additional conditions, they should be added if required
If you have a look in the CakePHP 1.3 source for paginate(), the code above is comparable; https://github.com/cakephp/cakephp/blob/1.3/cake/libs/controller/controller.php#L1204
i have a page tweety which contains a single form where a user enters a word in a textbox and in on pressing search the tweets corresponding to that word are displayed.
I want to use hook_nodeapi to add these tweets but i want those things only on a specific url not all the page(or any node type).
I ll use hook_menu to make that page and display the form for the search. What should i do after that. I knw the twiiter api to fetch the tweets so that is not a issue.
I think what you're attempting to do is have the tweets load on a predetermined page by passing the posted information to the page, is that correct?
There's a couple ways I think you could accomplish this - easiest, and not requiring any complex ahah scripting would be to take the word they enter and generate a path which you could then use to make the page in question.
Make a menu Item:
function mymodule_menu() {
$items = array();
$items['mymodule/tweets/%'] = array(
'title' => t('Tweets about' . check_plain(array(2))),
'page callback' => 'mymodule_tweet_display',
'page arguments' => array(2) // This is the 3rd argument in the path, the %
);
}
In your form, you'll need to send the user to this path so add to your hook_submit function:
$tweet_topic = $form_state['values']['your-field-name-here'];
drupal_goto('mymodule/tweets/' . $tweet_topic);
Now add your page module, this is where you can use the twitter api:
function mymodule_tweet_display($tweet_topic) {
$tweet_topic = check_plain($tweet_topic);
// Use this variable in your Twitter API call
// ...
// Make sure you assign your display content to the page by returning a variable
return $page;
}