问:
Xpages Repeat Control / Documents from View - best practice
What I wanted to achieve in a recent application was to use my own pagination - so I need to know how many documents are in a (categorized) view to calculate the pages needed.

```<code><xp:repeat id="repeat1" rows="30"
                value="#{javascript:viewScope.counterArray}"
                indexVar="counter" var="row" disableTheme="true"
                removeRepeat="true">
                <li>
                    <xp:link escape="true"
                        text="#{javascript:(counter + 1) + ' (' + row.toString() + ')'}" id="link3"
                        disableTheme="true">
                        <xp:eventHandler event="onclick"
                            submit="true">
                            <xp:this.action>
                                <xp:executeScript>
                                    <xp:this.script><![CDATA[#{javascript:viewScope.put("repeatFirstIndex", row);}]]></xp:this.script>
                                </xp:executeScript>
                            </xp:this.action>
                        </xp:eventHandler>
                    </xp:link>
                </li>
            </xp:repeat>
</code>```

Becaue of this known problem I have to do a workaround, querying the number of documents via a repeat control.

```<code><xp:repeat id="blindRepeat" rows="0" value="#{viewStories}"></xp:repeat>
</code>```

I use this sample code to get the right number of iterations for the repeat control.

```<code><xp:this.beforePageLoad><![CDATA[#{javascript:
var counterArray = [];
var numberOfStories = getComponent("blindRepeat").getRowCount();
var numberOfPagesNecessary = Math.ceil(numberOfStories / sessionScope.get("ListPagerPerPage"));
for (var i = 0; i < (numberOfPagesNecessary - 1); i++) {
    counterArray[i] = i*sessionScope.get("ListPagerPerPage");
    viewScope.lastPage = i*sessionScope.get("ListPagerPerPage");
}
viewScope.counterArray = counterArray;}]]></xp:this.beforePageLoad>
</code>```

But right here is where I run into a Catch 22:
I can only use a repeat control, because I cannot get the richt number of documents from the view directly, like Knut Herrmann (1) does, but the repeat control is not available , so I cannot use it like Per Henrik Lausten (2) proposes.

A colleague proposed filling the Array in a Script block that is nested behind the blind Repeat Control (so it should be drawn) but before the pagination repeat control (so viewScope should hold the right numbers), but this solution seems rather flaky to me.

So I am inquiring if there is a 'best practise' for this kind of situation - I'd like to solve this properly and not in a way that depends on the timely execution of some javascript

答:
Considered the reason you gave in the comment to your question I suggest you to implement your own custom renderer for the pager.

In the faces-config.xml declare as follows:

```<code><render-kit>
    <renderer>
        <component-family>com.ibm.xsp.Pager
        </component-family>
        <renderer-type>com.ibm.xsp.XPager
        </renderer-type>
        <renderer-class>com.irc.xsp.renderer.UIPagerRenderer
        </renderer-class>
    </renderer>
</render-kit>
</code>```

Where com.irc.xsp.renderer.UIPagerRenderer is the rendering class. Obviously you can name it whatever you want. Just use the same name in both places.

The renderer could look something like this:

