Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically filtering by parent node in nested JSON (BackboneJS)

I know the question title is a bit confusing so please excuse me- hopefully I can explain my problem.

I have a data structure like so:

{
  "_data": {
    "Test Alignment Form": [
      {
        "review_form": "Test Alignment Form",
        "rvee_uid": "52",
        "firstName": "Joe",
        "lastName": "Bloggs",
        "status": "NOT_STARTED",
        "status_clean": "Not started"
      },
      {
        "review_form": "Test Alignment Form",
        "rvee_uid": "54",
        "firstName": "Steve",
        "lastName": "Stevenson",
        "status": "NOT_STARTED",
        "status_clean": "Not started"
      },
      {
        "review_form": "Test Alignment Form",
        "rvee_uid": "13",
        "firstName": "Anne",
        "lastName": "Boleyn",
        "status": "COMPLETED",
        "status_clean": "Completed"
      }
    ],
    "Another Form": [
      {
        "review_form": "Another Form",
        "rvee_uid": "10",
        "firstName": "Luther",
        "lastName": "Vandross",
        "status": "NEVER_TOO_MUCH",
        "status_clean": "Never too much, never too much... duh duh duh"
      },
      {
        "review_form": "Another Form",
        "rvee_uid": "54",
        "firstName": "Steve",
        "lastName": "Stevenson",
        "status": "NOT_STARTED",
        "status_clean": "Not started"
      },
      {
        "review_form": "Another Form",
        "rvee_uid": "13",
        "firstName": "Anne",
        "lastName": "Boleyn",
        "status": "COMPLETED",
        "status_clean": "Completed"
      }
    ]
  },
  "_meta": {
    "generated": 1397642209,
    "length": 62,
    "duration": 3,
    "author": 0
  }
}

And my code is currently like this:

window.app = app = window.app or
  models:      {}
  collections: {}
  views:       {}
  routes:      {}
  init:        () ->
    console.log 'Initialised app.'
    app._app = new app.views.table()

#-----------------------------------------------------------------------------#

app.models.form = Backbone.RelationalModel.extend
  defaults:
    firstName:    ""
    lastName:     ""
    review_form:  ""
    rvee_uid:     "0"
    status:       ""
    status_clean: "UNKNOWN"

  initialize: () ->
    console.log "Initialised model 'form'."



app.collections.mainCollection = Backbone.Collection.extend
  model: app.models.form
  url: "data/data.json"
  initialize: (models, options) ->
    console.log "Initialised collection 'mainCollection'."
    @options = options

    @fetch reset: true

    @on "reset", () ->
      console.log "Collection reset!"



app.views.tableItem = Backbone.View.extend

  tagName: 'li'

  template: _.template $('#row').html()

  initialize: () ->
    console.log "Initialised view 'tableItem'."

  render: () ->
    console.log "Rendered view 'tableItem'."
    @$el.html @template @model.toJSON()
    @



app.views.table = Backbone.View.extend

  el: '#table'

  initialize: (data) ->
    console.log "Initialised view 'table'."
    @collection = new app.collections.mainCollection data

    @listenTo @collection, 'reset', () ->
      @render()

  render: () ->
    console.log "Rendered view 'table'."
    @$el.empty()
    console.log @collection.models
    _.each @collection.models, (_item) =>
      @renderItem(_item)

  renderItem: (_item) ->
    item = new app.views.tableItem
      model: _item

    @$el.append item.render().el


#-----------------------------------------------------------------------------#

app.init()

(Please bear in mind that I have a bigger, working version, but with an un-nested structure, so this is just for sandboxing).

Anyway, so imagine that I have another view that contains a select dropdown input, populated by "Test Alignment Form" and "Another Form". When I select one, I want the models returned to be children of that form. So, the equivalent of parsing out @['_data']['Test Alignment Form']. I want to be able to have access to the "_meta" object too, as I would like to be able to print out the generated date in another view, for example. Does anyone know of any best practices for achieving this? I've been pulling my hair out!

Thanks :)

like image 548
Matt Fletcher Avatar asked Oct 20 '22 09:10

Matt Fletcher


1 Answers

So.. hmm you have a collection of collections for one. Your data is a collection that contains forms. Forms are a collection of entries.

your form collection should take in your data structure.

then it should make form models. those form models have entry collections.

