Correct way to handle pagination with form submission? - pagination

I have a form for doing a search on a search page:
<form action="{{ url_for('searchresults') }}" method="get" name="noname" id="theform">
{{ form2.page(id="hiddenpage") }}
... some form inputs
<button id = "mybutton" type = "submit" >Apply</button>
</form>
The form is a SearchForm, where
class SearchForm(Form):
page = HiddenField()
categories = SelectMultipleField(u'Text', validators=[Optional()])
# some other stuff...
The view for searchresults handles the form:
#app.route('/searchresults', methods=['GET'])
def searchresults():
form = SearchForm()
# handle the form and get the search results using pagination
page = int(request.args.getlist('page')[0])
results = models.Product.query....paginate(page, 10, False)
return render_template('searchresults.html', form=form, results=results, curpage=page)
The results in render_template will be the first 10 results of my query. In searchresults.html I display the results, along with a next and previous link for the other results. This page also contains the same search form which I re-instate as per the initial submission. Currently I'm handling the next and previous links using
Next
So the next link re-submits the same initial form, but with the page value increased. I'm not really happy with this approach because when I hover over the next link I don't see the actual page I will be directed to. It also is starting to feel like a bit of a hack. Is there a more natural way to do this? When the form is initially submitted I could use it to create a long string of the desired parameters and use that string in the rendered page as href=" {{ url_for('searchresults') }}?mystring", but this also seems unnatural. How should I handle this?

You have your form configured to submit as a GET request, so you don't really need to force a re-submission through Javascript, you can achieve the same result by setting the next and prev links to URLs that include all the parameters in your form with the page modified to the correct next and previous page numbers.
This is really easy to do with url_for(). Any argument you add that do not match route components will be added to the query string, so you can do something like this:
Next
One thing to keep in mind is CSRF. If you have it enabled in your form, then your next/prev URLs will also need to have a valid token. Or you can disable CSRF, which for a search form might be okay.

Take advantage of the fact that your form arguments are already present in the URL and use request.args to pass the URL parameters into your form:
form = SearchForm(request.args)
Then, if you make your page field an IntegerField with a HiddenInput widget instead of a string field:
from wtforms.widgets import HiddenInput
class SearchForm(Form):
page = HiddenField(widget=HiddenInput, default=1)
you can increment page before you pass the form off to your search results page:
form.page.data += 1
And then, in your page, you simply create the link to the next page:
Next

Related

How to show and update current page number in an input box palceholder Using Tabulator

I am trying to show the current page number in an input box as a place holder the issue is I can't figure out how to update the value when users go to another page.
Here is what I tried:
<input id="currentPage"/>
document.getElementById("currentPage").placeholder = tabulator_table.getPage();
Here is the first part of the question
Here is sample
You want to use the pageLoaded callback on the table instance.
When creating the table, you need to add a property for pageLoaded as a function with a parameter for the page number. This callback is triggered each time the page is loaded.
Here is a working example, https://jsfiddle.net/nrayburn/w68d75Lq/1/.
So you would do something like this, where #table is the element id for the table and input is a reference to your input element where you keep the page number value.
new Tabulator('#table', {
...tableOptions,
pageLoaded: (pageNumber) => {
input.value = pageNumber
}
});

chameleon can not render deform form

I have used deform before with jinja engine but with chameleon i can not render my form
I pass the form to the view to be rendered
#view_config(route_name='home_cms',
renderer='../views/accounts.pt',
request_method='GET',
)
def home(self):
model = Bundle("ModelAccounts", xx.account_id,
xx.name, xx.state, xx.account_type)
cm_filters = xxx(self.request.db)
list_accounts = xxx.search(model=model)
return dict(list_accounts=list_accounts, filter_form=self.form.render())
in the view i add this line
<span tal:content="python:filter_form"></span>
and in the main function I add this line of code
deform.renderer.configure_zpt_renderer()
config.add_static_view('static_deform', 'deform:static')
You don't say specifically what you are getting, but from what I can see I think the issue you are facing is that your HTML form is getting escaped. If this is the case then change your template code in ../views/accounts.pt' to read:
<span tal:content="structure: filter_form"></span>
The "structure" keyword tells Chameleon to not escape the value. See http://chameleon.readthedocs.io/en/latest/reference.html#structure for more details