```package com.irc.xsp.renderer;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.component.UIPager;
import com.ibm.xsp.component.UIPagerControl;
import com.ibm.xsp.component.xp.XspPager;
import com.ibm.xsp.component.xp.XspPagerControl;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.event.PagerEvent;
import com.ibm.xsp.extsn.ResourceHandler;
import com.ibm.xsp.renderkit.html_extended.XPagerRenderer;
import com.ibm.xsp.util.AjaxUtilEx;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.JavaScriptUtil;
import com.ibm.xsp.util.TypedUtil;

public class UIPagerRenderer extends XPagerRenderer {

    public static final String VAR_PAGE = "page"; //$NON-NLS-1$

    private enum Property {
        LIST_ITEM_CLASS, PAGER_LINK_CLASS, ACTIVE_CLASS("active"), DISABLED_CLASS("disabled");

        String className;

        Property() {
        }

        Property(String s) {
            this.className = s;
        }

        String getClassName() {
            return className;
        }

    }

    @Override
    public void decode(FacesContext context, UIComponent component) {
        super.decode(context, component);

        // check that this component cause the submit
        if (decodeCausedSubmit(context, component)) {
            PagerEvent pagerEvent = new PagerEvent(component);

            String hiddenValue = FacesUtil.getHiddenFieldValue(context);
            if (StringUtil.isNotEmpty(hiddenValue)) {
                int pos = hiddenValue.lastIndexOf('_');
                if (pos > -1) {
                    hiddenValue = hiddenValue.substring(pos + 1);
                    if (isFirst(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_FIRST);
                    } else if (isLast(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_LAST);
                    } else if (isNext(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_NEXT);
                    } else if (isPrevious(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_PREVIOUS);
                    } else {
                        try {
                            int value = Integer.parseInt(hiddenValue);
                            pagerEvent.setAction(PagerEvent.ACTION_GOTOPAGE);
                            pagerEvent.setPage(value);
                        } catch (NumberFormatException nfe) {
                            return; // just don't queue the event
                        }
                    }
                } else {
                    return;
                }
            }
            ((UIPager) component).queueEvent(pagerEvent);
        }
    }

    private boolean decodeCausedSubmit(FacesContext context, UIComponent component) {
        String currentClientId = component.getClientId(context);
        String hiddenValue = FacesUtil.getHiddenFieldValue(context);

        if (currentClientId != null && hiddenValue != null) {
            return StringUtil.indexOfIgnoreCase(hiddenValue, currentClientId) > -1;
        }
        return false;
    }

    @Override
    public boolean getRendersChildren() {
        return true;
    }

    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        if (context == null || component == null) {
            throw new IOException();
        }

        List<?> controls = component.getChildren();

        if (controls.isEmpty()) {
            return;
        }

        XspPager pager = (XspPager) component;
        UIPager.PagerState pagerState = ((UIPager) component).createPagerState();

        if (pagerState == null) {
            throw new FacesExceptionEx(ResourceHandler.getString("PagerRenderer.Pagerisnotassociatedwithanydataco"));
        }

        encodePagerContent(context, context.getResponseWriter(), pager, pagerState, controls, false);
    }

    protected void encodePagerContent(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, List<?> controls, boolean rtl) throws IOException {
        // Compute the pages that should be displayed
        int pageCount = pagerState.getPageCount();
        int startCount = getStartCount(pagerState, pageCount);
        int endCount = getEndCount(pagerState, pageCount, startCount);

        String pagerId = pager.getClientId(context);

        String pagerRole = pager.getRole();
        String pagerTitle = pager.getTitle();
        String pagerOuterClass = pager.getOuterStyleClass();
        String pagerAriaLabel = pager.getAriaLabel();
        boolean closeOuterTag = false;

        if (isAnyAssigned(pagerRole, pagerTitle, pagerOuterClass, pagerAriaLabel)) {
            writer.startElement("div", null); // $NON-NLS-1$

            writeNonNullAttributeOnly(writer, "role", pagerRole);
            writeNonNullAttributeOnly(writer, "title", pagerTitle);
            writeNonNullAttributeOnly(writer, "class", pagerOuterClass);
            writeNonNullAttributeOnly(writer, "aria-label", pagerAriaLabel);

            closeOuterTag = true;
        }

        writer.startElement("ul", null);

        writeNonNullAttributeOnly(writer, "class", pager.getStyleClass());
        writeNonNullAttributeOnly(writer, "id", pagerId);

        Iterator<?> it = controls.iterator();

        while (it.hasNext()) {
            Object obj = it.next();

            if (obj instanceof XspPagerControl) {
                XspPagerControl control = (XspPagerControl) obj;
                String type = control.getType();

                if (StringUtil.isNotEmpty(type)) {
                    if (isFirst(type) || isNext(type) || isPrevious(type)
                            || (isLast(type) && pager.isAlwaysCalculateLast())) {
                        encodeAction(context, writer, pager, pagerState, pagerId, control, type, startCount, endCount,
                                rtl);

                        continue;
                    } else if (isLast(type) && !pager.isAlwaysCalculateLast()) {
                        if (!pagerState.hasMoreRows()) {
                            encodeAction(context, writer, pager, pagerState, pagerId, control, type, startCount,
                                    endCount, rtl);
                        } else {
                            writer.startElement("li", null);
                            writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS
                                    .getClassName(), Property.DISABLED_CLASS.getClassName()));

                            writer.startElement("a", null);
                            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
                            writer.writeText(getMayBeMorePages(), null);
                            writer.endElement("a");

                            writer.endElement("li");
                        }

                        continue;
                    } else if (type.equalsIgnoreCase(UIPagerControl.TYPE_GROUP)) {
                        encodeGroup(context, writer, pager, pagerState, pagerId, control, startCount, endCount);

                        continue;
                    } else if (type.equalsIgnoreCase(UIPagerControl.TYPE_STATUS)) {
                        encodeStatus(context, writer, pager, pagerState, control, startCount, endCount);

                        continue;
                    } else if (isSeparator(type)) {
                        encodeSeparator(context, writer, control, type);

                        continue;
                    } else if (type.equalsIgnoreCase(UIPagerControl.TYPE_GOTO)) {
                        encodeGoto();

                        continue;
                    }
                }

                // "Unknown control type {0}"
                String msg = com.ibm.xsp.extsn.ResourceHandler.getString("PagerRenderer.Unknowncontroltype0"); //$NON-NLS-1$
                msg = StringUtil.format(msg, type);
                throw new FacesExceptionEx(msg);
            }
        }

        writer.endElement("ul");

        if (closeOuterTag) {
            writer.endElement("div");
        }
    }

    protected void encodeAction(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, String pagerId, XspPagerControl control, String type, int startCount,
            int endCount, boolean rtl) throws IOException {
        String controlId = pagerId + "__" + type;
        String defaultText = "";
        String ariaLabel = "";
        boolean renderLink = true;

        //TODO need to handle BIDI here for the unicode symbols
        if (isFirst(type)) {
            renderLink = pagerState.getCurrentPage() > startCount;
            // "\u00AB" FirstSymbol
            defaultText = "\u00AB"; //$NON-NLS-1$
            // "First page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.First"); //$NON-NLS-1$
        } else if (isPrevious(type)) {
            renderLink = pagerState.getCurrentPage() > startCount;
            // "\u2039" PreviousSymbol
            defaultText = "\u2039"; //$NON-NLS-1$
            // "Previous page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.Previous"); //$NON-NLS-1$
        } else if (isNext(type)) {
            renderLink = pagerState.getCurrentPage() < endCount - 1;
            // "\u203A" NextSymbol
            defaultText = "\u203A"; //$NON-NLS-1$;
            // "Next page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.Next"); //$NON-NLS-1$
        } else if (isLast(type)) {
            renderLink = pagerState.getCurrentPage() < endCount - 1;
            // "\u00BB" LastSymbol
            defaultText = "\u00BB"; //$NON-NLS-1$
            // "Last page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.Last"); //$NON-NLS-1$
        }

        writer.startElement("li", null);
        String listItemClass = Property.LIST_ITEM_CLASS.getClassName();

        if (!renderLink) {
            //If current page is the first, disable first/previous pagers
            //and if current page is the last, disable last/next pagers
            listItemClass = com.irc.util.StringUtil.concat(listItemClass, Property.DISABLED_CLASS.getClassName());
        }

        writeNonNullAttributeOnly(writer, "class", listItemClass);

        // Generate the image link
        String val = (String) control.getValue();

        if (StringUtil.isEmpty(val)) {
            val = defaultText;
        }

        // Generate the text link
        if (StringUtil.isNotEmpty(val)) {
            writer.startElement("a", null); // $NON-NLS-1$

            if (renderLink) {
                writer.writeAttribute("aria-disabled", "false", null); // $NON-NLS-1$ $NON-NLS-2$
                writer.writeAttribute("href", "#", null); // $NON-NLS-1$ $NON-NLS-2$
            } else {
                //add a11y attributes
                writer.writeAttribute("aria-disabled", "true", null); // $NON-NLS-1$ $NON-NLS-2$
            }

            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());

            writer.writeAttribute("id", controlId + "__lnk", null); // $NON-NLS-1$ $NON-NLS-2$
            writer.writeAttribute("role", "button", null); // $NON-NLS-1$ $NON-NLS-2$
            writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$
            writer.writeText(val, null);
            writer.endElement("a"); // $NON-NLS-1$

            if (renderLink) {
                setupSubmitOnClick(context, pager, pagerState, controlId, controlId + "__lnk"); // $NON-NLS-1$
            }
        }

        writer.endElement("li"); // $NON-NLS-1$
    }

    protected void encodeGroup(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, String pagerId, XspPagerControl control, int startCount, int endCount)
            throws IOException {
        // Save the old page value
        Map<String, Object> requestMap = TypedUtil.getRequestMap(context.getExternalContext());
        Object oldPage = requestMap.get(VAR_PAGE);
        String controlId = pagerId + "__" + control.getType();//$NON-NLS-1$

        // Encode the pages
        for (int i = startCount; i < endCount; i++) {
            // Push the page number
            requestMap.put(VAR_PAGE, i + 1);
            boolean renderLink = (i != pagerState.getCurrentPage());

            writer.startElement("li", null); // $NON-NLS-1$
            String listItemClass = Property.LIST_ITEM_CLASS.getClassName();

            if (!renderLink) {
                listItemClass = com.irc.util.StringUtil.concat(listItemClass, Property.ACTIVE_CLASS.getClassName());
            }

            writeNonNullAttributeOnly(writer, "class", listItemClass);

            // Generate the image link
            String val = (String) control.getValue();

            if (StringUtil.isEmpty(val)) {
                val = Integer.toString(i + 1);
            }

            // Generate the text link
            if (StringUtil.isNotEmpty(val)) { // Generate the text link
                writer.startElement("a", control); //$NON-NLS-1$
                writer.writeAttribute("id", controlId + "__lnk__" + i, null); // $NON-NLS-1$ $NON-NLS-2$
                // "Page {0}"
                String ariaLabel = ResourceHandler.getString("PagerRenderer.Gotopage0"); //$NON-NLS-1$
                ariaLabel = StringUtil.format(ariaLabel, val);
                writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$
                writer.writeAttribute("role", "button", null); // $NON-NLS-1$ $NON-NLS-2$

                writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());

                if (renderLink) { //make sure the a is tab-able
                    writer.writeAttribute("tabindex", "0", null); // $NON-NLS-1$ $NON-NLS-2$
                    writer.writeAttribute("aria-pressed", "false", null); // $NON-NLS-1$ $NON-NLS-2$
                } else {
                    writer.writeAttribute("aria-pressed", "true", null); // $NON-NLS-1$ $NON-NLS-2$
                }

                writer.writeText(val, null);
                writer.endElement("a"); // $NON-NLS-1$

                if (renderLink) {
                    setupSubmitOnClick(context, pager, pagerState, controlId + "__lnk__" + i, controlId + "__lnk__" + i); // $NON-NLS-1$ $NON-NLS-2$
                }
            }

            writer.endElement("li"); // $NON-NLS-1$
        }

        // Encode after the pages
        if (!pager.isAlwaysCalculateLast()) {
            if (endCount < pagerState.getLastPage() || pagerState.hasMoreRows()) {
                writer.startElement("li", null); // $NON-NLS-1$
                writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
                        Property.DISABLED_CLASS.getClassName()));

                writer.startElement("a", control); //$NON-NLS-1$
                writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
                writer.writeText(getMayBeMorePages(), null);
                writer.endElement("a"); // $NON-NLS-1$
                writer.endElement("li"); // $NON-NLS-1$
            }
        }

        // Restore the old page value
        if (oldPage != null) {
            requestMap.put(VAR_PAGE, oldPage);
        } else {
            requestMap.remove(VAR_PAGE);
        }

    }

    protected void setupSubmitOnClick(FacesContext context, XspPager component, UIPager.PagerState st, String clientId,
            String sourceId) {
        boolean immediate = false;

        UIComponent subTree = ((FacesContextEx) context).getSubTreeComponent();

        boolean partialExec = component.isPartialExecute();
        String execId = null;
        if (partialExec) {
            execId = component.getClientId(context);
            immediate = true;
        } else {
            if (subTree != null) {
                partialExec = true;
                execId = subTree.getClientId(context);
                immediate = true;
            }
        }

        boolean partialRefresh = component.isPartialRefresh();
        String refreshId = null;
        if (partialRefresh) {
            UIComponent refreshComponent = component.findSharedDataPagerParent();
            if (null == refreshComponent) {
                refreshComponent = (UIComponent) st.getDataIterator();
            }
            refreshId = AjaxUtilEx.getRefreshId(context, refreshComponent);
        } else {
            if (subTree != null) {
                partialRefresh = true;
                refreshId = subTree.getClientId(context);
            }
        }

        // call some JavaScript in xspClient.js
        final String event = "onclick"; // $NON-NLS-1$
        // Note, the onClick event is also triggered if the user tabs to the
        // image\link and presses enter (Not just when clicked with a
        // mouse).

        // When the source is clicked, put its id in the hidden field and
        // submit the form.
        StringBuilder buff = new StringBuilder();
        if (partialRefresh) {
            JavaScriptUtil.appendAttachPartialRefreshEvent(buff, clientId, sourceId, execId, event,
            /* clientSideScriptName */null, immediate ? JavaScriptUtil.VALIDATION_NONE
                    : JavaScriptUtil.VALIDATION_FULL,
            /* refreshId */refreshId,
            /* onstart */getOnStart(component),
            /* oncomplete */getOnComplete(component),
            /* onerror */getOnError(component));
        } else {
            JavaScriptUtil.appendAttachEvent(buff, clientId, sourceId, execId, event,
            /* clientSideScriptName */null,
            /* submit */true, immediate ? JavaScriptUtil.VALIDATION_NONE : JavaScriptUtil.VALIDATION_FULL);
        }
        String script = buff.toString();

        // Add the script block we just generated.
        JavaScriptUtil.addScriptOnLoad(script);
    }

    protected String getOnStart(XspPager component) {
        return (String) component.getAttributes().get("onStart"); // $NON-NLS-1$
    }

    protected String getOnComplete(XspPager component) {
        return (String) component.getAttributes().get("onComplete"); // $NON-NLS-1$
    }

    protected String getOnError(XspPager component) {
        return (String) component.getAttributes().get("onError"); // $NON-NLS-1$
    }

    protected void encodeStatus(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, XspPagerControl control, int startCount, int endCount) throws IOException {
        writer.startElement("li", null); // $NON-NLS-1$
        writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
                Property.DISABLED_CLASS.getClassName()));

        String val = (String) control.getValue();

        if (StringUtil.isEmpty(val)) {
            val = "{0}"; // $NON-NLS-1$
        }

        if (pagerState.getLastPage() > 0) {
            writer.startElement("a", null); // $NON-NLS-1$
            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
            writer.writeAttribute("role", "button", null); // $NON-NLS-2$ $NON-NLS-1$
            val = StringUtil.format(val, pagerState.getCurrentPage() + 1, pagerState.getLastPage(), startCount,
                    endCount);
            writer.writeText(val, null);
            writer.endElement("a"); // $NON-NLS-1$
        }

        writer.endElement("li"); // $NON-NLS-1$
    }

    protected void encodeSeparator(FacesContext context, ResponseWriter writer, XspPagerControl control, String type)
            throws IOException {
        String val = (String) control.getValue();

        writer.startElement("li", null); // $NON-NLS-1$

        if (StringUtil.isEmpty(val)) {
            String defaultSeparator = "|"; // $NON-NLS-1$

            if (type.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATORPAGE)) {
                // "Page"
                defaultSeparator = com.ibm.xsp.extsn.ResourceHandler.getString("PagerRenderer.Page"); //$NON-NLS-1$
            }

            val = defaultSeparator;
        }

        writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
                Property.DISABLED_CLASS.getClassName()));

        // Generate the text link
        if (StringUtil.isNotEmpty(val)) {
            writer.startElement("a", null); // $NON-NLS-1$
            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
            writer.writeText(val, null);
            writer.endElement("a"); // $NON-NLS-1$
        }

        writer.endElement("li"); // $NON-NLS-1$
    }

    protected void encodeGoto() {
        // Do not exists in core XPages yet..
    }

    protected int getStartCount(UIPager.PagerState st, int pageCount) {
        int start = (st.getFirst() / st.getRows()) - pageCount / 2;
        start = Math.min(Math.max(0, st.getLastPage() - pageCount), Math.max(0, start));
        return start;
    }

    protected int getEndCount(UIPager.PagerState st, int pageCount, int start) {
        int sizeOfPageRange = Math.min(start + pageCount, st.getLastPage()) - start;
        int end = start + sizeOfPageRange;
        return end;
    }

    protected boolean isFirst(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_FIRST)
                || type.equalsIgnoreCase(UIPagerControl.TYPE_FIRSTARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_FIRSTIMAGE));
    }

    protected boolean isNext(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_NEXT) || type.equalsIgnoreCase(UIPagerControl.TYPE_NEXTARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_NEXTIMAGE));
    }

    protected boolean isLast(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_LAST) || type.equalsIgnoreCase(UIPagerControl.TYPE_LASTARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_LASTIMAGE));
    }

    protected boolean isPrevious(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUS)
                || type.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUSARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUSIMAGE));
    }

    protected boolean isSeparator(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATOR) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_SEPARATORPAGE));
    }

    protected String getMayBeMorePages() {
        return "..."; // $NON-NLS-1$
    }

    private void writeNonNullAttributeOnly(ResponseWriter writer, String attributeName, String attributeValue)
            throws IOException {
        if (attributeValue != null && !attributeValue.isEmpty()) {
            writer.writeAttribute(attributeName, attributeValue, null);
        }
    }

    private boolean isAnyAssigned(String... objs) {
        for (String s : objs) {
            if (s != null && !s.isEmpty())
                return true;
        }

        return false;
    }

}
```