FormCollection

  parse: (res) ->

    {@_meta} = res
    _.map res._data, (data, formName) =>
      {formName, data}  # we have 2 attributes on the form model

FormModel

  initialize: ->
    @on 'reset', ->
      # Since each form also has a collection of entries, we create a collection
      @entries ?= new EntryCollection
      @entries.parent = this # this circular dependency will create memory leaks
      @entries.reset @get('data'), parse: true #remember data is an array

EntryCollection

  parse: (res) ->
    @meta = parent.collection._meta
    res

EntryModel

models within EntryCollection can access @collection.meta

You should note that this sort of nesting is prone to memory leaks, so if your page stays open for days, you should consider delete @parent, etc, but you might not need to worry about it.

This is just a first shot at it, you could probably make improvements, but if you will be building this up more, you want to have a model for every object and a collection for every array. your _data is actually array.

you have

"_data": {
  "Test Alignment Form": [
    {
      "review_form": "Test Alignment Form",
      "rvee_uid": "52",
      "firstName": "Joe",
      "lastName": "Bloggs",
      "status": "NOT_STARTED",
      "status_clean": "Not started"
    },
    {
      "review_form": "Test Alignment Form",
      "rvee_uid": "54",
      "firstName": "Steve",
      "lastName": "Stevenson",
      "status": "NOT_STARTED",
      "status_clean": "Not started"
    },
    {
      "review_form": "Test Alignment Form",
      "rvee_uid": "13",
      "firstName": "Anne",
      "lastName": "Boleyn",
      "status": "COMPLETED",
      "status_clean": "Completed"
    }
  ],
  "Another Form": [
    {
      "review_form": "Another Form",
      "rvee_uid": "10",
      "firstName": "Luther",
      "lastName": "Vandross",
      "status": "NEVER_TOO_MUCH",
      "status_clean": "Never too much, never too much... duh duh duh"
    },
    {
      "review_form": "Another Form",
      "rvee_uid": "54",
      "firstName": "Steve",
      "lastName": "Stevenson",
      "status": "NOT_STARTED",
      "status_clean": "Not started"
    },
    {
      "review_form": "Another Form",
      "rvee_uid": "13",
      "firstName": "Anne",
      "lastName": "Boleyn",
      "status": "COMPLETED",
      "status_clean": "Completed"
    }
  ]
},

it should be

"_data": [
  {
    name: "Test Alignment Form",
    contents: [
      {
        "review_form": "Test Alignment Form",
        "rvee_uid": "52",
        "firstName": "Joe",
        "lastName": "Bloggs",
        "status": "NOT_STARTED",
        "status_clean": "Not started"
      },
      {
        "review_form": "Test Alignment Form",
        "rvee_uid": "54",
        "firstName": "Steve",
        "lastName": "Stevenson",
        "status": "NOT_STARTED",
        "status_clean": "Not started"
      },
      {
        "review_form": "Test Alignment Form",
        "rvee_uid": "13",
        "firstName": "Anne",
        "lastName": "Boleyn",
        "status": "COMPLETED",
        "status_clean": "Completed"
      }
    ],
  },
  {
    name: "Another Form",
    contents: [
      {
        "review_form": "Another Form",
        "rvee_uid": "10",
        "firstName": "Luther",
        "lastName": "Vandross",
        "status": "NEVER_TOO_MUCH",
        "status_clean": "Never too much, never too much... duh duh duh"
      },
      {
        "review_form": "Another Form",
        "rvee_uid": "54",
        "firstName": "Steve",
        "lastName": "Stevenson",
        "status": "NOT_STARTED",
        "status_clean": "Not started"
      },
      {
        "review_form": "Another Form",
        "rvee_uid": "13",
        "firstName": "Anne",
        "lastName": "Boleyn",
        "status": "COMPLETED",
        "status_clean": "Completed"
      }
    ],
  },
];

unless I am misunderstanding, but I think Another Form is user generated.. there could be infinite forms right?

In regard to your follow up

FormView

  render: ->

    @$el.empty().append formTemplate(this)
    @model.entries.each (model) =>

      # if you need more speed or re-render often, you can cache these views later
      entryView = new EntryView {model}
      # assumes you have an entries-container in your form template
      @$('.entries-container').append entryView.render().el
like image 113
dansch Avatar answered Oct 27 '22 10:10

dansch