how do we add url parameters? (EJS + Node + Express)

I understood how we parse the url parameters in express routes, as in the example
How to get GET (query string) variables in Express.js on Node.js?
But where do the url parameters come from in the first place?
EDIT:
Apparently, I can build such a query with jquery (i.e $.get). I can append params to this query object. It s cool, but still i m trying to understand how we achieve this in the query that renders the page as a whole.
An example : when i choose the oldest tab below, how does SO add ?answertab=oldest to the url so it becomes :
https://stackoverflow.com/questions/30516497/how-do-we-add-url-parameters-ejs-node-express?answertab=oldest#tab-top
The string you're looking at is a serialization of the values of a form, or some other such method of inputing data. To get a sense of this, have a look at jQuery's built in .serialize() method.
You can construct that string manually as well, and that's pretty straight forward as well. The format is just ?var1=data1&var2=data2 etc. If you have a JSON object {"name": "Tim", "age": 22} then you could write a very simple function to serialize this object:
function serializeObject(obj) {
var str = "?";
for(var i = 0; i < Object.keys(obj).length; i++) {
key = Object.keys(obj)[i];
if (i === Object.keys(obj).length - 1)
str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
else
str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]) + "&";
}
return str;
}
Running seralizeObject({"name": "Tim", "age": 22}) will output '?name=Tim&age=22'. This could be used to generate a link or whatnot.
The page author writes them so. This is how they "come in the first place". The authors of an HTML page decide (or are told by website designers) where to take the user when he clicks on a particular anchor element on it. If they want users to GET a page with some query parameters (which their server handles), they simply add query string of their choice to the link's href attribute.
Take a look at the href attribute of the oldest tab you clicked:
<a
class="youarehere"
href="/questions/30516497/how-do-we-add-url-parameters-ejs-node-express?answertab=oldest#tab-top"
title="Answers in the order they were provided"
>
oldest
</a>
When you clicked it, the browser simply took you to path indicated in href attribute /questions/30516497/how-do-we-add-url-parameters-ejs-node-express?answertab=oldest#tab-top relative to the base URL http://stackoverflow.com. So the address bar changed.
stackoverflow.com may have its own system of generating dynamic HTML pages. Their administrators and page authors have configured their server to handle particular query parameters and have put in place their own methods to make sure that links on their pages point to the URL(including query string) they wish.
You need to provide URIs with query strings of your choice (you can build them using url.format and querystring.stringify) to your template system to render. Then make your express routes process them and generate pages depending on their value.

Alloy user interface (access a tag value)

I'm working with liferay portal 6.2. And I want to get the value of the text in a tag with alloy user interface.
exemple:
<div>
<p> Paragraph </p>
"value"
</div>
the desired result is: value
please help.
AlloyUI, being an extension of YUI3, uses get/set methods to access and manipulate the properties/attributes of the object (YUI3 Node / AlloyUI Node) that is returned when looking up elements from the page.
Some examples can be reviewed in this documentation as well as this documentation.
In general you'll need something unique (i.e. id, css class) to the div in order to fetch only that element. Once you have that element, divNode.get('text') will give you all of the text within the element. There is not a means to easily "skip" the paragraph contents within the div without the value being contained within some other markup. If you have control over the markup and can do this, that would be the best option. Otherwise you are left to using the replace function to strip out the paragraph contents from the text.
<script>
AUI().use('aui-base', function(A) {
var paragraphText = A.one('#myDiv>p').get('text');
var divText = A.one('#myDiv').get('text')
var onlyValue = divText.replace(paragraphText, "").trim()
console.log(onlyValue)
})
</script>

Customize the search portlet in Plone for specific content types