That is a class I've been using in my last project. Basically it's re-implemeting everything that the pager does in order to have control over the look and feel of the component - it's called renderer after all. You could try to tweak it here and there to see how the various things work out on the page. It's a learning process.

Body:
What I wanted to achieve in a recent application was to use my own pagination - so I need to know how many documents are in a (categorized) view to calculate the pages needed.

<xp:repeat id="repeat1" rows="30"
                value="#{javascript:viewScope.counterArray}"
                indexVar="counter" var="row" disableTheme="true"
                removeRepeat="true">

                    <xp:link escape="true"
                        text="#{javascript:(counter + 1) + ' (' + row.toString() + ')'}" id="link3"
                        disableTheme="true">
                        <xp:eventHandler event="onclick"
                            submit="true">
                            <xp:this.action>
                                <xp:executeScript>
                                    <xp:this.script><![CDATA[#{javascript:viewScope.put("repeatFirstIndex", row);}]]></xp:this.script>
                                </xp:executeScript>
                            </xp:this.action>
                        </xp:eventHandler>
                    </xp:link>

            </xp:repeat>

Becaue of this known problem I have to do a workaround, querying the number of documents via a repeat control.

<xp:repeat id="blindRepeat" rows="0" value="#{viewStories}"></xp:repeat>

I use this sample code to get the right number of iterations for the repeat control.

<xp:this.beforePageLoad><![CDATA[#{javascript:
var counterArray = [];
var numberOfStories = getComponent("blindRepeat").getRowCount();
var numberOfPagesNecessary = Math.ceil(numberOfStories / sessionScope.get("ListPagerPerPage"));
for (var i = 0; i < (numberOfPagesNecessary - 1); i++) {
    counterArray[i] = i*sessionScope.get("ListPagerPerPage");
    viewScope.lastPage = i*sessionScope.get("ListPagerPerPage");
}
viewScope.counterArray = counterArray;}]]></xp:this.beforePageLoad>

But right here is where I run into a Catch 22:
I can only use a repeat control, because I cannot get the richt number of documents from the view directly, like Knut Herrmann (1) does, but the repeat control is not available , so I cannot use it like Per Henrik Lausten (2) proposes.

A colleague proposed filling the Array in a Script block that is nested behind the blind Repeat Control (so it should be drawn) but before the pagination repeat control (so viewScope should hold the right numbers), but this solution seems rather flaky to me.

So I am inquiring if there is a 'best practise' for this kind of situation - I'd like to solve this properly and not in a way that depends on the timely execution of some javascript

Body:
Considered the reason you gave in the comment to your question I suggest you to implement your own custom renderer for the pager.

In the faces-config.xml declare as follows:

<render-kit>
    <renderer>
        <component-family>com.ibm.xsp.Pager
        </component-family>
        <renderer-type>com.ibm.xsp.XPager
        </renderer-type>
        <renderer-class>com.irc.xsp.renderer.UIPagerRenderer
        </renderer-class>
    </renderer>
</render-kit>

Where com.irc.xsp.renderer.UIPagerRenderer is the rendering class. Obviously you can name it whatever you want. Just use the same name in both places.

The renderer could look something like this:

package com.irc.xsp.renderer;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.component.UIPager;
import com.ibm.xsp.component.UIPagerControl;
import com.ibm.xsp.component.xp.XspPager;
import com.ibm.xsp.component.xp.XspPagerControl;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.event.PagerEvent;
import com.ibm.xsp.extsn.ResourceHandler;
import com.ibm.xsp.renderkit.html_extended.XPagerRenderer;
import com.ibm.xsp.util.AjaxUtilEx;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.JavaScriptUtil;
import com.ibm.xsp.util.TypedUtil;

public class UIPagerRenderer extends XPagerRenderer {

    public static final String VAR_PAGE = "page"; //$NON-NLS-1$

    private enum Property {
        LIST_ITEM_CLASS, PAGER_LINK_CLASS, ACTIVE_CLASS("active"), DISABLED_CLASS("disabled");

        String className;

        Property() {
        }

        Property(String s) {
            this.className = s;
        }

        String getClassName() {
            return className;
        }

    }

    @Override
    public void decode(FacesContext context, UIComponent component) {
        super.decode(context, component);

        // check that this component cause the submit
        if (decodeCausedSubmit(context, component)) {
            PagerEvent pagerEvent = new PagerEvent(component);

            String hiddenValue = FacesUtil.getHiddenFieldValue(context);
            if (StringUtil.isNotEmpty(hiddenValue)) {
                int pos = hiddenValue.lastIndexOf('_');
                if (pos > -1) {
                    hiddenValue = hiddenValue.substring(pos + 1);
                    if (isFirst(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_FIRST);
                    } else if (isLast(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_LAST);
                    } else if (isNext(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_NEXT);
                    } else if (isPrevious(hiddenValue)) {
                        pagerEvent.setAction(PagerEvent.ACTION_PREVIOUS);
                    } else {
                        try {
                            int value = Integer.parseInt(hiddenValue);
                            pagerEvent.setAction(PagerEvent.ACTION_GOTOPAGE);
                            pagerEvent.setPage(value);
                        } catch (NumberFormatException nfe) {
                            return; // just don't queue the event
                        }
                    }
                } else {
                    return;
                }
            }
            ((UIPager) component).queueEvent(pagerEvent);
        }
    }

    private boolean decodeCausedSubmit(FacesContext context, UIComponent component) {
        String currentClientId = component.getClientId(context);
        String hiddenValue = FacesUtil.getHiddenFieldValue(context);

        if (currentClientId != null && hiddenValue != null) {
            return StringUtil.indexOfIgnoreCase(hiddenValue, currentClientId) > -1;
        }
        return false;
    }

    @Override
    public boolean getRendersChildren() {
        return true;
    }

    @Override
    public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
        if (context == null || component == null) {
            throw new IOException();
        }

        List<?> controls = component.getChildren();

        if (controls.isEmpty()) {
            return;
        }

        XspPager pager = (XspPager) component;
        UIPager.PagerState pagerState = ((UIPager) component).createPagerState();

        if (pagerState == null) {
            throw new FacesExceptionEx(ResourceHandler.getString("PagerRenderer.Pagerisnotassociatedwithanydataco"));
        }

        encodePagerContent(context, context.getResponseWriter(), pager, pagerState, controls, false);
    }

    protected void encodePagerContent(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, List<?> controls, boolean rtl) throws IOException {
        // Compute the pages that should be displayed
        int pageCount = pagerState.getPageCount();
        int startCount = getStartCount(pagerState, pageCount);
        int endCount = getEndCount(pagerState, pageCount, startCount);

        String pagerId = pager.getClientId(context);

        String pagerRole = pager.getRole();
        String pagerTitle = pager.getTitle();
        String pagerOuterClass = pager.getOuterStyleClass();
        String pagerAriaLabel = pager.getAriaLabel();
        boolean closeOuterTag = false;

        if (isAnyAssigned(pagerRole, pagerTitle, pagerOuterClass, pagerAriaLabel)) {
            writer.startElement("div", null); // $NON-NLS-1$

            writeNonNullAttributeOnly(writer, "role", pagerRole);
            writeNonNullAttributeOnly(writer, "title", pagerTitle);
            writeNonNullAttributeOnly(writer, "class", pagerOuterClass);
            writeNonNullAttributeOnly(writer, "aria-label", pagerAriaLabel);

            closeOuterTag = true;
        }

        writer.startElement("ul", null);

        writeNonNullAttributeOnly(writer, "class", pager.getStyleClass());
        writeNonNullAttributeOnly(writer, "id", pagerId);

        Iterator<?> it = controls.iterator();

        while (it.hasNext()) {
            Object obj = it.next();

            if (obj instanceof XspPagerControl) {
                XspPagerControl control = (XspPagerControl) obj;
                String type = control.getType();

                if (StringUtil.isNotEmpty(type)) {
                    if (isFirst(type) || isNext(type) || isPrevious(type)
                            || (isLast(type) && pager.isAlwaysCalculateLast())) {
                        encodeAction(context, writer, pager, pagerState, pagerId, control, type, startCount, endCount,
                                rtl);

                        continue;
                    } else if (isLast(type) && !pager.isAlwaysCalculateLast()) {
                        if (!pagerState.hasMoreRows()) {
                            encodeAction(context, writer, pager, pagerState, pagerId, control, type, startCount,
                                    endCount, rtl);
                        } else {
                            writer.startElement("li", null);
                            writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS
                                    .getClassName(), Property.DISABLED_CLASS.getClassName()));

                            writer.startElement("a", null);
                            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
                            writer.writeText(getMayBeMorePages(), null);
                            writer.endElement("a");

                            writer.endElement("li");
                        }

                        continue;
                    } else if (type.equalsIgnoreCase(UIPagerControl.TYPE_GROUP)) {
                        encodeGroup(context, writer, pager, pagerState, pagerId, control, startCount, endCount);

                        continue;
                    } else if (type.equalsIgnoreCase(UIPagerControl.TYPE_STATUS)) {
                        encodeStatus(context, writer, pager, pagerState, control, startCount, endCount);

                        continue;
                    } else if (isSeparator(type)) {
                        encodeSeparator(context, writer, control, type);

                        continue;
                    } else if (type.equalsIgnoreCase(UIPagerControl.TYPE_GOTO)) {
                        encodeGoto();

                        continue;
                    }
                }

                // "Unknown control type {0}"
                String msg = com.ibm.xsp.extsn.ResourceHandler.getString("PagerRenderer.Unknowncontroltype0"); //$NON-NLS-1$
                msg = StringUtil.format(msg, type);
                throw new FacesExceptionEx(msg);
            }
        }

        writer.endElement("ul");

        if (closeOuterTag) {
            writer.endElement("div");
        }
    }

    protected void encodeAction(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, String pagerId, XspPagerControl control, String type, int startCount,
            int endCount, boolean rtl) throws IOException {
        String controlId = pagerId + "__" + type;
        String defaultText = "";
        String ariaLabel = "";
        boolean renderLink = true;

        //TODO need to handle BIDI here for the unicode symbols
        if (isFirst(type)) {
            renderLink = pagerState.getCurrentPage() > startCount;
            // "\u00AB" FirstSymbol
            defaultText = "\u00AB"; //$NON-NLS-1$
            // "First page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.First"); //$NON-NLS-1$
        } else if (isPrevious(type)) {
            renderLink = pagerState.getCurrentPage() > startCount;
            // "\u2039" PreviousSymbol
            defaultText = "\u2039"; //$NON-NLS-1$
            // "Previous page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.Previous"); //$NON-NLS-1$
        } else if (isNext(type)) {
            renderLink = pagerState.getCurrentPage() < endCount - 1;
            // "\u203A" NextSymbol
            defaultText = "\u203A"; //$NON-NLS-1$;
            // "Next page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.Next"); //$NON-NLS-1$
        } else if (isLast(type)) {
            renderLink = pagerState.getCurrentPage() < endCount - 1;
            // "\u00BB" LastSymbol
            defaultText = "\u00BB"; //$NON-NLS-1$
            // "Last page"
            ariaLabel = ResourceHandler.getString("PagerRenderer.Last"); //$NON-NLS-1$
        }

        writer.startElement("li", null);
        String listItemClass = Property.LIST_ITEM_CLASS.getClassName();

        if (!renderLink) {
            //If current page is the first, disable first/previous pagers
            //and if current page is the last, disable last/next pagers
            listItemClass = com.irc.util.StringUtil.concat(listItemClass, Property.DISABLED_CLASS.getClassName());
        }

        writeNonNullAttributeOnly(writer, "class", listItemClass);

        // Generate the image link
        String val = (String) control.getValue();

        if (StringUtil.isEmpty(val)) {
            val = defaultText;
        }

        // Generate the text link
        if (StringUtil.isNotEmpty(val)) {
            writer.startElement("a", null); // $NON-NLS-1$

            if (renderLink) {
                writer.writeAttribute("aria-disabled", "false", null); // $NON-NLS-1$ $NON-NLS-2$
                writer.writeAttribute("href", "#", null); // $NON-NLS-1$ $NON-NLS-2$
            } else {
                //add a11y attributes
                writer.writeAttribute("aria-disabled", "true", null); // $NON-NLS-1$ $NON-NLS-2$
            }

            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());

            writer.writeAttribute("id", controlId + "__lnk", null); // $NON-NLS-1$ $NON-NLS-2$
            writer.writeAttribute("role", "button", null); // $NON-NLS-1$ $NON-NLS-2$
            writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$
            writer.writeText(val, null);
            writer.endElement("a"); // $NON-NLS-1$

            if (renderLink) {
                setupSubmitOnClick(context, pager, pagerState, controlId, controlId + "__lnk"); // $NON-NLS-1$
            }
        }

        writer.endElement("li"); // $NON-NLS-1$
    }

    protected void encodeGroup(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, String pagerId, XspPagerControl control, int startCount, int endCount)
            throws IOException {
        // Save the old page value
        Map<String, Object> requestMap = TypedUtil.getRequestMap(context.getExternalContext());
        Object oldPage = requestMap.get(VAR_PAGE);
        String controlId = pagerId + "__" + control.getType();//$NON-NLS-1$

        // Encode the pages
        for (int i = startCount; i < endCount; i++) {
            // Push the page number
            requestMap.put(VAR_PAGE, i + 1);
            boolean renderLink = (i != pagerState.getCurrentPage());

            writer.startElement("li", null); // $NON-NLS-1$
            String listItemClass = Property.LIST_ITEM_CLASS.getClassName();

            if (!renderLink) {
                listItemClass = com.irc.util.StringUtil.concat(listItemClass, Property.ACTIVE_CLASS.getClassName());
            }

            writeNonNullAttributeOnly(writer, "class", listItemClass);

            // Generate the image link
            String val = (String) control.getValue();

            if (StringUtil.isEmpty(val)) {
                val = Integer.toString(i + 1);
            }

            // Generate the text link
            if (StringUtil.isNotEmpty(val)) { // Generate the text link
                writer.startElement("a", control); //$NON-NLS-1$
                writer.writeAttribute("id", controlId + "__lnk__" + i, null); // $NON-NLS-1$ $NON-NLS-2$
                // "Page {0}"
                String ariaLabel = ResourceHandler.getString("PagerRenderer.Gotopage0"); //$NON-NLS-1$
                ariaLabel = StringUtil.format(ariaLabel, val);
                writer.writeAttribute("aria-label", ariaLabel, null); // $NON-NLS-1$
                writer.writeAttribute("role", "button", null); // $NON-NLS-1$ $NON-NLS-2$

                writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());

                if (renderLink) { //make sure the a is tab-able
                    writer.writeAttribute("tabindex", "0", null); // $NON-NLS-1$ $NON-NLS-2$
                    writer.writeAttribute("aria-pressed", "false", null); // $NON-NLS-1$ $NON-NLS-2$
                } else {
                    writer.writeAttribute("aria-pressed", "true", null); // $NON-NLS-1$ $NON-NLS-2$
                }

                writer.writeText(val, null);
                writer.endElement("a"); // $NON-NLS-1$

                if (renderLink) {
                    setupSubmitOnClick(context, pager, pagerState, controlId + "__lnk__" + i, controlId + "__lnk__" + i); // $NON-NLS-1$ $NON-NLS-2$
                }
            }

            writer.endElement("li"); // $NON-NLS-1$
        }

        // Encode after the pages
        if (!pager.isAlwaysCalculateLast()) {
            if (endCount < pagerState.getLastPage() || pagerState.hasMoreRows()) {
                writer.startElement("li", null); // $NON-NLS-1$
                writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
                        Property.DISABLED_CLASS.getClassName()));

                writer.startElement("a", control); //$NON-NLS-1$
                writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
                writer.writeText(getMayBeMorePages(), null);
                writer.endElement("a"); // $NON-NLS-1$
                writer.endElement("li"); // $NON-NLS-1$
            }
        }

        // Restore the old page value
        if (oldPage != null) {
            requestMap.put(VAR_PAGE, oldPage);
        } else {
            requestMap.remove(VAR_PAGE);
        }

    }

    protected void setupSubmitOnClick(FacesContext context, XspPager component, UIPager.PagerState st, String clientId,
            String sourceId) {
        boolean immediate = false;

        UIComponent subTree = ((FacesContextEx) context).getSubTreeComponent();

        boolean partialExec = component.isPartialExecute();
        String execId = null;
        if (partialExec) {
            execId = component.getClientId(context);
            immediate = true;
        } else {
            if (subTree != null) {
                partialExec = true;
                execId = subTree.getClientId(context);
                immediate = true;
            }
        }

        boolean partialRefresh = component.isPartialRefresh();
        String refreshId = null;
        if (partialRefresh) {
            UIComponent refreshComponent = component.findSharedDataPagerParent();
            if (null == refreshComponent) {
                refreshComponent = (UIComponent) st.getDataIterator();
            }
            refreshId = AjaxUtilEx.getRefreshId(context, refreshComponent);
        } else {
            if (subTree != null) {
                partialRefresh = true;
                refreshId = subTree.getClientId(context);
            }
        }

        // call some JavaScript in xspClient.js
        final String event = "onclick"; // $NON-NLS-1$
        // Note, the onClick event is also triggered if the user tabs to the
        // image\link and presses enter (Not just when clicked with a
        // mouse).

        // When the source is clicked, put its id in the hidden field and
        // submit the form.
        StringBuilder buff = new StringBuilder();
        if (partialRefresh) {
            JavaScriptUtil.appendAttachPartialRefreshEvent(buff, clientId, sourceId, execId, event,
            /* clientSideScriptName */null, immediate ? JavaScriptUtil.VALIDATION_NONE
                    : JavaScriptUtil.VALIDATION_FULL,
            /* refreshId */refreshId,
            /* onstart */getOnStart(component),
            /* oncomplete */getOnComplete(component),
            /* onerror */getOnError(component));
        } else {
            JavaScriptUtil.appendAttachEvent(buff, clientId, sourceId, execId, event,
            /* clientSideScriptName */null,
            /* submit */true, immediate ? JavaScriptUtil.VALIDATION_NONE : JavaScriptUtil.VALIDATION_FULL);
        }
        String script = buff.toString();

        // Add the script block we just generated.
        JavaScriptUtil.addScriptOnLoad(script);
    }

    protected String getOnStart(XspPager component) {
        return (String) component.getAttributes().get("onStart"); // $NON-NLS-1$
    }

    protected String getOnComplete(XspPager component) {
        return (String) component.getAttributes().get("onComplete"); // $NON-NLS-1$
    }

    protected String getOnError(XspPager component) {
        return (String) component.getAttributes().get("onError"); // $NON-NLS-1$
    }

    protected void encodeStatus(FacesContext context, ResponseWriter writer, XspPager pager,
            UIPager.PagerState pagerState, XspPagerControl control, int startCount, int endCount) throws IOException {
        writer.startElement("li", null); // $NON-NLS-1$
        writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
                Property.DISABLED_CLASS.getClassName()));

        String val = (String) control.getValue();

        if (StringUtil.isEmpty(val)) {
            val = "{0}"; // $NON-NLS-1$
        }

        if (pagerState.getLastPage() > 0) {
            writer.startElement("a", null); // $NON-NLS-1$
            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
            writer.writeAttribute("role", "button", null); // $NON-NLS-2$ $NON-NLS-1$
            val = StringUtil.format(val, pagerState.getCurrentPage() + 1, pagerState.getLastPage(), startCount,
                    endCount);
            writer.writeText(val, null);
            writer.endElement("a"); // $NON-NLS-1$
        }

        writer.endElement("li"); // $NON-NLS-1$
    }

    protected void encodeSeparator(FacesContext context, ResponseWriter writer, XspPagerControl control, String type)
            throws IOException {
        String val = (String) control.getValue();

        writer.startElement("li", null); // $NON-NLS-1$

        if (StringUtil.isEmpty(val)) {
            String defaultSeparator = "|"; // $NON-NLS-1$

            if (type.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATORPAGE)) {
                // "Page"
                defaultSeparator = com.ibm.xsp.extsn.ResourceHandler.getString("PagerRenderer.Page"); //$NON-NLS-1$
            }

            val = defaultSeparator;
        }

        writeNonNullAttributeOnly(writer, "class", com.irc.util.StringUtil.concat(Property.LIST_ITEM_CLASS.getClassName(),
                Property.DISABLED_CLASS.getClassName()));

        // Generate the text link
        if (StringUtil.isNotEmpty(val)) {
            writer.startElement("a", null); // $NON-NLS-1$
            writeNonNullAttributeOnly(writer, "class", Property.PAGER_LINK_CLASS.getClassName());
            writer.writeText(val, null);
            writer.endElement("a"); // $NON-NLS-1$
        }

        writer.endElement("li"); // $NON-NLS-1$
    }

    protected void encodeGoto() {
        // Do not exists in core XPages yet..
    }

    protected int getStartCount(UIPager.PagerState st, int pageCount) {
        int start = (st.getFirst() / st.getRows()) - pageCount / 2;
        start = Math.min(Math.max(0, st.getLastPage() - pageCount), Math.max(0, start));
        return start;
    }

    protected int getEndCount(UIPager.PagerState st, int pageCount, int start) {
        int sizeOfPageRange = Math.min(start + pageCount, st.getLastPage()) - start;
        int end = start + sizeOfPageRange;
        return end;
    }

    protected boolean isFirst(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_FIRST)
                || type.equalsIgnoreCase(UIPagerControl.TYPE_FIRSTARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_FIRSTIMAGE));
    }

    protected boolean isNext(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_NEXT) || type.equalsIgnoreCase(UIPagerControl.TYPE_NEXTARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_NEXTIMAGE));
    }

    protected boolean isLast(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_LAST) || type.equalsIgnoreCase(UIPagerControl.TYPE_LASTARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_LASTIMAGE));
    }

    protected boolean isPrevious(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUS)
                || type.equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUSARROW) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_PREVIOUSIMAGE));
    }

    protected boolean isSeparator(String type) {
        return (type.equalsIgnoreCase(UIPagerControl.TYPE_SEPARATOR) || type
                .equalsIgnoreCase(UIPagerControl.TYPE_SEPARATORPAGE));
    }

    protected String getMayBeMorePages() {
        return "..."; // $NON-NLS-1$
    }

    private void writeNonNullAttributeOnly(ResponseWriter writer, String attributeName, String attributeValue)
            throws IOException {
        if (attributeValue != null && !attributeValue.isEmpty()) {
            writer.writeAttribute(attributeName, attributeValue, null);
        }
    }

    private boolean isAnyAssigned(String... objs) {
        for (String s : objs) {
            if (s != null && !s.isEmpty())
                return true;
        }

        return false;
    }

}

