GloomyPagerBundle icon indicating copy to clipboard operation
GloomyPagerBundle copied to clipboard

Detailed documentation

Open pmithrandir opened this issue 12 years ago • 19 comments

Hello,

I'm trying to discover your Bundle that seems to do just what I need.

I read the documentation, but I think you could improve it by giving more example.

What I'm looking for :

  • An example or some explanation about using the bunddle in addintion with Ajax call.(jquery lib for example... what url call, how to link them, ...)
  • is it possible to create filter from foreign key value with a dropdownbox ?
  • Can we remove some columns in datagrid ? I have some column that are not mean to be seen by users.
  • How can we execute a custom query. I want to display value that where validated by the admin only. Now, I show the entire table. When I tried to put an object creating from a queryBuilder, I got an error saying that some datas where not find.

"An exception has been thrown during the rendering of a template ("Notice: Undefined index: slug in ...."

I think the Bundle seems promising, but this lack of documentation create difficulties that could be easily passed with your help.

Also, I think your demo website is down.

Please receive my thanks for your module. If you find some time to help me, it would be even greater !!!

Pierre

pmithrandir avatar Feb 03 '13 15:02 pmithrandir

By the way, I don't think filtering works on foreign key. Could you confirm ?

Thanks, Pierre

pmithrandir avatar Feb 03 '13 15:02 pmithrandir

Hi Pierre,

I know this bundle lacks of documentation... but help is welcome :-)

I will try to answer all of yours questions. First, here is an exemple to use with AJAX

<?php
//...
class DefaultController extends Controller
{
    /**
     * @Route("/exemple1")
     * @Template()
     */
    public function indexAction()
    {
         $colors = array(
            array('id' => 1, 'color' => 'blue'),
            array('id' => 2, 'color' => 'red'),
            array('id' => 3, 'color' => 'orange'),
            array('id' => 4, 'color' => 'black'),
            array('id' => 5, 'color' => 'green'),
            array('id' => 6, 'color' => 'yellow'),
            array('id' => 7, 'color' => 'grey'),
            array('id' => 8, 'color' => 'white'),
            array('id' => 9, 'color' => 'pink'),
            array('id' => 10, 'color' => 'wood'),
            array('id' => 11, 'color' => 'maroon'),
            );

        return array('datagrid' => $this->get('gloomy.datagrid')->factory($colors));
    }
//...

{% extends "::base.html.twig" %}

{% block stylesheets %}
    {{ parent() }}
    {{ datagrid_stylesheets(datagrid) }}
{% endblock %}

{% block javascripts %}
    {{ parent() }}
    {{ datagrid_javascripts(datagrid) }}

    <script src="/bundles/gloomypager/js/gloomy-utils.js" type="text/javascript"></script>

    <script language="javascript">
        updateDatagrid = function(url, data) {
            gloomyAjaxUpdater(url, '#datagrid', {spinner: '#loading', type: 'post', data: data, onsuccess: function() { bindPager() }});
        }

        bindPager = function() {
            $('#datagrid form').on(
                'submit',
                function(event) {
                    event.preventDefault();
                    updateDatagrid($(this).attr('action'), $(this).serializeArray());
                }
            );

            $('#datagrid a:not(.outbound, [href^="#"])').on(
                'click',
                function(event) {
                    event.preventDefault();
                    updateDatagrid(this.href);
                }
            );
        }

        jQuery(document).ready(function() {
            bindPager();
        })
    </script>
{% endblock %}

{% block body %}
    <div id="datagrid">
        <div id="loading" style="color: red; display: none;">
            Loading...
        </div>

        {{ datagrid_content(datagrid) }}
    </div>
{% endblock %}

I hope it helps !

iamluc avatar Feb 03 '13 16:02 iamluc

Hi,

I forgot to thank you for you example. I will try to put it in place next week end.

Regarding the 3 lasts questions, that are actually more emergency for me, do you have any answer / tip for me please ?

  • is it possible to create filter from foreign key value with a dropdownbox ?
  • Can we remove some columns in datagrid ? I have some column that are not mean to be seen by users.
  • How can we execute a custom query. I want to display value that where validated by the admin only. Now, I show the entire table. When I tried to put an object creating from a queryBuilder, I got an error saying that some datas where not find. -> this one is more : can we defined somehow to use an object returned by a querybuilder in my repository files ?

Thanks Pierre

PS : I will try to get some time to rephrase the documentation if I think I can addd something relevant / generic to add. I'm kind of busy on my project, but I will try.

pmithrandir avatar Feb 05 '13 09:02 pmithrandir

I wrote 2 exemples in the documentation :

