SlideShare a Scribd company logo
Datagrids with Symfony 2,
Backbone and Backgrid
Eugenio Pombi & Giorgio Cefaro
requirements - composer
http://getcomposer.org
Run this in your terminal to get the latest Composer version:
curl -sS https://getcomposer.org/installer | php
Or if you don't have curl:
php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"
requirements - symfony
http://symfony.com/download
Create a symfony 2.3.1 project in path/:
php composer.phar create-project symfony/framework-standard-edition path/ 2.3.1
requirements - dependencies
composer.json:
"require": {
[...]
"friendsofsymfony/rest-bundle": "0.12",
"jms/serializer-bundle": "dev-master",
"jms/di-extra-bundle": "dev-master",
"friendsofsymfony/jsrouting-bundle": "~1.1"
},
requirements - FOSRestBundle
https://github.com/FriendsOfSymfony/FOSRestBundle
app/config/config.yml:
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
view_response_listener: 'force'
requirements - javascript libs
Download the required libs:
http://backbonejs.org/
http://underscorejs.org/
http://jquery.com/
http://backgridjs.com/
http://twitter.github.io/bootstrap/
requirements - javascript libs
Place the libraries in src/Acme/MyBundle/Resources/public/js/ and include
them with Assetic:
base.html.yml:
{% block javascripts %}
{% javascripts
'bundles/mwtbrokertool/js/di-lite.js'
'bundles/mwtbrokertool/js/jquery.js'
'bundles/mwtbrokertool/js/underscore.js'
'bundles/mwtbrokertool/js/bootstrap.js'
'bundles/mwtbrokertool/js/backbone.js'
%}
<script src="{{ asset_url }}" type="text/javascript"></script>
{% endjavascripts %}
<script src="{{ asset('/js/fos_js_routes.js') }}"></script>
{% endblock %}
controllers - index
/**
* @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"})
* @FosRestGet("/ticket.{_format}",
* name="mwt_brokertool_ticket",
* defaults={"_format": "json"},
* options={"expose"=true})
*/
public function indexAction(User $user)
{
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('MyBundle:Ticket');
$tickets = $repo->findBySellerJoinAll($user);
return $tickets;
}
controllers - new
/**
* @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"})
* @FosRestPost("/ticket.{_format}",
* name="My_bundle_ticket_new",
* defaults={"_format": "json"},
* options={"expose"=true}
* )
* @FosRestView
* @param User $user
*/
public function newAction(User $user)
{
[...]
}
controllers - new ticket
$ticket = new Ticket();
$form = $this->createForm(new TicketType(), $ticket);
$data = $this->getRequest()->request->all();
$children = $form->all();
$data = array_intersect_key($data, $children);
$form->submit($data);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($ticket);
$em->flush();
return View::create($ticket, 201);
}
return View::create($form, 400);
test index
public function testIndex()
{
$client = static::createClient();
$crawler = $client->request('GET','/'.$this->user1->getId().'/ticket');
$this->assertTrue($client->getResponse()->isSuccessful());
$json_response = json_decode($client->getResponse()->getContent(), true);
$this->assertTrue(is_array($json_response));
$this->assertTrue(isset($json_response[0]['event_id']));
$this->assertTrue(isset($json_response[1]['event_id']));
$this->assertTrue(isset($json_response[2]['event_id']));
}
test new ticket
$client = static::createClient();
$client->request(
'POST',
'/' . $this->user1->getId() . '/ticket',
array(),
array(),
array('CONTENT_TYPE' => 'application/json'),
'[aJsonString]'
);
$this->assertEquals(201, $client->getResponse()->getStatusCode());
json_response = json_decode($client->getResponse()->getContent(), true);
$this->assertTrue(is_array($json_response));
$ticket = $this->em->getRepository('ACMEMyBundle:Ticket')->findOneBy(array
(...);
$this->assertNotNull($ticket);
backgrid
backgrid
backgrid
backgridjs.com
The goal of Backgrid.js is to produce a set of
core Backbone UI elements that offer you all
the basic displaying, sorting and editing
functionalities you'd expect, and to create an
elegant API that makes extending Backgrid.js
with extra functionalities easy.
backgrid
Backgrid.js depends on 3 libraries to function:
● jquery >= 1.7.0
● underscore.js ~ 1.4.0
● backbone.js >= 0.9.10
backgrid
● Solid foundation. Based on Backbone.js.
● Semantic and easily stylable. Just style with plain CSS like
you would a normal HTML table.
● Low learning curve. Works with plain old Backbone models
and collections. Easy things are easy, hards things possible.
● Highly modular and customizable. Componenets are just
simple Backbone View classes, customization is easy if you
already know Backbone.
● Lightweight. Extra features are separated into extensions,
which keeps the bloat away.
di-lite.js
minimalistic dependency injection container
ctx.register("name", instance);
ctx.get("name");
My.Stuff = Backbone.Collection.extend({
dependencies: "name",
[...]
});
di-lite.js - example
var ctx = di.createContext();
var user = function () {
this.id = $("#grid").attr('data-user);
};
ctx.register("user", user);
var App.Collections.Articles = Backbone.Collection.extend({
dependencies: "user",
model: App.Models.Article,
url: function() {
return '/article?userId=' + this.user.id;
}
[...]
});
ctx.register("articles", App.Collections.Articles);
backbone model + collection
var Ticket = Backbone.Model.extend({});
var Tickets = Backbone.Collection.extend({
model: Territory,
url: Routing.generate('my_bundle_ticket', { userId: App.userId })
});
var tickets = new Tickets();
backbone associations
Associations allows Backbone applications to model 1:1 & 1:
N associations between application models and Collections.
https://github.com/dhruvaray/backbone-associations
var TicketGroup = Backbone.AssociatedModel.extend({
relations: [
{
type: Backbone.Many,
key: 'tickets',
relatedModel: 'Ticket'
}]
});
backgrid columns
var columns = [{
name: "event_name",
label: "Event",
cell: "string" ,
editable: false,
}, {
name: "event_datetime",
label: "Event Date",
cell: "datetime"
}];
backgrid initialize
var grid = new Backgrid.Grid({
columns: columns,
collection: tickets
});
$("#my-list").append(grid.render().$el);
// Fetch some tickets from the url
tickets.fetch({reset: true});
backgrid - computed fields
https://github.com/alexanderbeletsky/backbone-computedfields
var CartItem = Backbone.Model.extend({
initialize: function () {
this.computedFields = new Backbone.ComputedFields(this);
},
computed: {
grossPrice: {
depends: ['netPrice', 'vatRate'],
get: function (fields) {
return fields.netPrice * (1 + fields.vatRate / 100);
}
}
}
});
backgrid - computed fields
var columns = [{
name: "netPrice",
label: "Net Price",
cell: "number"
}, {
name: "vatRate",
label: "VAT Rate",
cell: "integer"
}, {
name: "grossPrice",
label: "Gross price",
cell: "number"
}];
backgrid - select editor
{
name: "country",
label: "Country",
cell: Backgrid.SelectCell.extend({
optionValues: ctx.get('countries').getAsOptions()
})
}
backgrid - select editor
App.Collections.Countries = Backbone.Collection.extend({
getAsOptions: function () {
var options = new Array();
this.models.forEach(function(item) {
options.push([item.get('name'), item.get('id')])
});
return options;
}
});
toggle cell - column definition
{
name: 'nonModelField',
label: 'Details',
editable: false,
cell: Backgrid.ToggleCell,
subtable: function(el, model) {
var subtable = new Backgrid.Grid({
columns: columns,
collection: model.get('tickets')
});
el.append(subtable.render().$el);
return subtable;
}
toggle cell - cell extension
Backgrid.ToggleCell = Backgrid.Cell.extend({
[...]
});
toggle cell - cell extension -
render
Backgrid.ToggleCell = Backgrid.Cell.extend({
[...]
render: function() {
this.$el.empty();
var new_el = $('<span class="toggle"></span>');
this.$el.append(new_el);
this.set_toggle().delegateEvents();
return this;
}
});
toggle cell - cell extension -
event
set_toggle: function() {
var self = this;
var td_el = this.$el;
td_el.find('.toggle').click( function() {
var details_row = td_el.closest('tr').next('.child-table');
if (details_row.length > 0) {
$(details_row).remove();
} else {
details_row = $('<tr class="child-table"><td colspan="100"></td></tr>');
$(this).closest('tr').after(details_row);
self.subtable = self.column.get('subtable')(details_row.find('td'), self.model);
}
});
return this;
}
retrieve data - model
App.Models.TicketGroup = Backbone.AssociatedModel.extend({
relations: [
{
type: Backbone.Many,
key: tickets,
relatedModel: 'App.Models.Ticket'
}
],
[...]
});
retrieve data - collection
App.Collections.TicketGroups = Backbone.Collection.extend({
model: App.Models.TicketGroup,
parse: function(tickets, options) {
[...]
return ticketGroups;
},
});
retrieve data - collection
var ticketGroups = [];
_.each(tickets, function (element, index, list) {
var foundElement = _.findWhere(
ticketGroups,
{event_id: element.event_id}
)
if (foundElement == null) {
ticketGroups.push({
"event_id": element.event_id,
"event_name": element.event_name,
"tickets": [element]
});
} else {
foundElement.tickets.push(element);
}
}, this);
testing!
describe("TicketGroups Collection", function () {
describe("parse", function () {
beforeEach(function () {
this.ticketGroupCollection = new App.Collections.TicketGroups();
});
it("parse should return a ticketGroup with nested tickets", function ()
{
var jsonWith3Records = [...];
var result = this.ticketGroupCollection.parse(jsonWith3Records, {});
result.should.have.length(2);
var firstResult = result[0];
firstResult.event_name.should.equal("Concerto Iron Maiden");
firstResult.tickets.should.have.length(2);
var secondResult = result[1];
secondResult.event_name.should.equal("Battle Hymns Tour");
secondResult.tickets.should.have.length(1);
//close brackets
thanks
@giorrrgio
giorgiocefaro.com
@euxpom
nerd2business.net

More Related Content

What's hot (20)

PDF
"Mobage DBA Fight against Big Data" - NHN TE
Ryosuke IWANAGA
 
PDF
Writing Redis in Python with asyncio
James Saryerwinnie
 
PDF
And now you have two problems. Ruby regular expressions for fun and profit by...
Codemotion
 
PDF
Symfony2 revealed
Fabien Potencier
 
PDF
Advanced symfony Techniques
Kris Wallsmith
 
PPTX
A Functional Guide to Cat Herding with PHP Generators
Mark Baker
 
PDF
Doctrine MongoDB ODM (PDXPHP)
Kris Wallsmith
 
PDF
A Little Backbone For Your App
Luca Mearelli
 
PDF
Kickin' Ass with Cache-Fu (without notes)
err
 
PDF
Kickin' Ass with Cache-Fu (with notes)
err
 
PDF
Controlling The Cloud With Python
Luca Mearelli
 
PDF
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Kacper Gunia
 
KEY
Api Design
sartak
 
PDF
Introducing Assetic (NYPHP)
Kris Wallsmith
 
PDF
Introduction to Nodejs
Gabriele Lana
 
PDF
Ansible leveraging 2.0
bcoca
 
PPTX
Read, store and create xml and json
Kim Berg Hansen
 
PPTX
Psycopg2 - Connect to PostgreSQL using Python Script
Survey Department
 
PPTX
Python mongo db-training-europython-2011
Andreas Jung
 
PDF
Clojure and the Web
nickmbailey
 
"Mobage DBA Fight against Big Data" - NHN TE
Ryosuke IWANAGA
 
Writing Redis in Python with asyncio
James Saryerwinnie
 
And now you have two problems. Ruby regular expressions for fun and profit by...
Codemotion
 
Symfony2 revealed
Fabien Potencier
 
Advanced symfony Techniques
Kris Wallsmith
 
A Functional Guide to Cat Herding with PHP Generators
Mark Baker
 
Doctrine MongoDB ODM (PDXPHP)
Kris Wallsmith
 
A Little Backbone For Your App
Luca Mearelli
 
Kickin' Ass with Cache-Fu (without notes)
err
 
Kickin' Ass with Cache-Fu (with notes)
err
 
Controlling The Cloud With Python
Luca Mearelli
 
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Kacper Gunia
 
Api Design
sartak
 
Introducing Assetic (NYPHP)
Kris Wallsmith
 
Introduction to Nodejs
Gabriele Lana
 
Ansible leveraging 2.0
bcoca
 
Read, store and create xml and json
Kim Berg Hansen
 
Psycopg2 - Connect to PostgreSQL using Python Script
Survey Department
 
Python mongo db-training-europython-2011
Andreas Jung
 
Clojure and the Web
nickmbailey
 

Similar to Datagrids with Symfony 2, Backbone and Backgrid (20)

PDF
using Mithril.js + postgREST to build and consume API's
Antônio Roberto Silva
 
PDF
Code Splitting in Practice - Shanghai JS Meetup May 2016
Wiredcraft
 
PDF
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Fabio Franzini
 
PPTX
Rails Engine | Modular application
mirrec
 
PDF
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Universitat Politècnica de Catalunya
 
PDF
IOC + Javascript
Brian Cavalier
 
PPTX
Lambdas puzzler - Peter Lawrey
JAXLondon_Conference
 
PPTX
Lecture: Webpack 4
Sergei Iastrebov
 
PDF
backend
tutorialsruby
 
PDF
backend
tutorialsruby
 
PDF
Webpack: your final module bundler
Andrea Giannantonio
 
PPTX
Designing REST API automation tests in Kotlin
Dmitriy Sobko
 
KEY
JavaScript Growing Up
David Padbury
 
PDF
Symfony2 - from the trenches
Lukas Smith
 
KEY
How and why i roll my own node.js framework
Ben Lin
 
PPT
Laurens Van Den Oever Xopus Presentation
Ajax Experience 2009
 
PPSX
RequireJS
Tim Doherty
 
PDF
The Ring programming language version 1.8 book - Part 95 of 202
Mahmoud Samir Fayed
 
PDF
Frontend JS workflow - Gulp 4 and the like
Damien Seguin
 
PDF
Play 2.0
elizhender
 
using Mithril.js + postgREST to build and consume API's
Antônio Roberto Silva
 
Code Splitting in Practice - Shanghai JS Meetup May 2016
Wiredcraft
 
WebNet Conference 2012 - Designing complex applications using html5 and knock...
Fabio Franzini
 
Rails Engine | Modular application
mirrec
 
Deep Learning for Computer Vision: Software Frameworks (UPC 2016)
Universitat Politècnica de Catalunya
 
IOC + Javascript
Brian Cavalier
 
Lambdas puzzler - Peter Lawrey
JAXLondon_Conference
 
Lecture: Webpack 4
Sergei Iastrebov
 
backend
tutorialsruby
 
backend
tutorialsruby
 
Webpack: your final module bundler
Andrea Giannantonio
 
Designing REST API automation tests in Kotlin
Dmitriy Sobko
 
JavaScript Growing Up
David Padbury
 
Symfony2 - from the trenches
Lukas Smith
 
How and why i roll my own node.js framework
Ben Lin
 
Laurens Van Den Oever Xopus Presentation
Ajax Experience 2009
 
RequireJS
Tim Doherty
 
The Ring programming language version 1.8 book - Part 95 of 202
Mahmoud Samir Fayed
 
Frontend JS workflow - Gulp 4 and the like
Damien Seguin
 
Play 2.0
elizhender
 
Ad

More from eugenio pombi (7)

ODP
Parlo al mio codice
eugenio pombi
 
PDF
Processing one year of leading for Pug roma
eugenio pombi
 
PDF
Codemotion workshop
eugenio pombi
 
PDF
Arduino - il mio primo sketch
eugenio pombi
 
ODP
PHPUnit elevato alla Symfony2
eugenio pombi
 
PDF
Appetite comes with testing
eugenio pombi
 
ODP
breve introduzione a node.js
eugenio pombi
 
Parlo al mio codice
eugenio pombi
 
Processing one year of leading for Pug roma
eugenio pombi
 
Codemotion workshop
eugenio pombi
 
Arduino - il mio primo sketch
eugenio pombi
 
PHPUnit elevato alla Symfony2
eugenio pombi
 
Appetite comes with testing
eugenio pombi
 
breve introduzione a node.js
eugenio pombi
 
Ad

Recently uploaded (20)

PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PPTX
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PDF
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PPTX
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
PPTX
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PPTX
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
PDF
Advancing WebDriver BiDi support in WebKit
Igalia
 
PPTX
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
PDF
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
Using FME to Develop Self-Service CAD Applications for a Major UK Police Force
Safe Software
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
WooCommerce Workshop: Bring Your Laptop
Laura Hartwig
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
From Sci-Fi to Reality: Exploring AI Evolution
Svetlana Meissner
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
CIFDAQ Market Insights for July 7th 2025
CIFDAQ
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
Webinar: Introduction to LF Energy EVerest
DanBrown980551
 
Advancing WebDriver BiDi support in WebKit
Igalia
 
"Autonomy of LLM Agents: Current State and Future Prospects", Oles` Petriv
Fwdays
 
CIFDAQ Token Spotlight for 9th July 2025
CIFDAQ
 

Datagrids with Symfony 2, Backbone and Backgrid

  • 1. Datagrids with Symfony 2, Backbone and Backgrid Eugenio Pombi & Giorgio Cefaro
  • 2. requirements - composer http://getcomposer.org Run this in your terminal to get the latest Composer version: curl -sS https://getcomposer.org/installer | php Or if you don't have curl: php -r "eval('?>'.file_get_contents('https://getcomposer.org/installer'));"
  • 3. requirements - symfony http://symfony.com/download Create a symfony 2.3.1 project in path/: php composer.phar create-project symfony/framework-standard-edition path/ 2.3.1
  • 4. requirements - dependencies composer.json: "require": { [...] "friendsofsymfony/rest-bundle": "0.12", "jms/serializer-bundle": "dev-master", "jms/di-extra-bundle": "dev-master", "friendsofsymfony/jsrouting-bundle": "~1.1" },
  • 6. requirements - javascript libs Download the required libs: http://backbonejs.org/ http://underscorejs.org/ http://jquery.com/ http://backgridjs.com/ http://twitter.github.io/bootstrap/
  • 7. requirements - javascript libs Place the libraries in src/Acme/MyBundle/Resources/public/js/ and include them with Assetic: base.html.yml: {% block javascripts %} {% javascripts 'bundles/mwtbrokertool/js/di-lite.js' 'bundles/mwtbrokertool/js/jquery.js' 'bundles/mwtbrokertool/js/underscore.js' 'bundles/mwtbrokertool/js/bootstrap.js' 'bundles/mwtbrokertool/js/backbone.js' %} <script src="{{ asset_url }}" type="text/javascript"></script> {% endjavascripts %} <script src="{{ asset('/js/fos_js_routes.js') }}"></script> {% endblock %}
  • 8. controllers - index /** * @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"}) * @FosRestGet("/ticket.{_format}", * name="mwt_brokertool_ticket", * defaults={"_format": "json"}, * options={"expose"=true}) */ public function indexAction(User $user) { $em = $this->getDoctrine()->getManager(); $repo = $em->getRepository('MyBundle:Ticket'); $tickets = $repo->findBySellerJoinAll($user); return $tickets; }
  • 9. controllers - new /** * @ParamConverter("user", class="MyBundle:User", options={"id" = "userId"}) * @FosRestPost("/ticket.{_format}", * name="My_bundle_ticket_new", * defaults={"_format": "json"}, * options={"expose"=true} * ) * @FosRestView * @param User $user */ public function newAction(User $user) { [...] }
  • 10. controllers - new ticket $ticket = new Ticket(); $form = $this->createForm(new TicketType(), $ticket); $data = $this->getRequest()->request->all(); $children = $form->all(); $data = array_intersect_key($data, $children); $form->submit($data); if ($form->isValid()) { $em = $this->getDoctrine()->getManager(); $em->persist($ticket); $em->flush(); return View::create($ticket, 201); } return View::create($form, 400);
  • 11. test index public function testIndex() { $client = static::createClient(); $crawler = $client->request('GET','/'.$this->user1->getId().'/ticket'); $this->assertTrue($client->getResponse()->isSuccessful()); $json_response = json_decode($client->getResponse()->getContent(), true); $this->assertTrue(is_array($json_response)); $this->assertTrue(isset($json_response[0]['event_id'])); $this->assertTrue(isset($json_response[1]['event_id'])); $this->assertTrue(isset($json_response[2]['event_id'])); }
  • 12. test new ticket $client = static::createClient(); $client->request( 'POST', '/' . $this->user1->getId() . '/ticket', array(), array(), array('CONTENT_TYPE' => 'application/json'), '[aJsonString]' ); $this->assertEquals(201, $client->getResponse()->getStatusCode()); json_response = json_decode($client->getResponse()->getContent(), true); $this->assertTrue(is_array($json_response)); $ticket = $this->em->getRepository('ACMEMyBundle:Ticket')->findOneBy(array (...); $this->assertNotNull($ticket);
  • 15. backgrid backgridjs.com The goal of Backgrid.js is to produce a set of core Backbone UI elements that offer you all the basic displaying, sorting and editing functionalities you'd expect, and to create an elegant API that makes extending Backgrid.js with extra functionalities easy.
  • 16. backgrid Backgrid.js depends on 3 libraries to function: ● jquery >= 1.7.0 ● underscore.js ~ 1.4.0 ● backbone.js >= 0.9.10
  • 17. backgrid ● Solid foundation. Based on Backbone.js. ● Semantic and easily stylable. Just style with plain CSS like you would a normal HTML table. ● Low learning curve. Works with plain old Backbone models and collections. Easy things are easy, hards things possible. ● Highly modular and customizable. Componenets are just simple Backbone View classes, customization is easy if you already know Backbone. ● Lightweight. Extra features are separated into extensions, which keeps the bloat away.
  • 18. di-lite.js minimalistic dependency injection container ctx.register("name", instance); ctx.get("name"); My.Stuff = Backbone.Collection.extend({ dependencies: "name", [...] });
  • 19. di-lite.js - example var ctx = di.createContext(); var user = function () { this.id = $("#grid").attr('data-user); }; ctx.register("user", user); var App.Collections.Articles = Backbone.Collection.extend({ dependencies: "user", model: App.Models.Article, url: function() { return '/article?userId=' + this.user.id; } [...] }); ctx.register("articles", App.Collections.Articles);
  • 20. backbone model + collection var Ticket = Backbone.Model.extend({}); var Tickets = Backbone.Collection.extend({ model: Territory, url: Routing.generate('my_bundle_ticket', { userId: App.userId }) }); var tickets = new Tickets();
  • 21. backbone associations Associations allows Backbone applications to model 1:1 & 1: N associations between application models and Collections. https://github.com/dhruvaray/backbone-associations var TicketGroup = Backbone.AssociatedModel.extend({ relations: [ { type: Backbone.Many, key: 'tickets', relatedModel: 'Ticket' }] });
  • 22. backgrid columns var columns = [{ name: "event_name", label: "Event", cell: "string" , editable: false, }, { name: "event_datetime", label: "Event Date", cell: "datetime" }];
  • 23. backgrid initialize var grid = new Backgrid.Grid({ columns: columns, collection: tickets }); $("#my-list").append(grid.render().$el); // Fetch some tickets from the url tickets.fetch({reset: true});
  • 24. backgrid - computed fields https://github.com/alexanderbeletsky/backbone-computedfields var CartItem = Backbone.Model.extend({ initialize: function () { this.computedFields = new Backbone.ComputedFields(this); }, computed: { grossPrice: { depends: ['netPrice', 'vatRate'], get: function (fields) { return fields.netPrice * (1 + fields.vatRate / 100); } } } });
  • 25. backgrid - computed fields var columns = [{ name: "netPrice", label: "Net Price", cell: "number" }, { name: "vatRate", label: "VAT Rate", cell: "integer" }, { name: "grossPrice", label: "Gross price", cell: "number" }];
  • 26. backgrid - select editor { name: "country", label: "Country", cell: Backgrid.SelectCell.extend({ optionValues: ctx.get('countries').getAsOptions() }) }
  • 27. backgrid - select editor App.Collections.Countries = Backbone.Collection.extend({ getAsOptions: function () { var options = new Array(); this.models.forEach(function(item) { options.push([item.get('name'), item.get('id')]) }); return options; } });
  • 28. toggle cell - column definition { name: 'nonModelField', label: 'Details', editable: false, cell: Backgrid.ToggleCell, subtable: function(el, model) { var subtable = new Backgrid.Grid({ columns: columns, collection: model.get('tickets') }); el.append(subtable.render().$el); return subtable; }
  • 29. toggle cell - cell extension Backgrid.ToggleCell = Backgrid.Cell.extend({ [...] });
  • 30. toggle cell - cell extension - render Backgrid.ToggleCell = Backgrid.Cell.extend({ [...] render: function() { this.$el.empty(); var new_el = $('<span class="toggle"></span>'); this.$el.append(new_el); this.set_toggle().delegateEvents(); return this; } });
  • 31. toggle cell - cell extension - event set_toggle: function() { var self = this; var td_el = this.$el; td_el.find('.toggle').click( function() { var details_row = td_el.closest('tr').next('.child-table'); if (details_row.length > 0) { $(details_row).remove(); } else { details_row = $('<tr class="child-table"><td colspan="100"></td></tr>'); $(this).closest('tr').after(details_row); self.subtable = self.column.get('subtable')(details_row.find('td'), self.model); } }); return this; }
  • 32. retrieve data - model App.Models.TicketGroup = Backbone.AssociatedModel.extend({ relations: [ { type: Backbone.Many, key: tickets, relatedModel: 'App.Models.Ticket' } ], [...] });
  • 33. retrieve data - collection App.Collections.TicketGroups = Backbone.Collection.extend({ model: App.Models.TicketGroup, parse: function(tickets, options) { [...] return ticketGroups; }, });
  • 34. retrieve data - collection var ticketGroups = []; _.each(tickets, function (element, index, list) { var foundElement = _.findWhere( ticketGroups, {event_id: element.event_id} ) if (foundElement == null) { ticketGroups.push({ "event_id": element.event_id, "event_name": element.event_name, "tickets": [element] }); } else { foundElement.tickets.push(element); } }, this);
  • 35. testing! describe("TicketGroups Collection", function () { describe("parse", function () { beforeEach(function () { this.ticketGroupCollection = new App.Collections.TicketGroups(); }); it("parse should return a ticketGroup with nested tickets", function () { var jsonWith3Records = [...]; var result = this.ticketGroupCollection.parse(jsonWith3Records, {}); result.should.have.length(2); var firstResult = result[0]; firstResult.event_name.should.equal("Concerto Iron Maiden"); firstResult.tickets.should.have.length(2); var secondResult = result[1]; secondResult.event_name.should.equal("Battle Hymns Tour"); secondResult.tickets.should.have.length(1); //close brackets