That is a class I've been using in my last project. Basically it's re-implemeting everything that the pager does in order to have control over the look and feel of the component - it's called renderer after all. You could try to tweak it here and there to see how the various things work out on the page. It's a learning process.


来源: stackexchange

问:
how to display output in the following format in unix
Hi my data looks in the below format in unix file

BENEFITS-T_FACT_DEPNDT_COVRG-230

BENEFITS-T_FACT_DEPNDT_COVRG-290

BENEFITS-T_FACT_ELECTN-0

BENEFITS-T_FACT_ELECTN-39092

HR-T_DIM_CLNT-0

HR-T_DIM_CLNT-98

HR-T_DIM_CMPNY-10

HR-T_DIM_CMPNY-45

I need to display the output like this:

domain -tablename- before load cnt: 230 after load cnt :290

ex : BENEFITS - T_FACT_DEPNDT_COVRG -before load cnt: 230 after load cnt :290

```<code> HR -T_DIM_CLNT -before load cnt: 0 after load cnt :98
</code>```

Can someone please suggest an answer in unix ???

答:
Is this enough?</p>

```<code>awk -F  "-" '{if(NR%2){printf $1 " " $2 " -before load cnt: " $3}else{printf " after load cnt: " $3 "\n"}}' filename
</code>```

ContentLicense: CC BY-SA 3.0

