Coder Social home page Coder Social logo

reactive-table's Introduction

Reactive Table

A reactive table for Meteor, using Blaze.

Table of Contents

Quick Start

Install reactive table:

meteor add aslagle:reactive-table

This package adds a template called reactiveTable. Create and subscribe to a collection, and pass it to the template:

{{> reactiveTable collection=myCollection}}

When the whole collection should be in the table, it's best to pass in the Meteor collection object (returned by new Meteor.Collection()). You can also pass in the cursor returned by collection.find() to show a subset of the collection, or a plain array to show data that's not in a Meteor collection.

If you're new to Meteor, note that global variables aren't available from templates. You can add template helpers to access them:

Template.myTemplate.helpers({
    myCollection: function () {
        return myCollection;
    }
});

Customization

The reactiveTable helper accepts additional arguments that can be used to configure the table.

{{> reactiveTable collection=collection showNavigation='never' rowsPerPage=5}}

Settings

  • showFilter: Boolean. Whether to display the filter box above the table. Default true, unless using custom filters.
  • filters: Array. An array of custom filter ids to use with this table. Default [].
  • rowsPerPage: Number. The desired number of rows per page. May also be a ReactiveVar, see accessing and controlling table state. Defaults to 10.
  • showNavigation: 'always', 'never' or 'auto'. The latter shows the navigation footer only if the collection has more rows than rowsPerPage.
  • showRowCount: Boolean. If the navigation footer is visible, display the total number of rows in the collection. When filtering, the value changes to the total number of rows in the filtered collection. Default false.
  • showNavigationRowsPerPage: Boolean. If the navigation footer is visible, display rows per page control. Default 'true'.
  • fields: Object. Controls the columns; see below.
  • showColumnToggles: Boolean. Adds a 'Columns' button to the top right that allows the user to toggle which columns are displayed. (Note: there aren't translations for this button yet - please add one if you're using it.) Add hidden to fields to hide them unless toggled on, see below. Add hideToggle to a field to exclude it from the toggle list. Default false.
  • useFontAwesome: Boolean. Whether to use Font Awesome for icons. Requires the fortawesome:fontawesome package to be installed. Default true if fortawesome:fontawesome is installed, else false.
  • enableRegex: Boolean. Whether to use filter text as a regular expression instead of a regular search term. When true, users won't be able to filter by special characters without escaping them. Default false. (Note: Setting this option on the client won't affect server-side filtering - see Server-side pagination and filtering)
  • ready: ReactiveVar(Boolean). When using ReactiveTable.publish on the server, pass in a ReactiveVar for ready on the client and it will be updated to true or false so you can check if the subscription is ready.
  • noDataTmpl: Template. Template to render in place of the table when the collection is empty or filtered to 0 rows. Default none (renders table header with no rows).
  • multiColumnSort: Boolean. Whether to enable sorting with multiple columns based on the order the user clicks them. Default: true.
  • class: String. Classes to add to the table element in addition to 'reactive-table'. Default: 'table table-striped table-hover col-sm-12'.
  • id: String. Unique id to add to the table element. Default: generated with _.uniqueId.
  • rowClass: String or function returning a class name. The row element will be passed as first parameter.

rowClass examples

As a function

rowClass: function(item) {
  var qnt = item.qnt;
  //
  switch (qnt) {
    case 0:
      return 'danger';
    case 1:
    case 2:
      return 'warning';
    default:
      return ''
  }
},

as a string

rowClass: 'danger',

Settings Object

Settings can also be grouped into a single object to pass to the table:

{{> reactiveTable settings=settings}}

Define the settings in a helper for the template that calls reactiveTable:

Template.myTemplate.helpers({
    settings: function () {
        return {
            collection: collection,
            rowsPerPage: 10,
            showFilter: true,
            fields: ['name', 'location', 'year']
        };
    }
});

You can continue to pass some settings as named arguments while grouping the others into the settings object:

{{> reactiveTable collection=collection fields=fields settings=settings}}

Styling

Add bootstrap or bootstrap-3 to style the table, or add your own css. The generated table will have the class 'reactive-table'. To use Font Awesome for icons, also add the fortawesome:fontawesome package. You can also use the argument class to define table styling:

{{> reactiveTable class="table table-bordered table-hover" collection=myCollection}}

Setting columns

To specify columns, add a fields key to the settings object.

Fields can simply be an array of field names (attributes in the collection).

{ fields: ['name', 'location', 'year'] }

Setting column headers

To set labels for the column headers, use an array of field elements, each with a key (the attribute in the collection) and a label (to display in the table header).

{ fields: [
    { key: 'name', label: 'Name' },
    { key: 'location', label: 'Location' },
    { key: 'year', label: 'Year' }
] }

The label can be a string or a function or a Blaze Template:

{ fields: [
    { key: 'name', label: function () { return new Spacebars.SafeString('<i>Name</i>'); } }
    { key: 'ageRange', label: Template.ageRangeColumnLabel, labelData: {ageFrom: 18, ageTo: 50}}
] }

where the template is defined as:

<template name="ageRangeColumnLabel">
  <span>Age {{ageFrom}} to {{ageTo}}</span>
</template>

The labelData element is used to set the data context of the label template.

Column Header CSS Class

To set the css class for table header <th>, use the optional headerClass key. This attribute can be a String or a Function.

{ fields: [
  { key: 'name', label: 'Name' , headerClass: 'col-md-4'},  // as String
  { key: 'location', label: 'Location',
    headerClass: function () {
     var css = 'col-md2';
     '/*do some logic here */
     return css;}  // as Function
  },
  { key: 'year', label: 'Year' }
] }

Cell CSS Class

To set the css class for the table cells in a column, add the cellClass key to the field settings. This attribute can be a String or a Function. The function arguments will be the value for this key, and the full row object.

{ fields: [
  { key: 'name', label: 'Name' , cellClass: 'col-md-4'},  // as String
  { key: 'location', label: 'Location',
    cellClass: function (value, object) {
     var css = 'col-md2';
     '/*do some logic here */
     return css;}  // as Function
  },
  { key: 'year', label: 'Year' }
] }

Templates

You can specify a template to use to render cells in a column, by adding tmpl to the field options.

{ fields: [
    { key: 'name', label: 'Name', tmpl: Template.nameTmpl },
    { key: 'location', label: 'Location', tmpl: Template.locationTmpl }
] }

The template's context will be the full object, so it will have access to all fields.

Virtual columns

You can also compute a function on the attribute's value to display in the table, by adding fn to the field.

{ fields: [
    {
        key: 'resources',
        label: 'Number of Resources',
        fn: function (value, object, key) { return value.length; }
    }
] }

If the key exists in the record, it will be passed to fn in value. Otherwise, value will be null.

The object argument contains the full object, so you can compute a value using multiple fields.

The key argument contains the key of the field. This allows you to get the reference point of where the value comes from in case fields are generated dynamically.

By default for client-side collections, fields that use fn will be sorted by the result of this function. If you want to sort by the field's original value instead (for example, if you are making a date human-readable), set sortByValue to true on the field object.

For server-side collections, sorting is always by value.

Be aware that it is impossible at the moment to filter on virtual fields.

In case of a need for a more sophisticated algorithm, use sortFn. This function behaves similarly to fn but does not affect the visible cell value.

HTML

You can use HTML in a virtual column by creating a Spacebars SafeString:

fn: function (value) {
    return new Spacebars.SafeString("<a href="+Routes.route['view'].path({_id:value})+">View</a>");
}

When adding user-generated fields to the HTML, ensure that they have been properly escaped to prevent cross-site scripting vulnerabilities.

Default sorting

All columns are sortable by default, but sorting can be disabled by setting sortable to false:

{ key: 'year', label: 'Year', sortable: false }

Default sort order and direction can be controlled by adding sortOrder and sortDirection to fields:

{ fields: [
    { key: 'year', label: 'Year', sortOrder: 0, sortDirection: 'descending' },
    { key: 'name',  label: 'Name', sortOrder: 1, sortDirection: 'ascending'}
] }

sortDirection will accept any truthy value for ascending order, and 'desc', 'descending' or -1 for descending order.

sortOrder will give fields with lower sortOrder higher priority in sorting, so the field with the lowest sortOrder will be the primary sort.

Nested objects and arrays

For elements of nested objects and arrays, use mongo's syntax in the key:

{'key': 'emails.0.address', label: 'Email Address'}

Hidden columns

To hide a column, add hidden to the field. It can be a boolean or a function.

{ key: 'location', label: 'Location', hidden: true }
{ key: 'location', label: 'Location', hidden: function () { return true; } }

If the showColumnToggles setting is true, hidden columns will be available in a dropdown and can be enabled by the user.

Dynamic columns

If you need to be able to add new columns to an existing table (e.g. in a reactive computation), you must explicitly set a unique-valued fieldId attribute on each and every field definition:

{ fields: [
    {
        fieldId: 'month',
        key: 'postingDate',
        label: 'Posting Month',
        fn: function (value) { return value.month; }
    },
    {
        fieldId: 'year',
        key: 'postingDate',
        label: 'Posting Year',
        fn: function (value) { return value.year; }
    }
] }

Having unique fieldId values ensures that default column visibility, visibility toggling and currently sorted column work correctly when adding new columns:

  tmpl.autorun(function() {
    if (Session.equals('showPostingDay', true)) {
      tmpl.fields.set(tmpl.fields.get().unshift({
        fieldId: 'day',
        key: 'postingDate',
        label: 'Posting Day',
        fn: function (value) { return value.day; }
      }));
    }
  });