  • AJAX (better than the one in this thread)
  • Custom datagrid (custom filters, display, ...)

iamluc avatar Feb 05 '13 10:02 iamluc

I just added an exemple with a QueryBuilder in the documentation.

Tell me if you need something else :-)

iamluc avatar Feb 05 '13 14:02 iamluc

Hello,

I tried to apply your advices today.

I have a question about my code... I managed to dispay a datagrid with a filter on a foregign key value(the theme).When I select one value, the page reload, and the value is kept.

But, the filter doesn't work.

Do I have to do something special in my controler ?

Thanks,

My twig :

{% block datagrid_column_filter__theme %}
    <th>
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[f][theme]" value="theme" />
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[o][theme]" value="equals" />

        <select name="{{ datagrid.pager.getConfig('filtersVar') }}[v][id]" onchange="this.form.submit();">
            <option value="">-- Thème --</option>
            {% for row in field.getOption('select_options') %}
                <option value="{{ row.id }}" {% if datagrid.pager.getValue('filtersVar').v['id']|default('') == row.id %}selected="selected"{% endif %}>{{ row.nom }}</option>
            {% endfor %}
        </select>
    </th>
{% endblock %}

My controller :

    public function listAction()
    {

        $idees = $this->getDoctrine()->getEntityManager()
                ->getRepository('MyBundle:Idee')->getLatestIdeesQb()
                ->addSelect('t')->leftJoin('i.theme', 't')
                ;

        $wrapper = new QueryBuilderWrapper($idees);
        $wrapper
            ->addField(new Field('theme.nom', 'string', 'Thème', 't.nom', array('tree' => true)), 'theme')
        ;
        $wrapper->setOrderBy(array('updated_at' => 'desc'));
        $datagrid = $this->get('gloomy.datagrid')->factory($wrapper);
        $datagrid->showOnly(
            array(
                'title', 
                'description', 
                'created_at', 
                'theme'
                )
            );

        $themes = $this->getDoctrine()->getEntityManager()->getRepository('MyBundle:Theme')->findAll();
        $datagrid->getField('theme')->addOption('select_options', $themes);

        return array('datagrid' => $datagrid);

    }

Also, the created_at column containing a date is always empty, any clue ?

Thanks for your help and the code update,

Pierre

pmithrandir avatar Feb 09 '13 17:02 pmithrandir

Hello,

I found answers to my questions : the code for the filter is that :

{% block datagrid_column_filter__theme %}
    <th>
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[f][theme]" value="theme" />
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[o][theme]" value="equals" />

        <select name="{{ datagrid.pager.getConfig('filtersVar') }}[v][theme]" onchange="this.form.submit();">
            <option value="">-- Thème --</option>
            {% for row in field.getOption('select_options') %}
                <option value="{{ row.id }}" {% if datagrid.pager.getValue('filtersVar').v['theme']|default('') == row.id %}selected="selected"{% endif %}>{{ row.nom }}</option>
            {% endfor %}
        </select>
    </th>
{% endblock %}

For the date, I think I found a bug in the code. My field is named "created_at", which need qualifier : i.created_at, but property createdAt. The property by default is wrong, and the only way I found to correct it was to add a new setter in the Field class to redefine the property. It's just a temporary solution, as I had to change your code.

Do you think we could resolv the bug, or at least to provide both missing setters for qualifier and property ? I don't see any probleme to allow the usser to change these values.

    public function setProperty($property)
    {
        $this->_property = $property;
        return $this;
    }
    public function setQualifier($qualifier)
    {
        $this->_qualifier = $qualifier;
        return $this;
    }

To come back to the main subject, I think what I missed the most was a description in the Field class of all variable. "What is a qualifier or a property for you". A quick description in the header of the class would be a great improvment to your work.

Also, the javascript variable are not easy to understand(that was the problem when I tried to customize the filter).

_gp[f][f][theme]=theme
_gp[f][f][title]=title
_gp[f][o][theme]=equals
_gp[f][o][title]=contains
_gp[f][v][theme]=1
_gp[f][v][title]=