Body:
Hi my data looks in the below format in unix file

BENEFITS-T_FACT_DEPNDT_COVRG-230

BENEFITS-T_FACT_DEPNDT_COVRG-290

BENEFITS-T_FACT_ELECTN-0

BENEFITS-T_FACT_ELECTN-39092

HR-T_DIM_CLNT-0

HR-T_DIM_CLNT-98

HR-T_DIM_CMPNY-10

HR-T_DIM_CMPNY-45

I need to display the output like this:

domain -tablename- before load cnt: 230 after load cnt :290

ex : BENEFITS - T_FACT_DEPNDT_COVRG -before load cnt: 230 after load cnt :290

 HR -T_DIM_CLNT -before load cnt: 0 after load cnt :98

Can someone please suggest an answer in unix ???

Body:
Is this enough?

awk -F  "-" '{if(NR%2){printf $1 " " $2 " -before load cnt: " $3}else{printf " after load cnt: " $3 "\n"}}' filename


问:
HTTP server with Ruby
I am trying to make a small HTTP server in Ruby. Its just meant to learn how stuff works, nothing big. So what i did is to send the server an ajax request. The server is listening on port 2000, and so the ajax request is also on port 2000.

The problem i am facing is that the ajax request is returned only with the headers, the content is missing. I tried everything i could find, but it seems to fail too...

I have attached the code, for you to take a look

```<code>require 'socket'               # Get sockets from stdlib
server = TCPServer.new(2000)  # Socket to listen on port 2000
loop {                         # Servers run forever
  client = server.accept       # Wait for a client to connect
  headers = "HTTP/1.1 200 OK\r\nDate: Tue, 14 Dec 2010 10:48:45 GMT\r\nServer: Ruby\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n"
  client.puts headers  # Send the time to the client
  client.puts "<html>amit</html>"
  client.close                 # Disconnect from the client
}
</code>```

The ajax request is working when pointed to a PHP script running on Apache. the only problem seems to occur when using this server.

Any help is as always, deeply appreciated :)

Regards,
Amit

答:
Your code works fine.

```<code>$ telnet localhost 2000
HTTP/1.1 200 OK
Date: Tue, 14 Dec 2010 10:48:45 GMT
Server: Ruby
Content-Type: text/html; charset=iso-8859-1

<html>amit</html>

Connection to host lost.
</code>```

Now you'll have to find out what's wrong with your AJAX request...

ContentLicense: CC BY-SA 2.5

Title: HTTP server with Ruby

Body:
I am trying to make a small HTTP server in Ruby. Its just meant to learn how stuff works, nothing big. So what i did is to send the server an ajax request. The server is listening on port 2000, and so the ajax request is also on port 2000.

The problem i am facing is that the ajax request is returned only with the headers, the content is missing. I tried everything i could find, but it seems to fail too...