where tmpl.fields could be a template instance reactive variable used in a helper to provide a reactive table's settings.

Reactive Table will print an error to the console if at least one field has a 'fieldId' attribute and:

  1. One or more other fields do NOT have a fieldId attribute, or
  2. There are duplicate (or null) fieldId values.

Using events

Make the event selector be tr, and you'll have your row object in this:

Template.posts.events({
  'click .reactive-table tbody tr': function (event) {
    // set the blog post we'll display details and news for
    var post = this;
    Session.set('post', post);
  }
});

If you want single elements inside a row to become clickable, you still have to target tr. Otherwise this won't refer to the corresponding object of your targeted row. With this in mind, you have to specify a target inside your 'click .reactive-table tbody tr' eventlistener:

Template.posts.events({
  'click .reactive-table tbody tr': function (event) {
    event.preventDefault();
    var post = this;
    // checks if the actual clicked element has the class `delete`
    if (event.target.className == "delete") {
      Posts.remove(post._id)
    }
  }
});

Accessing and controlling table state

The table's pagination and column visibility can be controlled by ReactiveVars. When the value changes, the table will automatically update, and when the user changes the state of the table, the value of the variable will be changed.

These main table settings can be ReactiveVars:

  • currentPage (will contain the 0-indexed page the table is displaying)
  • rowsPerPage

In addition, columns can contain an isVisible ReactiveVar, which will contain a boolean that determines whether the column is displayed. The sortOrder and sortDirection column options can also be ReactiveVars (sortDirection should be 1 or -1) if using a ReactiveVar.

For example, to save the user's current page to the Session and restore it from the Session, set up a ReactiveVar in the template containing your reactiveTable:

Template.myTemplate.onCreated(function () {
  var currentPage = new ReactiveVar(Session.get('current-page') || 0);
  this.currentPage = currentPage;
  this.autorun(function () {
    Session.set('current-page', currentPage.get());
  });
});

Template.myTemplate.helpers({
  tableSettings: function () {
    return {currentPage: Template.instance().currentPage};
  }
});

Server-side Pagination and Filtering BETA

Use ReactiveTable.publish on the server to make a collection available to reactive-table without subscribing to the full collection.