would be more intuitive like that.

_gp[filter][qualifier][theme]=theme
_gp[filter][qualifier][title]=title
_gp[filter][type][theme]=equals
_gp[filter][type][title]=contains
_gp[filter][value][theme]=1
_gp[filter][value][title]=

Thank you, Pierre

pmithrandir avatar Feb 10 '13 12:02 pmithrandir

Hi,

I got another problem with the documentation of the Ajax call.

As explained in the post before, i have a custom filter. But, this filter is not rebuild when I call datagrid_content in ajax.

Because there is an inclusion, I can't redefine the block in the ajax content...

I'm not so familiar with block usage and Ajax, but I didn't find a way to regenerate the page(with pagination, table content, and everything) using your construction.

If you have time, I would appreciate some help on this subject. Pierre

pmithrandir avatar Feb 10 '13 15:02 pmithrandir

Hi Pierre, I'm currently on vacations. I will try to answer next week.

iamluc avatar Feb 14 '13 22:02 iamluc

Hello,

Thank you for your attention, I will wait your answer. Thanks, Pierre

pmithrandir avatar Feb 20 '13 20:02 pmithrandir

Hi,

Concerning the Field class, the arguments are :

$property : It is the way to access and display the data If you use the QueryBuilderWrapper or the EntityWrapper, it is just the name of you property. If you need to walk through object, you must put array('tree' => true) in the $options argument. ie. :

$wrapper->addField(new Field('company.name', 'text', 'Company name', 'company.name', array('tree' => true)), 'company_name');

$type : The type of the data (string or date) If it is a date, you can set the format like that :

$field->setDateFormat('d/m/Y')

$label : The label of the field

$qualifier : The way to sort or filter the datas. If you use DbalQueryBuilderWrapper to make a query like that :

    select  CONCAT(t.lastname, ' ', t.firstname) as name
    from    Customers

Your field will be

new Field('name', 'text', 'Customer name', 'CONCAT(t.lastname, \' \', t.firstname)')

$options : Misc. Use array('tree' => true) if your need to walk through object to get your propert.

Some wrappers (QueryBuilderWrapper or the EntityWrapper) have a populateFields() method to create automatically the fields. But you can redefine them with methods getFields() and addField()

If you don't want the populateFields() method to be called, just put your Fields in the constructor.

Concerning the filers variables :

_gp[f][f][0] = theme  --> Filter Field 0 is theme
_gp[f][o][0] = equals --> Filter Operator 0 is equals
_gp[f][v][0] = 1      --> Filter value 0 is 1

I use short notation because if you put a lot of filters in an URL (GET), you will have a problem with Internet Explorer which limit the GET string to 2048 characters.

At last, for your problem with datagrid_content use in ajax. Did you put 'datagrid_theme' in the shell template or in the datagrid one ?

iamluc avatar Feb 23 '13 14:02 iamluc

Hello,

Here is my code :

{% block mainCol %}
        <span id="loading" style="color: orange; position:absolute; width:100px; height:30px; left:48%; top:30%;border:1px solid black; background-color:white; display: none;text-align:center; line-height:30px;">
            Loading...
        </span>
        <div id="datagrid">
                    {% include 'JaiUneIdeeSiteBundle:Idee:datagrid.html.twig' with {'datagrid': datagrid} %}
                </div>
{% endblock %}
{% block datagrid_column_filter__theme %}
    <th>
        <div id="div_filter_511fa4a7551c6" onmouseout="hideFiltersOpts( event, '511fa4a7551c6' );">
                <div style="white-space: nowrap;">
            <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[f][theme]" value="theme" />
            <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[o][theme]" value="equals" />

            <select name="{{ datagrid.pager.getConfig('filtersVar') }}[v][theme]" onchange="this.form.submit();">
                <option value="">-- Thème --</option>
                {% for row in field.getOption('select_options') %}
                    <option value="{{ row.id }}" {% if datagrid.pager.getValue('filtersVar').v['theme']|default('') == row.id %}selected="selected"{% endif %}>{{ row.nom }}</option>
                {% endfor %}
            </select>
          </div>
        </div>
    </th>
{% endblock %}

And in the datagrid file : (it's in a block, so I can't redefine a block here with the theme).

            {{ datagrid_content(datagrid) }}