I have attached the code, for you to take a look

require 'socket'               # Get sockets from stdlib
server = TCPServer.new(2000)  # Socket to listen on port 2000
loop {                         # Servers run forever
  client = server.accept       # Wait for a client to connect
  headers = "HTTP/1.1 200 OK\r\nDate: Tue, 14 Dec 2010 10:48:45 GMT\r\nServer: Ruby\r\nContent-Type: text/html; charset=iso-8859-1\r\n\r\n"
  client.puts headers  # Send the time to the client
  client.puts "amit"
  client.close                 # Disconnect from the client
}

The ajax request is working when pointed to a PHP script running on Apache. the only problem seems to occur when using this server.

Any help is as always, deeply appreciated :)

Regards,
Amit

Body:
Your code works fine.

$ telnet localhost 2000
HTTP/1.1 200 OK
Date: Tue, 14 Dec 2010 10:48:45 GMT
Server: Ruby
Content-Type: text/html; charset=iso-8859-1

<html>amit</html>

Connection to host lost.

Now you'll have to find out what's wrong with your AJAX request...


问:
Google Line Chart set value for no data to 0
I want integrate a google line chart on my raspberry pi terminal to show some statistics about my coffee consumption. If my json have got no value for a date, the line chart should set the value to 0. At the moment, dates with no values have got the value of the day before. Any ideas?

I have used this configuration:

```<code>let options = {
                hAxis: {
                    format: 'd.M.yy',
                    gridlines: {count: 15},

                },
                vAxis: {
                    title: 'Cups of Coffee',

                },
                colors: ['#34495e'],
                interpolateNulls : true

            };
</code>```

Dates with no value are not displayed in my json. For example: no entry for the date 3.6.2017 Here is the json:

```<code>[{
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 9,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-09T06:41:50.904Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 8,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-08T05:44:04.081Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 7,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-07T06:10:01.713Z",
    "count": 4
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 6,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-06T05:52:09.775Z",
    "count": 2
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 2,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-02T06:03:47.243Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 1,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-01T05:37:31.399Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 5,
      "day": 31,
      "action": "Coffee made"
    },
    "createdAt": "2017-05-31T05:18:49.220Z",
    "count": 1
  }
]
</code>```

Current line chart output (The values of date 2. Jun to 5. June should be 0)

答:
just need to add a row for the missing dates...

use data table method getFilteredRows to check data for a certain day

see following working snippet...

the json is loaded, then starting with the min date in the data,

and ending with the current date, each day is checked for data

if now rows are found, one is added with value of 0

google.charts.load('current', {
  callback: function () {
    drawChart();
    window.addEventListener('resize', drawChart, false);
  },
  packages:['corechart', 'table']
});

function drawChart() {
  var jsonData = [{
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 9,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-09T06:41:50.904Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 8,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-08T05:44:04.081Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 7,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-07T06:10:01.713Z",
      "count": 4
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 6,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-06T05:52:09.775Z",
      "count": 2
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 2,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-02T06:03:47.243Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 1,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-01T05:37:31.399Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 5,
        "day": 31,
        "action": "Coffee made"
      },
      "createdAt": "2017-05-31T05:18:49.220Z",
      "count": 1
    }
  ];

  var datePattern = 'd.M.yy';
  var formatDate = new google.visualization.DateFormat({
    pattern: datePattern
  });

  var dataTable = new google.visualization.DataTable({
    "cols": [
      {"label": "Date", "type": "date"},
      {"label": "Cups of Coffee", "type":"number"}
    ]
  });

  jsonData.forEach(function (row) {
    dataTable.addRow([
      new Date(row.createdAt),
      row.count
    ]);
  });

  var startDate = dataTable.getColumnRange(0).min;
  var endDate = new Date();
  var oneDay = (1000 * 60 * 60 * 24);
  for (var i = startDate.getTime(); i < endDate.getTime(); i = i + oneDay) {
    var coffeeData = dataTable.getFilteredRows([{
      column: 0,
      test: function (value, row, column, table) {
        var coffeeDate = formatDate.formatValue(table.getValue(row, column));
        var testDate = formatDate.formatValue(new Date(i));
        return (coffeeDate === testDate);
      }
    }]);
    if (coffeeData.length === 0) {
      dataTable.addRow([
        new Date(i),
        0
      ]);
    }
  }
  dataTable.sort({column: 0});

  var chartLine = new google.visualization.ChartWrapper({
    chartType: 'LineChart',
    containerId: 'chart',
    dataTable: dataTable,
    options: {
      theme: 'material',
      legend: {
        position: 'none',
      },
      chartArea: {
        top: 12,
        right: 12,
        bottom: 48,
        left: 48,
        height: '100%',
        width: '100%'
      },
      colors: ['#34495e'],
      hAxis: {
        format: datePattern,
        gridlines: {
          count: 15
        },
      },
      pointSize: 4,
      vAxis: {
        title: 'Cups of Coffee',
      }
    }
  });
  chartLine.draw();
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div></code>```

Body:
I want integrate a google line chart on my raspberry pi terminal to show some statistics about my coffee consumption. If my json have got no value for a date, the line chart should set the value to 0. At the moment, dates with no values have got the value of the day before. Any ideas?

I have used this configuration:

let options = {
                hAxis: {
                    format: 'd.M.yy',
                    gridlines: {count: 15},

                },
                vAxis: {
                    title: 'Cups of Coffee',

                },
                colors: ['#34495e'],
                interpolateNulls : true

            };

Dates with no value are not displayed in my json. For example: no entry for the date 3.6.2017 Here is the json:

[{
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 9,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-09T06:41:50.904Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 8,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-08T05:44:04.081Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 7,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-07T06:10:01.713Z",
    "count": 4
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 6,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-06T05:52:09.775Z",
    "count": 2
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 2,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-02T06:03:47.243Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 6,
      "day": 1,
      "action": "Coffee made"
    },
    "createdAt": "2017-06-01T05:37:31.399Z",
    "count": 1
  },
  {
    "_id": {
      "year": 2017,
      "month": 5,
      "day": 31,
      "action": "Coffee made"
    },
    "createdAt": "2017-05-31T05:18:49.220Z",
    "count": 1
  }
]

Current line chart output (The values of date 2. Jun to 5. June should be 0)

Body:
just need to add a row for the missing dates...

use data table method getFilteredRows to check data for a certain day

see following working snippet...

the json is loaded, then starting with the min date in the data,

and ending with the current date, each day is checked for data

if now rows are found, one is added with value of 0

google.charts.load('current', {
  callback: function () {
    drawChart();
    window.addEventListener('resize', drawChart, false);
  },
  packages:['corechart', 'table']
});

function drawChart() {
  var jsonData = [{
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 9,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-09T06:41:50.904Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 8,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-08T05:44:04.081Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 7,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-07T06:10:01.713Z",
      "count": 4
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 6,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-06T05:52:09.775Z",
      "count": 2
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 2,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-02T06:03:47.243Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 6,
        "day": 1,
        "action": "Coffee made"
      },
      "createdAt": "2017-06-01T05:37:31.399Z",
      "count": 1
    },
    {
      "_id": {
        "year": 2017,
        "month": 5,
        "day": 31,
        "action": "Coffee made"
      },
      "createdAt": "2017-05-31T05:18:49.220Z",
      "count": 1
    }
  ];

  var datePattern = 'd.M.yy';
  var formatDate = new google.visualization.DateFormat({
    pattern: datePattern
  });

  var dataTable = new google.visualization.DataTable({
    "cols": [
      {"label": "Date", "type": "date"},
      {"label": "Cups of Coffee", "type":"number"}
    ]
  });

  jsonData.forEach(function (row) {
    dataTable.addRow([
      new Date(row.createdAt),
      row.count
    ]);
  });

  var startDate = dataTable.getColumnRange(0).min;
  var endDate = new Date();
  var oneDay = (1000 * 60 * 60 * 24);
  for (var i = startDate.getTime(); i < endDate.getTime(); i = i + oneDay) {
    var coffeeData = dataTable.getFilteredRows([{
      column: 0,
      test: function (value, row, column, table) {
        var coffeeDate = formatDate.formatValue(table.getValue(row, column));
        var testDate = formatDate.formatValue(new Date(i));
        return (coffeeDate === testDate);
      }
    }]);
    if (coffeeData.length === 0) {
      dataTable.addRow([
        new Date(i),
        0
      ]);
    }
  }
  dataTable.sort({column: 0});

  var chartLine = new google.visualization.ChartWrapper({
    chartType: 'LineChart',
    containerId: 'chart',
    dataTable: dataTable,
    options: {
      theme: 'material',
      legend: {
        position: 'none',
      },
      chartArea: {
        top: 12,
        right: 12,
        bottom: 48,
        left: 48,
        height: '100%',
        width: '100%'
      },
      colors: ['#34495e'],
      hAxis: {
        format: datePattern,
        gridlines: {
          count: 15
        },
      },
      pointSize: 4,
      vAxis: {
        title: 'Cups of Coffee',
      }
    }
  });
  chartLine.draw();
}

<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>


问:
Save many to many relation via view model in entity framework 6
I have two entities (TaskStep and Role) with many to many relation, I use a view model (named StepViewModel) to display taskstep and it's allowed Roles, StepViewModel is :

```<code>  public class StepViewModel
{
    public StepViewModel()
    {

    }
    public TaskStep TaskStep { get; set; }

    public virtual ICollection<AllowedRole> AllowedRole { get; set; }

}
</code>```

and I use this method to display taskstep:

```<code> public static StepViewModel GetTaskStepData(int stepId)
    {
        using (var context = new WFContext())
        {

            return context.TaskSteps.Where(ts => ts.ID == stepId).Include(ts => ts.Roles)
                .Select(ts => new StepViewModel()
                {
                    AllowedRole = context.Roles.Select(r => new AllowedRole()
                    {
                        Role = r,
                        Allowed = ts.Roles.Select(x => x.ID).Contains(r.ID)
                    }).ToList(),
                    TaskStep = ts
                })
                .SingleOrDefault();
        }
    }