Arguments:

  • name: The name of the publication
  • collection: A function that returns the collection to publish (or just a collection, if it's insecure).
  • selector: (Optional) A function that returns mongo selector that will limit the results published (or just the selector).
  • settings: (Optional) A object with settings on server side's publish function. (Details below)

Inside the functions, this is the publish handler object as in Meteor.publish, so this.userId is available.

Note: Although ReactiveTable.publish uses Meteor.publish, it publishes the rows to a special collection that's only accessible inside the reactive-table package. If you want to use your collection directly you'll have to publish it separately as well.

On the client, use the publication name as the collection argument to the reactiveTable template.

{{> reactiveTable collection="name"}}

Items = new Meteor.Collection('items');

if (Meteor.isServer) {
  // Insecure: entire collection will be available to all clients
  ReactiveTable.publish("insecure-items", Items);

  // Publish only a subset of items with "show" set to true
  ReactiveTable.publish("some-items", Items, {"show": true});

  // Publish only to logged in users
  ReactiveTable.publish("all-items", function () {
    if (this.userId) {
      return Items;
    } else {
      return [];
    }
  });

  // Publish only the current user's items
  ReactiveTable.publish("user-items", Items, function () {
    return {"userId": this.userId};
  });
}

Other table settings should work normally, except that all fields will be sorted by value, even if using fn. The fields setting is required when using a server-side collection.

Server-side Settings

The following options are available in the settings argument to ReactiveTable.publish:

  • fields (Mongo Field Specifier) A set of fields to exclude or include from results and filtering, e.g. {fields: {name: 1, email: 1}} or {fields: {password: 0}}
  • enableRegex (Boolean - default= false): Whether to use filter text as a regular expression instead of a regular search term. When true, users will be able to enter regular expressions to filter the table, but your application may be vulnerable to a ReDoS attack. Also, when true, users won't be able to use special characters in filter text without escaping them.
  • disablePageCountReactivity (Boolean - default= false): Whether to disable reactive updates of the displayed page count. Setting this to true will improve performance and is a good idea if you don't need the page count to automatically update.
  • disableRowReactivity (Boolean - default= false): Whether to disable reactive updates of the displayed rows (which rows are displayed and their contents). Setting both this and disablePageCountReactivity to true will disable all reactivity.

Regex Examples: A user filters with "me + you"

    ReactiveTable.publish(
        "some-items",
        Items,
        {"show": true}
        {"enableRegex": false});

will provide you search results, while

    ReactiveTable.publish(
        "some-items",
        Items,
        {"show": true}
        {"enableRegex": true});

will crash on the server, since "me + you" is not a valid regex ("me \+ you" would be correct).

Default is to disable regex and automatically escape the term, since most users wont 'speak' regex and just type in a search term.

Custom Filters

reactive-table allows you to add multiple filters, anywhere on the page, and link them to a table instead of using the default filter box.

Multiple filters outside a table

To create a filter, use the reactiveTableFilter template:

{{> reactiveTableFilter id="myFilter" label="Filter" }}

Use the id of the filter in the filters argument in your reactiveTable settings.

{
  fields: [...]
  filters: ['myFilter']
}

reactiveTableFilter accepts the following arguments:

  • id: String. A unique id for the filter, used to link the filter to tables. Also used as the HTML id attribute.
  • class: String. HTML class attribute to apply to the element containing the filter. Default: input-group.
  • label: String. Label to display with the filter box.
  • fields: Array. Optional array of field keys that this filter should apply to, eg ["firstName", "lastName"]. Default: [], which will use all fields in the table. Note that you can't use can't use arrays directly in Spacebars templates - you'll need to write a template helper that returns the array.

By default, the filters are combined with $and, but you can set the operator to $or with the filterOperator setting. Add it to the main reactiveTable settings for client-side collections, or the server-side settings when using server-side filtering and pagination.

Creating your own filter

For even more customization, you can create your own ReactiveTable.Filter:

var filter = new ReactiveTable.Filter(filterId, fields);

new ReactiveTable.Filter accepts these arguments:

  • id: String. A unique id for the filter, used to link the filter to tables.
  • fields: Array. Optional array of field keys that this filter should apply to, eg ["firstName", "lastName"]. Default: [], which will use all fields in the table.

Once created, you can use the filter id in the reactiveTable filters, and call filter.get() and filter.set() to modify the filter. set can accept either a string or a mongo selector (eg {"$gt": 5}).

To clear the filter, set it to an empty string: filter.set(""). For convenience, there is also a ReactiveTable.clearFilters function that will clear a list of filter ids:

ReactiveTable.clearFilters(['filter1', 'filter2', 'filter3']);

Here's an example of a custom template using ReactiveTable.Filter:

<template name="greaterThanFilter">
    <div class="input-group">
      <span class="input-group-addon">
        <span>Score Greater Than </span>
      </span>
      <input class="form-control greater-than-filter-input" type="text"/>
    </div>
</template>

Template.greaterThanFilter.created = function () {
  this.filter = new ReactiveTable.Filter('greater-than-filter', ['score']);
};

Template.greaterThanFilter.events({
   "keyup .greater-than-filter-input, input .greater-than-filter-input": function (event, template) {
      var input = parseInt($(event.target).val(), 10);
      if (!_.isNaN(input)) {
        template.filter.set({'$gt': input});
      } else {
        template.filter.set("");
      }
   }
});

Nested Tables

You can use filters to set up tables within tables – in essence, doing client-side reactive joins. A practical example would be showing a list of users, and then having a “Purchases” column where you then show that user's purchases.

The first step would be specifying the tmpl option for the user's Purchases column:

fields: [
  {key: 'purchases', label: "Purchases", tmpl: Template.userPurchases}
]

You will then need to define the userPurchases template, which we're including for each row of the main Users table:

<template name="userPurchases">
  {{> reactiveTable settings=settings}}
</template>

Along with its template helper:

Template.userPurchases.onCreated(function () {
  var user = this.data;
  this.filter = new ReactiveTable.Filter("userPurchasesFilter_"+user._id, ["userId"]);
  this.filter.set(user._id);
});


Template.userPurchases.helpers({
  settings: function() {
    var user = this;
    return {
      collection: "user-purchases",
      filters: ["userPurchasesFilter_"+user._id],
      field: [...]
    };
  }
});

For each iteration of the userPurchases template, we're defining a new filter based on the current user's _id, and using it to generate a new table containing that user's purchases.

And finally, the server-side publication:

ReactiveTable.publish("user-purchases", function () {
  if(isAdmin(this.userId)){
    return Purchases;
  } else {
    return [];
  }
});

Note that the filter will automatically be passed on to the publication and be applied to the collection it returns.

Internationalization

Internationalization support is provided using anti:i18n.

Add anti:i18n to your project:

meteor add anti:i18n

To set your language to French:

i18n.setLanguage('fr');

We currently have translations (except the 'Columns' button) for:

  • Arabic (ar)
  • Brazilian Portuguese (pt-br)
  • Bulgarian (bg)
  • Chinese simplified (zh)
  • Chinese traditional (zh-tw)
  • Croatian (hr)
  • Czech (cs)
  • Danish (da)
  • Dutch (nl)
  • Finnish (fi)
  • French (fr)
  • German (de)
  • Greek (gr)
  • Hebrew (he)
  • Icelandic (is)
  • Italian (it)
  • Macedonian (mk)
  • Norwegian (no)
  • Persian (fa)
  • Polish (pl)
  • Portuguese (pt)
  • Romanian (ro)
  • Russian (ru)
  • Slovak (sk)
  • Spanish (es)
  • Swedish (sv)
  • Turkish (tr)
  • Ukrainian (ua)

For other languages or the 'Columns' button, contribute a translation to reactive_table_i18n.js.

reactive-table's People

Contributors

aslagle avatar atoy40 avatar boboci9 avatar brugnara avatar brylie avatar ciwolsey avatar clarencel avatar dandv avatar eskan avatar evliu avatar fafournier avatar fix avatar gabrielhpugliese avatar istoramandiri avatar jgoley avatar markus-li avatar physiocoder avatar pierreozoux avatar pscanf avatar rjsmith avatar s7dhansh avatar serkandurusoy avatar sturmer-eha avatar tomvolek avatar willemx avatar wtfuii avatar wuzhuzhu avatar yasinuslu avatar yehchuonglong avatar zimme avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reactive-table's Issues

Add filtering capabilities

It would be great if users could search across fields and rows to filter results. Perhaps a search box?

searching just after onkeyup is triggered, is not a good idea.

I'm using a IR scanner and it's super fast on typing but, because every keyup we are triggering a search calling session.set and many other "fat" things, many chars are lost. I'll solve this applying a setTimeout(search, 200) on event keyup and if another keyup is triggered, before the 200ms has passed, I'll do a clearTimeout() and a setTimeout() again. I'll fix soon, I'm writing this as a reminder.

Server side filter and pagination

I like this package but I can't figure out how to get it to work so that it does server side filtering and pagination.

Anything over a few thousand records gets clunky.

Any ideas?

support for functions in transformed documents

Hello, and thank you for sharing this awsome reactive-table
i would like to pass a transform to my collection.find, to use my models, but some keys are functions so i end up doing this in my template helper

 return {fields: [
     {key:'functionName', label: "Label", fn: function(v,o){return v.call(o)}
 ]}

i guess this may go to the package, i will send a patch.

Thank you.
Jamal

Dynamically created collections fail

Hi, I've been working on building a gui for making collections and reactive-table is throwing
Exception from Deps recompute function: TypeError: Object.keys called on non-object
on line 487
fields = .without(.keys(collection.findOne()), '_id');

now I can console.log the collection and it comes back as a collection, I can setup collectionname = new Meteor.Collection("myCollection") outside of Meteor.startup and that also works but creating the collections within Meteor.startup does not seem to work with reactive table. I can also iterate over data etc from the collection without issues. This might be a Meteor issue but I'm not sure.

Ok I seemed to have figured out the issue. Collection.findOne() is returning undefined.

so it looks like the data isn't ready but I'm using fastrender and iron router meteor 8.0
Would love some help.

Thanks

Events support?

How can I detect on which row the user has clicked? I tried

Template.posts.events({
  'click .reactive-table .name': function () {
    console.log('Clicked on', this.name);
  }
})

But this points to the column definition: {key: "name", label: "Name", fn: function(...)} rather than the underlying data.

Event management, other than "click .tr"

Is it possible to access the "this" object through other events than click on the row ?
In my project, I would like to be able to access the "this" object by clicking on a link in a cell.

Thanks and very helpful package,

Regards
Gabor

incremental search when typing in the filter

Not a huge issue, but a nice to have form a UX standpoint since there's no button to click to "Filter" and the only way to run the filter is to press Enter. Should search after more than X ms passed since last input.

Validation for pagination

It would be great to add some validation for page number and number of rows because currently user is able to add any values and it breaks pagination:

Page 1 of NaN

if rows per page is empty or string

Page 1 of Infinity

if rows per page is 0

Error: >reactiveTable.show< >reactiveTable.rowsPerPage< >reactiveTable.page< >reactiveTable.of< 1

Any ideas on why I am getting this text surrounding the pagination?

reactiveTable.show< >reactiveTable.rowsPerPage< >reactiveTable.page< >reactiveTable.of< 1

I have 5 different tables in my app and this is happening to all of them

Also the filter now has >reactiveTable.filter< as the placeholder in the filter, but the filter works.

It still happens after I remove and add again and even after "mrt uninstall --system"

here is my smart.json
{
"meteor": {},
"packages": {
"bootstrap-3": {},
"iron-router": {},
"autoform": {},
"moment": {},
"spin": {},
"stripe": {},
"collection2": {},
"collection-helpers": {},
"connection-banner": {},
"statemachine": {},
"pdfjs": {},
"accounts-entry": {},
"font-awesome": {},
"iron-router-progress": {},
"highcharts": {},
"roles": {},
"accounts-admin-ui-bootstrap-3": {},
"googlemaps": {},
"reactive-table": {}
}
}

Settings keys for showing navigation and filter control

The pagination control is always shown, even if the table is destined to have fewer records than rowsPerPage, so the user will always see Show 10 rows per page - Page 1 of 1. An ability to toggle its display from settings, or an autohide option would be nice.

The filter control could be optional as well.

As a workaround, they can both be hidden via CSS, so not a big issue.

Filter crashes server ?

=> App running at: http://localhost:3000/
I20140811-18:55:05.304(-4)? Exception in Mongo write: MongoError: E11000 duplicate key error index: meteor.services.$_id_  dup key: { : "BT5FQJMHNKDG4L9gR" }
I20140811-18:55:05.348(-4)?     at Object.toError (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/utils.js:110:11)
I20140811-18:55:05.348(-4)?     at /home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/collection/core.js:599:22
I20140811-18:55:05.348(-4)?     at Server.Base._callHandler (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/base.js:445:41)
I20140811-18:55:05.349(-4)?     at /home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/server.js:468:18
I20140811-18:55:05.349(-4)?     at MongoReply.parseBody (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
I20140811-18:55:05.349(-4)?     at null.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/server.js:426:20)
I20140811-18:55:05.349(-4)?     at emit (events.js:95:17)
I20140811-18:55:05.349(-4)?     at null.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:201:13)
I20140811-18:55:05.350(-4)?     at emit (events.js:98:17)
I20140811-18:55:05.350(-4)?     at Socket.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/connection.js:422:22)
=> Meteor server restarted
I20140811-18:55:20.612(-4)? Exception in Mongo write: MongoError: E11000 duplicate key error index: meteor.services.$_id_  dup key: { : "BT5FQJMHNKDG4L9gR" }
I20140811-18:55:20.613(-4)?     at Object.toError (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/utils.js:110:11)
I20140811-18:55:20.613(-4)?     at /home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/collection/core.js:599:22
I20140811-18:55:20.614(-4)?     at Server.Base._callHandler (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/base.js:445:41)
I20140811-18:55:20.615(-4)?     at /home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/server.js:468:18
I20140811-18:55:20.615(-4)?     at MongoReply.parseBody (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
I20140811-18:55:20.615(-4)?     at null.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/server.js:426:20)
I20140811-18:55:20.615(-4)?     at emit (events.js:95:17)
I20140811-18:55:20.615(-4)?     at null.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:201:13)
I20140811-18:55:20.616(-4)?     at emit (events.js:98:17)
I20140811-18:55:20.616(-4)?     at Socket.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/connection.js:422:22)
=> Meteor server restarted
I20140811-18:55:25.298(-4)? Exception in Mongo write: MongoError: E11000 duplicate key error index: meteor.services.$_id_  dup key: { : "BT5FQJMHNKDG4L9gR" }
I20140811-18:55:25.298(-4)?     at Object.toError (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/utils.js:110:11)
I20140811-18:55:25.299(-4)?     at /home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/collection/core.js:599:22
I20140811-18:55:25.299(-4)?     at Server.Base._callHandler (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/base.js:445:41)
I20140811-18:55:25.299(-4)?     at /home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/server.js:468:18
I20140811-18:55:25.299(-4)?     at MongoReply.parseBody (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/responses/mongo_reply.js:68:5)
I20140811-18:55:25.300(-4)?     at null.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/server.js:426:20)
I20140811-18:55:25.300(-4)?     at emit (events.js:95:17)
I20140811-18:55:25.300(-4)?     at null.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:201:13)
I20140811-18:55:25.300(-4)?     at emit (events.js:98:17)
I20140811-18:55:25.300(-4)?     at Socket.<anonymous> (/home/brendon/.meteor/packages/mongo-livedata/4821944ffe/npm/node_modules/mongodb/lib/mongodb/connection/connection.js:422:22)