My controller :

    public function listAction()
    {

        $idees = $this->getDoctrine()->getEntityManager()
                ->getRepository('JaiUneIdeeSiteBundle:Idee')->getLatestIdeesQb()
                ->addSelect('t')->leftJoin('i.theme', 't')
                ->addSelect('t')->leftJoin('i.user', 'u')
                ;

        $wrapper = new QueryBuilderWrapper($idees);
        $wrapper
            ->addField(new Field('theme.nom', 'string', 'Thème', 't.id', array('tree' => true)), 'theme')
            ->addField(new Field('user.username', 'string', 'Auteur', 'u.username', array('tree' => true)), 'user')
        ;
        $wrapper->setOrderBy(array('updated_at' => 'desc'));
        $datagrid = $this->get('gloomy.datagrid')->factory($wrapper);
        $datagrid->showOnly(
            array(
                'title', 
                'description', 
                'created_at', 
                'theme', 
                'user'
                )
            );

        $themes = $this->getDoctrine()->getEntityManager()->getRepository('JaiUneIdeeSiteBundle:Theme')->findAll();
        $datagrid->getField('theme')->addOption('select_options', $themes);
        $datagrid->getField('created_at')->setProperty('createdAt');
        $datagrid->getField('created_at')->setDateFormat('d/m/Y à H:i:s');

        $template = $this->getRequest()->isXMLHttpRequest() ? 'datagrid.html.twig' : 'list.html.twig';
    return $this->render(
            'JaiUneIdeeSiteBundle:Idee:'.$template,
            array('datagrid' => $datagrid)
        );
        return array('datagrid' => $datagrid);

    }

It's pretty similar to your code. The only change is this setProperty method I didn't find a way to replace.

BTW, other question, are we suppose to get the same filter with a field with DateTime ? I was wonderind if there was specific output there.

You can, see the problem at this page : http://www.jaiuneidee.net/idee/list Press ENTER after clicking in any text filter. and you will see the dropdown disapeer.

Thanks, Pierre

pmithrandir avatar Feb 24 '13 14:02 pmithrandir

Hi Pierre, Sorry for the delay.

I don't understand why you said you can't theme your datagrid because datagrid_content() is in a block. I just tried that and it works :

datagrid.html.twig

{% extends "TestTestBundle:Default:layout.html.twig" %}

{% block body %}
    {% datagrid_theme datagrid _self %}
    {{ datagrid_content(datagrid) }}
{% endblock %}

{% block datagrid_column_value__color %}
    <td>
        <span style="color: {{ datagrid_item_value(datagrid, field, item) }};">{{ datagrid_item_value(datagrid, field, item) }}</span>
    </td>
{% endblock %}

{% block datagrid_column_filter__color %}
    <th>
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[f][color]" value="color" />
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[o][color]" value="equals" />

        <select name="{{ datagrid.pager.getConfig('filtersVar') }}[v][color]" onchange="this.form.submit();">
            <option value="">-- Choose a color --</option>
            {% for row in field.getOption('select_options') %}
                <option value="{{ row.color }}" {% if datagrid.pager.getValue('filtersVar').v['color']|default('') == row.color %}selected="selected"{% endif %}>{{ row.color }}</option>
            {% endfor %}
        </select>
    </th>
{% endblock %}

For your other question : yes the filter for date is a bit different. You can have a look at https://github.com/iamluc/GloomyPagerBundle/blob/master/Resources/views/Pager/macros.html.twig (macro filter)

                {% if type == 'date' %}
                    <div style="margin-top: 20px">
                        {{ 'Date'|trans([], 'pager') }}
                        <div id="div_date_{{ id }}" class="gloomy-filter-date-inline"></div>
                    </div>
                {% endif %}

I used that to display a jquery-ui datepicker inside the filter div

iamluc avatar Mar 04 '13 13:03 iamluc

Hi,

Thanks, for the tips for datepicker.

Regarding the custom filter, they works great without ajax. I tried to follow your exempale about AJAX, where you have a different file for datagrid_content.

The idea is to reload juste the datagrid content / filter, and not the all layout. This is where the problem is, if you reload datagrid_content, you cannot, or I didn't find how to, redefine the filter in this file. As I said, there is an example of none working datagrid ajax on my website : http://www.jaiuneidee.net/idee/list