</code>```

but when I save the taskstep, it's roles changes don't save. My save method is :

```<code>public static void SaveTaskStep(StepViewModel stepViewModel)
    {
        using (var context = new WorkFlowContext())
        {

            //TaskStep taskStep = context.TaskSteps.Where(ts => ts.ID == stepViewModel.TaskStep.ID).Include(ts => ts.Roles).SingleOrDefault();

            TaskStep taskStep = stepViewModel.TaskStep;

            List<int> aRolesIDs = stepViewModel.AllowedRole.Where(ar => ar.Allowed == true).Select(ar => ar.Role.ID).ToList();
            List<Role> roles = context.Roles.Where(r => aRolesIDs.Contains(r.ID)).Include(r => r.AllowedTaskSteps).ToList();

            taskStep.Roles.Clear();
            foreach (Role role in roles)
            {
                if (context.Entry(role).State == EntityState.Detached)
                    context.Roles.Attach(role);

                taskStep.Roles.Add(role);
            }

            if (taskStep.ID == 0)
                context.TaskSteps.Add(taskStep);
            else
                context.Entry(taskStep).State = EntityState.Modified;

            context.SaveChanges();
        }
    }
</code>```

if I get taskstep from db with include roles (like first -comment- line in save method), it work's well. but in this way, I have to make all taskstep field's changes manually. In addition when I assign taskstep to stepviewmodel,I use "include" but it's still doesn't work.what can I do to save stepViewModel roles?

答:
context.TaskSteps.Attach(taskStep) should work in your case

SaveTaskStep method:

```<code>using (var context = new WorkFlowContext())
{
  TaskStep taskStep = stepViewModel.TaskStep;
  if (taskStep.ID == 0)
    context.TaskSteps.Add(taskStep);
  else
    context.TaskSteps.Attach(taskStep); //from this point EF start change tracking

//I just copy-pasted this part your code assuming it's correct
  List<int> aRolesIDs = stepViewModel.AllowedRole.Where(ar => ar.Allowed == true).Select(ar => ar.Role.ID).ToList();
  List<Role> roles = context.Roles.Where(r => aRolesIDs.Contains(r.ID)).Include(r => r.AllowedTaskSteps).ToList();

  taskStep.Roles.Clear();
  foreach (Role role in roles)
  {
     if (context.Entry(role).State == EntityState.Detached)
       context.Roles.Attach(role);

       taskStep.Roles.Add(role);
  }

  context.SaveChanges();
} // end of using (var context
</code>```

Body:
I have two entities (TaskStep and Role) with many to many relation, I use a view model (named StepViewModel) to display taskstep and it's allowed Roles, StepViewModel is :

  public class StepViewModel
{
    public StepViewModel()
    {

    }
    public TaskStep TaskStep { get; set; }

    public virtual ICollection<AllowedRole> AllowedRole { get; set; }

}

and I use this method to display taskstep:

 public static StepViewModel GetTaskStepData(int stepId)
    {
        using (var context = new WFContext())
        {

            return context.TaskSteps.Where(ts => ts.ID == stepId).Include(ts => ts.Roles)
                .Select(ts => new StepViewModel()
                {
                    AllowedRole = context.Roles.Select(r => new AllowedRole()
                    {
                        Role = r,
                        Allowed = ts.Roles.Select(x => x.ID).Contains(r.ID)
                    }).ToList(),
                    TaskStep = ts
                })
                .SingleOrDefault();
        }
    }

but when I save the taskstep, it's roles changes don't save. My save method is :

public static void SaveTaskStep(StepViewModel stepViewModel)
    {
        using (var context = new WorkFlowContext())
        {

            //TaskStep taskStep = context.TaskSteps.Where(ts => ts.ID == stepViewModel.TaskStep.ID).Include(ts => ts.Roles).SingleOrDefault();

            TaskStep taskStep = stepViewModel.TaskStep;

            List<int> aRolesIDs = stepViewModel.AllowedRole.Where(ar => ar.Allowed == true).Select(ar => ar.Role.ID).ToList();
            List<Role> roles = context.Roles.Where(r => aRolesIDs.Contains(r.ID)).Include(r => r.AllowedTaskSteps).ToList();

            taskStep.Roles.Clear();
            foreach (Role role in roles)
            {
                if (context.Entry(role).State == EntityState.Detached)
                    context.Roles.Attach(role);

                taskStep.Roles.Add(role);
            }

            if (taskStep.ID == 0)
                context.TaskSteps.Add(taskStep);
            else
                context.Entry(taskStep).State = EntityState.Modified;

            context.SaveChanges();
        }
    }

if I get taskstep from db with include roles (like first -comment- line in save method), it work's well. but in this way, I have to make all taskstep field's changes manually. In addition when I assign taskstep to stepviewmodel,I use "include" but it's still doesn't work.what can I do to save stepViewModel roles?

Body:
context.TaskSteps.Attach(taskStep) should work in your case

SaveTaskStep method:

using (var context = new WorkFlowContext())
{
  TaskStep taskStep = stepViewModel.TaskStep;
  if (taskStep.ID == 0)
    context.TaskSteps.Add(taskStep);
  else
    context.TaskSteps.Attach(taskStep); //from this point EF start change tracking

//I just copy-pasted this part your code assuming it's correct
  List<int> aRolesIDs = stepViewModel.AllowedRole.Where(ar => ar.Allowed == true).Select(ar => ar.Role.ID).ToList();
  List<Role> roles = context.Roles.Where(r => aRolesIDs.Contains(r.ID)).Include(r => r.AllowedTaskSteps).ToList();

  taskStep.Roles.Clear();
  foreach (Role role in roles)
  {
     if (context.Entry(role).State == EntityState.Detached)
       context.Roles.Attach(role);

       taskStep.Roles.Add(role);
  }

  context.SaveChanges();
} // end of using (var context


问:
How can I deal with @objc inference deprecation with #selector() in Swift 4?
I'm trying to convert my project's source code from Swift 3 to Swift 4. One warning Xcode is giving me is about my selectors.

For instance, I add a target to a button using a regular selector like this:

```<code>button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)
</code>```

This is the warning it shows:

Argument of '#selector' refers to instance method 'myAction()' in 'ViewController' that depends on '@objc' attribute inference deprecated in Swift 4

Add '@objc' to expose this instance method to Objective-C

Now, hitting Fix on the error message does this to my function:

```<code>// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }
</code>```

I don't really want to rename all of my functions to include the @objc mark and I'm assuming that's not necessary.

How do I rewrite the selector to deal with the deprecation?

Related question:

The use of Swift 3 @objc inference in Swift 4 mode is deprecated?

答:
As Apple Official Documentation. you need to use @objc to call your Selector Method.

In Objective-C, a selector is a type that refers to the name of an
  Objective-C method. In Swift, Objective-C selectors are represented by
  the Selector structure, and can be constructed using the #selector
  expression. To create a selector for a method that can be called from
  Objective-C, pass the name of the method, such as
  #selector(MyViewController.tappedButton(sender:)). To construct a selector for a property’s Objective-C getter or setter method, pass
  the property name prefixed by the getter: or setter: label, such as
  #selector(getter: MyViewController.myButton).

ContentLicense: CC BY-SA 4.0

Body:
I'm trying to convert my project's source code from Swift 3 to Swift 4. One warning Xcode is giving me is about my selectors.

For instance, I add a target to a button using a regular selector like this:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

This is the warning it shows:

Argument of '#selector' refers to instance method 'myAction()' in 'ViewController' that depends on '@objc' attribute inference deprecated in Swift 4

Add '@objc' to expose this instance method to Objective-C

Now, hitting Fix on the error message does this to my function:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

I don't really want to rename all of my functions to include the @objc mark and I'm assuming that's not necessary.

How do I rewrite the selector to deal with the deprecation?

Related question:

The use of Swift 3 @objc inference in Swift 4 mode is deprecated?


问:
Why are the input boxes completely overflowing their bounding divs?
I intend to line up 4 input text boxes housed in 4 equal sized columns in one row of the form. Making use of Bootstrap's grid column classes, I assigned  col--3 for medium and large screen sizes to those four input divs.
Likewise for small and extra small sizes, I assigned them col--12  so that the inputs occupy their own whole row. Then my guide wanted those input boxes sizes reduced. I added offset class to the four input and reduced their column span to 6 columns. These were centered appropriately.

However, this change screwed up my small screen layout. Now there are only two inputs in each row, while the other two in the next row.

/* button modification*/

#button-wrapper {
  width: 40%;
  min-width: 200px;
  margin: 0 auto;
}

#ebook-download-button {
  margin: auto 0.5em;
}

.inner {
  margin: 0 auto 0 auto;
  width: 150px;
  padding: 0;
  /* center the block element*/
}

img {
  min-width: 100%;
}

input {
  height: 60%;
  margin: 0px;
}

/* square boxes*/

.square-elem {
  border-radius: 0px;
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />

<body>

  <form id="registration-form" class="">
    <!--<div class="col-md-12 col-sm-12">-->
    <div class="row">
      <div class="col-lg-3 col-md-3 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3 ">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="first-name" placeholder="First Name">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="last-name" placeholder="Last Name">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="company" placeholder="Company">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="email" placeholder="Email">
        </div>
      </div>
    </div>
    <div class="row">
      <div id="button-wrapper" style="">
        <div id="ebook-download-button" class="col-md-12 col-sm-12 col-xs-12 text-center">
           DOWNLOAD EBOOK
        </div>
      </div>
    </div>
    <!-- </div>
  -->
  </form>

</body></code>```

Here's the JSFiddle(Drag the frame) too. It seems the inputs(in each form group div) have completely positioned outside their bounding div box.The intended output should be the four inputs lined side by side in one row.
What can possibly be the issue here?