I'm using the search portlet in certain areas of my website, but I'd like to restrict the results to only search for a specific content type: for example only search the news items, or only show Faculty Staff Directory profiles.
I know you can do this after you get to the ##search form through that "filter" list, but is there a way to start with the filter on, so that the "Live Search" results only show the relevant results (i.e. only news items or only profiles).
I suspect you know it already, but just to be sure: You can globally define which types should be allowed to show up in searchresults in the navigations-settings of the controlpanel, and then export and include the relevant parts to your product's GS-profile-propertiestool.xml.
However, if you would like to have some types excluded only in certain sections, you can customize Products.CMFPlone/skins/plone_scripts/livesearch_reply, which already filters the types, to only show "friendly_types" around line 38 (version 4.3.1) and add a condition like this:
Edit:
I removed the solution to check for the absolute_url of the context, because the context is actually the livesearch_reply in this case, not the current section-location. Instead the statement checks now, if the referer is our section:
REQUEST = context.REQUEST
current_location = REQUEST['HTTP_REFERER']
location_to_filter = '/fullpath/relative/to/siteroot/sectionId'
url_to_filter = str(portal_url) + location_to_filter
types_to_filter = ['Event', 'News Item']
if current_location.find(url_to_filter) != -1 or current_location.endswith(url_to_filter):
friendly_types = types_to_filter
else:
friendly_types = ploneUtils.getUserFriendlyTypes()
Yet, this leaves the case open, if the user hits the Return- or Enter-key or the 'Advanced search...'-link, landing on a different result-page than the liveresults have.
Update:
An opportunity to apply the filtering to the ##search-template can be to register a Javascript with the following content:
(function($) {
$(document).ready(function() {
// Let's see, if we are coming from our special section:
if (document.referrer.indexOf('/fullpath/relative/to/siteroot/sectionId') != -1) {
// Yes, we have the button to toggle portal_type-filter:
if ($('#pt_toggle').length>0) {
// If it's checked we uncheck it:
if ($('#pt_toggle').is(':checked')) {
$('#pt_toggle').click();
}
// If for any reason it's not checked, we check and uncheck it,
// which results in NO types to filter, for now:
else {
$('#pt_toggle').click();
$('#pt_toggle').click();
}
// Then we check types we want to filter:
$("input[value='Event']").click();
$("input[value='News Item']").click();
}
}
})
})(jQuery);
Also, the different user-actions result in different, inconsistent behaviours:
Livesearch accepts terms which are not sharp, whereas the ##search-view only accepts sharp terms or requires the user to know, that you can append an asterix for unsharp results.
When hitting the Enter/Return-key in the livesearch-input, the searchterm will be transmitted to the landing-page's (##search) input-element, whilst when clicking on 'Advanced search...' the searchterm gets lost.
Update:
To overcome the sharp results, you can add this to the JS right after the if-statement:
// Get search-term and add an asterix for blurry results:
var searchterm = decodeURI(window.location.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI('SearchableText').replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1")) + '*';
// Insert new searchterm in input-text-field:
$('input[name=SearchableText]').val(searchterm);
Update2:
In this related quest, Eric Brehault provides a better solution for passing the asterix during submit: Customize Plone search
Of course you can also customize the target of advanced-search-link in livesearch_reply, respectively in the JS for ##search, yet this link is rather superfluous UI-wise, imho.
Also, if you're still with Archetypes and have more use-cases for pre-filtered searchresults depending on the context, I can recommend to have a look at collective.formcriteria, which allows to define search-criteria via the UI. I love it for it's generic and straightforward plone-ish approach: catalogued indizi and collections. In contradiction to eea.facetednavigation it doesn't break accessibility and can be enhanced progressively with some live-search-js-magic with a little bit of effort, too. Kudos to Ross Patterson here! Simply turn a collection (old-style) into a searchform by changing it's view and it can be displayed as a collection-portlet, as well. And you can decide which criteria the user should be able to change or not (f.e. you hide the type-filter and offer a textsearch-input).
Watch how the query string changes when you use the filter mechanism on the ##search page. You're simply adding/subtracting catalog query criteria.
You may any of those queries in hidden fields in a search form. For example:
<form ...>
....
<input type="hidden" name="portal_type" value="Document" />
</form>
The form on the query string when you use filter is complicated a bit by its record mechanism, which allows for some min/max queries. Simple filters are much easier.

Resources