Select whatever filter and press enter, and you will see the issue.

Pierre

pmithrandir avatar Mar 04 '13 14:03 pmithrandir

You have 2 solutions :

With layout

ajax_layout.html.twig (which can contains only one single block)

{% block body %}
{% endblock %}

datagrid.html.twig

{% extends "TestTestBundle:Default:ajax_layout.html.twig" %}

{% block body %}
    {% datagrid_theme datagrid _self %}
    {{ datagrid_content(datagrid) }}
{% endblock %}

{% block datagrid_column_value__color %}
    <td>
        <span style="color: {{ datagrid_item_value(datagrid, field, item) }};">{{ datagrid_item_value(datagrid, field, item) }}</span>
    </td>
{% endblock %}

{% block datagrid_column_filter__color %}
    <th>
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[f][color]" value="color" />
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[o][color]" value="equals" />

        <select name="{{ datagrid.pager.getConfig('filtersVar') }}[v][color]" onchange="this.form.submit();">
            <option value="">-- Choose a color --</option>
            {% for row in field.getOption('select_options') %}
                <option value="{{ row.color }}" {% if datagrid.pager.getValue('filtersVar').v['color']|default('') == row.color %}selected="selected"{% endif %}>{{ row.color }}</option>
            {% endfor %}
        </select>
    </th>

Without layout, in a new file (ie. datagrid_theme.html.twig)

datagrid.html.twig

{% datagrid_theme datagrid 'TestTestBundle:Default:datagrid_theme.html.twig' %}
{{ datagrid_content(datagrid) }}

datagrid_theme.html.twig

{% block datagrid_column_value__color %}
    <td>
        <span style="color: {{ datagrid_item_value(datagrid, field, item) }};">{{ datagrid_item_value(datagrid, field, item) }}</span>
    </td>
{% endblock %}

{% block datagrid_column_filter__color %}
    <th>
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[f][color]" value="color" />
        <input type="hidden" name="{{ datagrid.pager.getConfig('filtersVar') }}[o][color]" value="equals" />

        <select name="{{ datagrid.pager.getConfig('filtersVar') }}[v][color]" onchange="this.form.submit();">
            <option value="">-- Choose a color --</option>
            {% for row in field.getOption('select_options') %}
                <option value="{{ row.color }}" {% if datagrid.pager.getValue('filtersVar').v['color']|default('') == row.color %}selected="selected"{% endif %}>{{ row.color }}</option>
            {% endfor %}
        </select>
    </th>
{% endblock %}

iamluc avatar Mar 04 '13 14:03 iamluc

Thank you for this very precise solution. I will try it this week end normally

Pierre

pmithrandir avatar Mar 04 '13 14:03 pmithrandir

Hello.

i'm trying to update my bundles, but because I had to modify the gloomy pager Bundle, I can't do it.

Do you think you could apply the patch I had to put in place ?

    public function setProperty($property)
    {
        $this->_property = $property;
        return $this;
    }
    public function setQualifier($qualifier)
    {
        $this->_qualifier = $qualifier;
        return $this;
    }

It helps me to execute this method to correct the wrong initialisation of column with "_" in the name like created_at.

        $datagrid->getField('created_at')->setProperty('createdAt');

I should not add any issue for what I understand the code.

Best regards, Pierre

BTW : everything else works fine. I think it would be great to allow people to create "custom" query bnehind the filter, but it's not a priority.

pmithrandir avatar May 18 '13 16:05 pmithrandir

Hi Pierre, It's done !

Glad to see that everything works for you.

iamluc avatar May 21 '13 07:05 iamluc

Thank you, I will update the Bundle this evening. For you to see it in real : http://jaiuneidee.net/idee/list

I still have some issues with CSS on the autocomplete element, but it works fine now.

Some improvments I have in my mind :

  • To be able to define which filter will be displayed. greater than doesn't make much sence for a string.
  • To integrate a date time picker for the date filter
  • To have a button "submit" for the filter. Some user doesn't understand that they have to press ENTER.

Something less important(and I'm not sure relevant)

  • To be able to change the query. For example, if I select France, I would like to get all children, which mean to get all values between a mix and a max value in my DB. I cannot do that now.(this is an exemple, but the idea behind is to allow complex queries for a column).

Pierre

pmithrandir avatar May 21 '13 07:05 pmithrandir