答:
If your problem is, the offset is still in larger screens,You have to reverse the offsetting for each of the other col-classes .Give col-md-offset-0 to the divs where you specified offset for smaller device

/* button modification*/

#button-wrapper {
  width: 40%;
  min-width: 200px;
  margin: 0 auto;
}

#ebook-download-button {
  margin: auto 0.5em;
}

.inner {
  margin: 0 auto 0 auto;
  width: 150px;
  padding: 0;
  /* center the block element*/
}

img {
  min-width: 100%;
}

input {
  height: 60%;
  margin: 0px;
}

/* square boxes*/

.square-elem {
  border-radius: 0px;
}```
<pre class="snippet-code-html lang-html prettyprint-override"><code><link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />

<body>

  <form id="registration-form" class="">
    <!--<div class="col-md-12 col-sm-12">-->
    <div class="row">
      <div class="col-lg-3 col-md-3 col-md-offset-0 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3 ">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="first-name" placeholder="First Name">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-md-offset-0 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="last-name" placeholder="Last Name">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-md-offset-0 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="company" placeholder="Company">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-md-offset-0 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="email" placeholder="Email">
        </div>
      </div>
    </div>
    <div class="row">
      <div id="button-wrapper" style="">
        <div id="ebook-download-button" class="col-md-12 col-sm-12 col-xs-12 text-center">
           DOWNLOAD EBOOK
        </div>
      </div>
    </div>
    <!-- </div>
   -->
  </form>

</body></code>```

Body:
I intend to line up 4 input text boxes housed in 4 equal sized columns in one row of the form. Making use of Bootstrap's grid column classes, I assigned  col--3 for medium and large screen sizes to those four input divs.
Likewise for small and extra small sizes, I assigned them col--12  so that the inputs occupy their own whole row. Then my guide wanted those input boxes sizes reduced. I added offset class to the four input and reduced their column span to 6 columns. These were centered appropriately.

However, this change screwed up my small screen layout. Now there are only two inputs in each row, while the other two in the next row.

/* button modification*/

#button-wrapper {
  width: 40%;
  min-width: 200px;
  margin: 0 auto;
}

#ebook-download-button {
  margin: auto 0.5em;
}

.inner {
  margin: 0 auto 0 auto;
  width: 150px;
  padding: 0;
  /* center the block element*/
}

img {
  min-width: 100%;
}

input {
  height: 60%;
  margin: 0px;
}

/* square boxes*/

.square-elem {
  border-radius: 0px;
}

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />

  <form id="registration-form" class="">

          <input type="input" class="form-control square-elem" id="first-name" placeholder="First Name">

          <input type="input" class="form-control square-elem" id="last-name" placeholder="Last Name">

          <input type="input" class="form-control square-elem" id="company" placeholder="Company">

          <input type="input" class="form-control square-elem" id="email" placeholder="Email">

           DOWNLOAD EBOOK

  </form>

Here's the JSFiddle(Drag the frame) too. It seems the inputs(in each form group div) have completely positioned outside their bounding div box.The intended output should be the four inputs lined side by side in one row.
What can possibly be the issue here?

Body:
If your problem is, the offset is still in larger screens,You have to reverse the offsetting for each of the other col-classes .Give col-md-offset-0 to the divs where you specified offset for smaller device

/* button modification*/

#button-wrapper {
  width: 40%;
  min-width: 200px;
  margin: 0 auto;
}

#ebook-download-button {
  margin: auto 0.5em;
}

.inner {
  margin: 0 auto 0 auto;
  width: 150px;
  padding: 0;
  /* center the block element*/
}

img {
  min-width: 100%;
}

input {
  height: 60%;
  margin: 0px;
}

/* square boxes*/

.square-elem {
  border-radius: 0px;
}

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet" />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />

<body>

  <form id="registration-form" class="">
    <!--<div class="col-md-12 col-sm-12">-->
    <div class="row">
      <div class="col-lg-3 col-md-3 col-md-offset-0 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3 ">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="first-name" placeholder="First Name">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-md-offset-0 col-sm-6 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="last-name" placeholder="Last Name">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-md-offset-0 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="company" placeholder="Company">
        </div>
      </div>

      <div class="col-lg-3 col-md-3 col-sm-6 col-md-offset-0 col-sm-offset-3 col-xs-6 col-xs-offset-3">
        <div class="form-group">
          <input type="input" class="form-control square-elem" id="email" placeholder="Email">
        </div>
      </div>
    </div>
    <div class="row">
      <div id="button-wrapper" style="">
        <div id="ebook-download-button" class="col-md-12 col-sm-12 col-xs-12 text-center">
          <a href="#" role="button" class="btn btn-danger btn-download form-control square-elem" value=""> DOWNLOAD EBOOK </a>
        </div>
      </div>
    </div>
    <!-- </div>
   -->
  </form>

</body>


问:
How to display records based on selected year from dropdown dynamically without any submit button
I am working on PHP code-igniter.

My view code goes like below:

```<code><form method="post" action="">
    <div class="col-md-4 col-sm-4 ">
        <div class="form-group">
            <label class="h5" for="Mode"><b>See year wise commitments</b></label>
            <select class="form-control" name="Year" id="Year"  onchange="this.form.submit()">
                <option value="">Select Year</option>
                <option value="2015">2015</option>
                <option value="2016">2016</option>
                <option value="2017">2017</option>
            </select>
        </div>
    </div>
</form>
</code>```

Just by selecting any year from dropdown, I have to pass the selected year to my controller and inturn to model and get the query results based on the selected year.

In my controller I am receiving the posted value like:

```<code>$selectedYear = $this->input->post('Year');
</code>```

And before selecting any dropdown value the records in my view has to be of Current year.

My ajax code is

```<code>$( "#year" ).change(function() {
    year = $(this).val();
    $.ajax({
          url : "controllerfilename/controllername",
          data : {"year": year},
          type : "post",
          success: function(data){
             console.log(data);
          }
   });
</code>```

});

答:
You can make an ajax call to your controller on change function of dropdown</p>

```<code>    $( "#year" ).change(function() {
        year = $(this).val();
        $.ajax({
              url : "controller url",
              data : {"year": year},
              type : "post",
              success: function(data){
                 console.log(data);
              }
       });
   });
</code>```

Body:
I am working on PHP code-igniter.

My view code goes like below:

<form method="post" action="">

            See year wise commitments
            <select class="form-control" name="Year" id="Year"  onchange="this.form.submit()">
                <option value="">Select Year</option>
                <option value="2015">2015</option>
                <option value="2016">2016</option>
                <option value="2017">2017</option>
            </select>

</form>

Just by selecting any year from dropdown, I have to pass the selected year to my controller and inturn to model and get the query results based on the selected year.

In my controller I am receiving the posted value like:

$selectedYear = $this->input->post('Year');

And before selecting any dropdown value the records in my view has to be of Current year.

My ajax code is

$( "#year" ).change(function() {
    year = $(this).val();
    $.ajax({
          url : "controllerfilename/controllername",
          data : {"year": year},
          type : "post",
          success: function(data){
             console.log(data);
          }
   });

});

Body:
You can make an ajax call to your controller on change function of dropdown

    $( "#year" ).change(function() {
        year = $(this).val();
        $.ajax({
              url : "controller url",
              data : {"year": year},
              type : "post",
              success: function(data){
                 console.log(data);
              }
       });
   });


问:
Rails Application fails with 500 for any page on Home Controller - Heroku Only
I am having troubles figuring out what is wrong for this. My app is working perfectly in development on cloud9, but my production app on Heroku is not working. Any page I try to load from HomeController throws up the 500 error, so I imagine it has something to do with a before action for that controller, but I removed it and still get the error.
I have run bundle install, I have run rake db:migrate.
I'm not really sure what else I am missing. Here is my code for the HomeController. When I visit a page linked to an action on my "ProductController" everything works fine, no error.

EDIT

Removed my code because it wasn't related in the end.

答:
You probably forgot migrations, which can explain why it fails everywhere.
Run:

```<code>heroku run rake db:migrate
</code>```

If it doesn't change, check the logs while refreshing the page:

```<code>heroku logs -t
</code>```

You'll see the error.
Other explanation: you forgot to add API keys in the env variables on heroku.

Body:
You probably forgot migrations, which can explain why it fails everywhere.
Run:

heroku run rake db:migrate

If it doesn't change, check the logs while refreshing the page:

heroku logs -t

You'll see the error.
Other explanation: you forgot to add API keys in the env variables on heroku.


问:
Including Delphi Type Libraries in repository (SVN)?
What is the best way to save a Delphi Type Library in SVN.

The file changes every time you compile the Application

The file is not saved in a readable form of  AscII

It is very difficult to work out what changes have been made from one version to the next

This is a  major problem when more than one person is changing the file

QUESTIONS:

1) Should one save an exported ‘IDL’ file in the version control ?

2) Can one covert an ‘IDL ’ into a Delphi type library If so how ?

3) What are the best practices when working with a Delphi type library ?

4) How easy is it get rid of the type library in Delphi2009 datasnap project?

p.s. I am using BDS2006

答: There are some good answers to this question at Delphi and COM: TLB and maintenance issues.</p>

OwnerDisplayName: Charles Faiga

LastEditorDisplayName: Charles Faiga

OwnerDisplayName: Gustavo


问:
How can I change the size of a CustomItem inside a form in J2ME?
How can I change the size of a CustomItem inside a form in J2ME? I created a CustomItem (derived  class of javax.microedition.lcdui.CustomItem) and placed it inside a form (of derived class of javax.microedition.lcdui.Form). After some event triggers, I want to change the CustomItem's height and width. How should I change my code?

答: I got the solution. We should create a new object for the new CustomItem with new properties.Then delete the existing CustomItem from the form and add the new CustomItem inside the form.</p>