Any ideas? Fresh install, happening as soon as I type in the filter (monitoring the server console). I tried deleting all of the records in that collection and starting fresh... made no difference :(

i18n localisation for Ukrainian language

Can You add ua localisation to reactive_table_i18n.js?

i18n.map('ua', {
reactiveTable: {
filter: 'Фільтр',
show: 'Показати',
rowsPerPage: 'линій на страниці',
page: 'Страниця',
of: 'з'
}
});

Resizable columns

@dandv, I'm trying out colResizable. It's on a branch called resizable-columns if you want to test it. The columns resize, but the changes don't persist yet.

Field Function fails for first field

So I've been trying to get past this "Exception from Deps recompute function: Error" error ... think I just found a bug.

This works (note the "Duh, Test" initially):

    settings: function () {
        return {
            rowsPerPage: 1000,
            showFilter: true,
            showNavigation: "never",  
            group:'clientSingle',           
            fields: [
            {
                key:'duh',
                label: 'test'
            },
            {
                key: 'employee',
                label: 'Team Member',
                fn: function (value, object) { return 0; }                                       
            },
            {
                key: 'blocks',
                label: 'Total Blocks',
            },
            {
                key: 'completed',
                label: 'Completed Blocks'  
            },
            {
                key: 'incompleteBlocks',
                label: 'Incomplete Blocks',
                fn: function (value, object) { return object.blocks - object.completed; }
            }
            ] 
        };
    }

This doesn't:

settings: function () {
        return {
            rowsPerPage: 1000,
            showFilter: true,
            showNavigation: "never",  
            group:'clientSingle',           
            fields: [
            {
                key: 'employee',
                label: 'Team Member',
                fn: function (value, object) { return 0; }                    
            },
            {
                key: 'blocks',
                label: 'Total Blocks'
            },
            {
                key: 'completed',
                label: 'Completed Blocks'  
            },
            {
                key: 'incompleteBlocks',
                label: 'Incomplete Blocks',
                fn: function (value, object) { return object.blocks - object.completed; }
            }
            ] 
        };
    }

It fails with this error:

Exception from Deps recompute function: Error
at checkSubtree (http://localhost:3000/packages/check.js?a54cf3c7b3abe9208d064621eb80a3ecaf2f4add:235:11)
at check (http://localhost:3000/packages/check.js?a54cf3c7b3abe9208d064621eb80a3ecaf2f4add:49:5)
at _.extend._getFindOptions (http://localhost:3000/packages/mongo-livedata.js?ff5d7a0fd0742a865dba1e613726b435ae06e9d3:303:7)
at _.extend.find (http://localhost:3000/packages/mongo-livedata.js?ff5d7a0fd0742a865dba1e613726b435ae06e9d3:323:39)
at Object.Template.reactiveTable.helpers.sortedRows (http://localhost:3000/packages/reactive-table.js?5a966e4aeb892bebd6f879084179af63266948ea:637:40)
at http://localhost:3000/packages/blaze-layout.js?58621fa8e3ed65d10240815d5ba3fb34e2e9fdc5:520:21
at Object.Spacebars.call (http://localhost:3000/packages/spacebars.js?f49560fc90a8d71b7637d9596696078881b8c812:173:18)
at Component.HTML.TABLE.HTML.TBODY.UI.Each.UI.block.self [as __sequence] (http://localhost:3000/packages/reactive-table.js?5a966e4aeb892bebd6f879084179af63266948ea:190:24)
at http://localhost:3000/packages/ui.js?f0696b7e9407b8f11e5838e7e7820e9b6d7fc92f:2688:19
at http://localhost:3000/packages/observe-sequence.js?2d685c5e1f0a3395de32e4cd78255cc082f7d1b5:116:17 

I know it's just a "return 0;" - but ANY function used there regardless causes this error... I also tried removing the last object's function and it made no difference... Something about the FIRST object's function is breaking things. It's a nasty issue because nothing rendereds on the page once this happens.

Let me know if it's just me, maybe i'm doing something silly?

Thanks for all your hard work- this package is phenomenal once you get the swing of it... DataTables fooled me- it looked so good but ended up having way more issues that it was worth... this is much more Meteor like.

'keyup .reactive-table-filter input' finer grain target

'keyup .reactive-table-filter input' should be targeted to an id rather than the input so that people can inject other html input elements into the reactive-table-filter.

For example, I am injecting a start and end date date selector into the reactive-table-filter and when I select a start or end date the 'keyup .reactive-table-filter input' is firing.

If this was tightly bound to an id of filter then people could inject other input elements into the filter div in order to enable server side subscription management.

For example, my start and end date html fragments injected fire their event handlers to be used in the autorun subscription.

Make columns conditional

Is there a way to make columns conditional?

Inherent use of Mongo is to have collection docs that do not always have the same fields.

I am trying to figure out how to make a column conditional based on the record iterated.

For example
Invoices.customer vs Invoice (that doesn't have a customer record).

Remove attrs option

I don't think this is necessary or particularly useful, given that the full objects are already available through meteor's event handlers, and it will be simpler not to support it. This won't be backwards-compatible, so it's probably best to remove it asap.

Exception in queued task: Error: Duplicate _id

Got this error after upgrading from Meteor 0.7.2 and reactive-table 0.2.5, to Meteor 0.8.0 and the current reactive-table (checked out from GitHub). The error is triggered by the addedCallback.

The app is pretty complex and creating a minimal example to reproduce the bug will take some work. Hope that might not be necessary :)

How do I return HTML in a virtual field?

In a table with blog posts (title, author, date), I'd like to hyperlink the title to a page for the blog post. Ideally this would be an iron-router {{pathFor}}, but is there a way at least to return an HTML <a href="...computed slug..."> anchor?

Add back trailing zeros from <td> fields.

Using the table (great package, thanks!) for a list of products in $, thus I would prefer to show all the values in the table with two trailing decimals.

It appears the default is to eliminate a trailing zero, so instead of $0.60, I get $0.6 -- how can I set this to show the full $ amt as $0.60?

Thanks.

Idea: Default Sort Field

Would be nice to be able to customise which field is sorted by default and in ascending or descending order.

Send the whole object to fn()

Hello,

what do you think of sending the whole "row" object to the fn function as 2nd parameter (line 133) :

return fn(value, object);

so, you can use other keys to compute a virtual column value. It can be ignore in most case, but very useful (in my case at least :))

Anthony.

Can't select text

Tried this on Chrome on Windows and Mac, can't select text even outside the table (the "rows per page" text).

Wrong pagination when changing items-per-page

If you are at page 2 and then set the items-per-page, say, at 100 items (I'm supposing you have less than 100 items in your table), you will get an empty page without chances to revert, because input fields disappear. I'm going to fix this asap.

A key of active causes bootstrap active css

If you have a field called active which is a key in reactive table it causes bootstrap to implement it's active css.

This is because reactive table puts the key name into the class of the td.

dauJmxCxDnwuzT7Ms true

Which results in not being able to use a Collection.active [boolean] field for records.

guess I could change it to isActive

Translations

What do you think of adding translation for filter and pager strings ?
If you agree, i can pull a patch which use just-i18n and provide en and fr locales.

Anthony.

parsing handlebars within a cell

Love this table, thanks for the package.

I'm relatively new at meteor and was wondering if you could help - I want to invoke a bootstrap dialog when clicking on a button within a cell of reactive table. I can use the fn param to assign a function, I have handlebars template all set up for dialog boxes and outside of reactive-table I can invoke dialogs :

{{#bootstrapModal
        triggerLabel="Delete"
        triggerIcon="fa-trash-o"
        triggerClass="btn-danger"
        id="deleteModal"
        title="Delete Item"
        closeLabel="Close it!"
        primaryLabel="Bye bye!"
        infoId=""
        infoName="Hi"
    }}
    Are you sure you want to delete:
    {{/bootstrapModal}}

Is there a way to invoke this/pass params to it using the fn feature of reactive tables?

HTML output in the table

Is there a way to pass in html to the reactive table?

Potentially as per example below, sending in an option on the field?

Thanks - LB

Template.report.settings = function() {
  return {
    fields: [
      { key: 'reference', label: 'Reference' },
      { key: 'typeName', label: 'Type' },
      { key: 'status', label: 'Status' },
      { key: '_id', label: 'Location', html: true,
        fn: function (value) {
        return '<a href="+Routes.route['view'].path({_id:value})+">View</a>'; 
        }
      },
    ],
    attrs:  { 'data-id': '_id' }
  }
};
'''

Reactive programming buttons or tag parsed from reactiveTable

EDIT: Closed as more a Meteor/bad code issue, not specific to Reactive Table.

I have a problem with timing/rendering of dynamic content based when using Template.reactiveTable as the data source.

Here is a complete explanation: http://stackoverflow.com/questions/25575845/reactive-meteor-templates-and-deps-dependency-update-a-template-when-another

The code will run effectively when I use a jQuery selector NOT from Template.reactiveTable. However, when I attempt to select the text from my reactiveTable the operation fails.

The code does on occasion work, typically after I make a significant code change and hard refresh the page, however upon subsequent page loads the problem persists.

Can I use Template.reactiveTable.rendered to set the data context and then pass it to another template to dynamically create some buttons? I need to somehow retrieve whatever content is currently rendered in the table (constantly changing) and pass it to a distinct buttons template.

I can see this being a potential add on to the table, since I aim to generate 'helper buttons' for the filter box. When user clicks on the button, it will populate the filter bar and reactiveTable will do it's things.

Feature suggestion: Add a "css" property in fields:

Fields now support fn, this is very good feature. I suggest to add another property called "css". css is a function, return a string. This string will be put into the class.
code example: in reactive_table.html source file

{{#each sortedRows}} {{#each ../fields}} {{getField ..}} {{/each}} {{/each}}

Please pay attention on the 5th line, I added {{css}}

and in leaderboard.js example, in leaderboard.js
Template.leaderboard.tableSettings = function () {
return {
fields: [
{ key: 'name', label: 'Full Name' },
{ key: 'name', label: 'First Name', fn: function (name) { return name.split(' ')[0]; } },
{ key: 'name', label:'Last Name', fn:function(name){ return name.split(' ')[1]; }},
{ key: 'score', label: 'Score',
css: function(field, object){

       if (field < 10 )
        return "class1"
       else return "class2"}
      }
    ],
    useFontAwesome: true
};

};

In this case, when a player's score is less than 10, the score cell will have a class1, otherwise class2. I can set different color to class1 or class2.
This is useful when (usually) we set negative number to red color.

This feature can be easily added into reactive_table.js inside Template.reactiveTable.helpers.

Please let me know if my description is not clear. I can give you more example.

Filter does not work in all cases

Check the demo and try to input "✘" or "partial" on filter, it is strange.
EDIT: It filters everything - I'm supposing it's because you return html on these tds?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.