Skip to content

Instantly share code, notes, and snippets.

@diegomanuel
Created December 22, 2016 17:55
Show Gist options
  • Save diegomanuel/53675de7266ef70e0cad3ae0b41ed5cf to your computer and use it in GitHub Desktop.
Save diegomanuel/53675de7266ef70e0cad3ae0b41ed5cf to your computer and use it in GitHub Desktop.
Ejemplos de CoffeeScript
#=require usecases/app/undeleteObjectModal
#=require usecases/app/confirmModal
window.defaultCRUDApiValues = (crudName, hashName, extensions = {}) -> _.extend({
create:
call: Api["create#{capitalize(crudName)}"]
url: "#{hashName}/agregar"
update:
call: Api["update#{capitalize(crudName)}"]
url: "#{hashName}/editar"
detail:
call: Api["detail#{capitalize(crudName)}"]
url: "#{hashName}/detalle"
search:
call: Api["search#{capitalize(crudName)}"]
url: "#{hashName}/buscar" # Not used..
remoteUrl: Api.url[crudName]?.search()
}, extensions)
class window.CRUDModel extends DynamicModel
@_entity: "ModelName" # Should be overriden!
@api: # Should be overriden!
create: { call: (->), url: "" }
update: { call: (->), url: "" }
detail: { call: (->), url: "" }
delete: { call: (->), url: "" }
list: { call: (->), url: "" }
search: { call: (->), url: "" }
@detailLinkPermission: undefined
initialize: -> super
construct: -> super
entity: -> @constructor._entity
PK: -> parseInt @get "id"
isUpdating: -> @id > 0
isCreating: -> !@isUpdating()
isPersistent: -> @isUpdating()
isDeleted: -> !!@get("deleted")
submitId: -> @attr undefined, "id"
iconizedStatus: ->
color = "gray"
title = App.keyToText "NO_DISPONIBLE"
if @get "deleted"
color = "white"
title = App.keyToText "STATUS_DELETED"
obj = CRUDView.getIconizedStatusObjectFromColor color
obj.attr "title", App.keyToText title
CRUDView.parseIconizedStatusObject obj
remove: ( func="remove" )->
@collection[func](@) if @collection?[func]?
attr: ( createAttr, updateAttr )->
attr = if @isCreating() then createAttr else updateAttr
@attributes[ attr ]
bindForm: ( aDeclarativeForm )->
@form = aDeclarativeForm
hashCreateURL: -> @constructor.urlComponentCreate()
hashUpdateURL: -> @constructor.urlComponentUpdate @PK()
hashDetailURL: -> @constructor.urlComponentDetail @PK()
fetchDetailData: ( onSuccess )->
success = ( response )=>
response = @onFetchDetailData response
onSuccess? response
@_fetchData "detail", success
onFetchDetailData: ( response )-> response
# By default, load detail data as form data
fetchFormData: ( onSuccess )->
return if !(id = @get "id")
success = ( response )=>
response = @onFetchFormData response
onSuccess? response
@_fetchData "detail", success
onFetchFormData: ( response )-> response
# By default, load detail data when App.getParam "id"
fetchDataByID: ( onSuccess )->
@fetchDetailData onSuccess
# Sets fetched data as model attributes
_fetchData: ( wich="detail", onSuccess )->
if (apiCall = @constructor.api[wich])
apiCall.call @get("id"), ( response )=>
response = @_fetchDataFormatter response
@set response
onSuccess? response
else
onSuccess? @attributes
_fetchDataFormatter: ( response )-> response
validateExistance: ->
indetifyKey = @form.identifyKeyModel()
entityApiName = @constructor.entityApiName.toLowerCase()
onExistanceResponse = (response)=>
if(response.status == "BORRADO")
apiCallFunction = (entityId, callback, options) =>
callPostAPI( {url: Api.url[entityApiName].undelete(),data: {id: entityId}}, callback, handleError500, options)
new UndeleteObjectModal({apiCallFunction: apiCallFunction, hashToNavigate: @constructor.api.detail.url, entityId: response.id}).show()
callPostAPI( {url: Api.url[entityApiName].exists(),data: {idString: @get(indetifyKey)}}, onExistanceResponse, handleError500)
entityApiName: -> @constructor.entityApiName.toLowerCase()
permissionTagHeader: -> @constructor.permissionTagHeader
crudFeatures: ->
@constructor.crudFeatures()
executeDelete: ->
this.confirmEntityOperation(@doExecuteDelete, 'eliminar')
confirmEntityOperation: (operation, operationName) ->
new ConfirmModal({onConfirm: operation, title: "#{capitalize(operationName)} entidad", message: "¿Está seguro que desea #{operationName} la entidad?" }).show()
executeUndelete: ->
this.confirmEntityOperation(@doExecuteUndelete, 'recuperar')
doExecuteAction: (api, actionNameExecuted, event) ->
onSuccess = (response) =>
App.logMessage("Entidad #{actionNameExecuted}")
this.trigger(event)
callPostAPI( {url: api,data: {id: @get('id')}}, onSuccess, handleError500)
doExecuteDelete: =>
this.doExecuteAction(Api.url[@entityApiName()].delete(), 'eliminada', 'onDelete')
doExecuteUndelete: =>
this.doExecuteAction(Api.url[@entityApiName()].undelete(), 'recuperada', 'onUndelete')
########### CLASS METHODS ###########
@crudFeatures: ->
crudFeatures[@entityApiName.toLowerCase()] || []
@getInstance: ( options={} )-> # VERY important to be "->"
new @ options
@urlComponent: -> @_entity.toLowerCase()
@urlComponentCreate: -> @api.create.url
@urlComponentUpdate: ( id )-> "#{@api.update.url}?id=#{id}"
@urlComponentDetail: ( id )-> "#{@api.detail.url}?id=#{id}"
@parseDetailLink: ( id, text, permission )->
return text unless id and text and User.can (permission || @detailLinkPermission)
href = @urlComponentDetail id
CRUDView.parseDetailLink {href, id, text}
@fetchMarkets: ( success=(->), error=(->) )=>
@fetch "markets", "getMarkets", "mercados", "mercados", success, error
@fetchCountries: ( success=(->), error=(->) )=>
@fetch "countries", "getCountries", "zonas", "países", success, error
@fetchRoles: ( success=(->), error=(->) )=>
@fetch "roles", "getRoles", "roles", "roles y permisos", success, error
@fetchRadios: ( success=(->), error=(->) )=>
@fetch "radios", "getRadiosCRUD", "radios", "radios", success, error
@fetchFleets: ( success=(->), error=(->) )=>
@fetch "radios", "getFleetsCRUD", "flotillas", "flotillas", success, error
@findRole: ( roleId, callback=null )=>
# @TODO DMC: Aplicar el uso opcional de callback,
# que sólo tendría sentido si los roles aun no fueron fetched,
# pero mejor forzarlo asi.. que pasen un callback que recibirá
# como parámetro el rol encontrado, sin importar si están
# cargados o no al momento de llamar este método!
if (roles = @::roles) and roles.length is 0 then throw "Roles were not loaded from server!"
for role in roles
return role if role.name is roleId
throw "The role '#{roleId}' was not found within defined roles!"
activo: ->
if @get("deleted") then "No" else "Sí"
# Creates instance properties from api call responses
@fetch: ( attr, apiGetter, key, wtf, success, error )=>
obj = @:: #@TODO DMC: Y si uso window o local storage??
# Wait a little bit if we are getting data from server for given apiGetter
if @_fetching[apiGetter]
_fetch = =>
@fetch attr, apiGetter, key, wtf, success, error
return setTimeout _fetch, 100
# Tweaks for DeclarativeForm fetcher
#@TODO: No debería hacerse esto!
if success?.onSuccess? then success = success.onSuccess
else if !success then success = (->)
if error?.onError? then error = error.onError
else if !error then error = (->)
# Return if we already fetched the data!
return success obj[attr] if obj[attr] isnt undefined
# OK, fetch fresh data from server..
onSuccess = ( rs )=>
@_fetching[apiGetter] = false
obj[attr] = (if key? then rs[key] else rs)
success rs[key] if success
onError = ( jqXHR )=>
@_fetching[apiGetter] = false
App.errorMessage "No se pudo cargar la lista de <b>#{wtf}</b>.<br/><br/>Intente nuevamente en unos instantes."
error jqXHR if error
@_fetching[apiGetter] = attr
Api[apiGetter] onSuccess, onError
@_fetching: {}
#= require models
class window.CRUDView extends Backbone.View
@_entity: CRUDModel
#__$: @constructor
entity: -> @constructor._entity
genericEvents:
"click .cancel" : -> @constructor.goBack()
initialize: ->
super
@$el.attr "class", "#{@_className} #{@className}"
@events = _.extend @genericEvents, @viewEvents, @events
@preInit()
@doInit()
@postInit()
preInit: ->
postInit: ->
doInit: ->
#@TODO DMC: Revisar.. me gustaria que sea como doRender.
#Debería estar vacío y jamás tendría que ser necesario
#llamar "super" en ningún doAlgo(), ni init ni render, etc!
if ((id = App.getParam("id")) && !@options.embedded && (! (@options.model? && @options.model.get('id')) ))
@model = @newModel {id} # Important to assing to @model right here!
@model.fetchDataByID => @setModel @model, true
else
@model = (@options.model ? @newModel())
@setModel @model
render: ->
super
@preRender()
@doRender()
@postRender()
@delegateEvents()
@$el
preRender: ->
postRender: ->
doRender: ->
delegateEvents: -> super # Does nothing, for now
remove: =>
@mapClose()
super()
crudFeatures: ->
@entity().crudFeatures()
newModel: ( options={} )-> # @TODO DMC: Revisar uso de CRUDFormView.modelForm y CRUDListView.modelList
@entity().getInstance options
setModel: ( model, render=false )->
@model = model
@render() if render
template: ( tpl=@tpl, view )->
throw("No template defined!") unless tpl and view
"templates/#{tpl}/#{view}"
templateView: ( view )->
@template @tpl, view
parseView: ( view, opts={} )->
@parse @templateView(view), opts
parseModule: ( opts={} )->
@parse @template(), opts
parse: ( view, opts )->
opts = _.extend @model.toJSON(), {@model}, opts
JST[view] opts
showDetailView: ( object, hash=@navHashDetailView(object))->
#viewsCommunication.set "objectForCRUDDetailView", object
#object.fetchDetailData ->App.navigator.navigateToHash hash, {id: object.get('id')}
App.navigator.navigateToHash hash, {id: object.get('id')}
showFormView: ( object, hash=@navHashFormView(object))->
#viewsCommunication.set "objectForCRUDFormView", object
#object.fetchFormData ->App.navigator.navigateToHash hash, {id: object.get('id')}
App.navigator.navigateToHash hash, {id: object.get('id')}
navHashDetailView: ( object )->
#@TODO DMC: Este método no debería existir!
@entity().api.detail.url
navHashFormView: ( object )->
#@TODO DMC: Este método no debería existir!
what = if object.isUpdating() then "update" else "create"
@entity().api[what].url
sendMessage: ( options={} )->
options = _.extend {
#entityId
title: "Enviar Mensaje"
addApi: null
}, options
new SendMessageModal options
createMap: ( container )=>
@map = new EcoTaxiGMap container[0]
@map.reposition = true
@map.parseInfoWindow = @infoWindowMap
@map
updateMap: ( rs )=> # Should be overriden!
@mapUpdate []
infoWindowMap: ( gMarker, marker )=>
if @constructor.infoWindowMapVars? # The view class should have a class method @infoWindowMapVars defined!
iwinvars = @constructor.infoWindowMapVars gMarker, marker
return @map.renderInfoWindow iwinvars
null
refreshMap: ( callback=@updateMap, apiFunc=@entity().api.mapLocations )=>
return unless @map?
return if !@map.container.is ":visible" # FIX HORRENDOOOOOOOOO...!!!
###
App.showLoading @map.container
success = ( rs )=>
App.hideLoading @map.container
callback rs
apiFunc?.call success
###
apiFunc?.call callback
mapOpen: ( hide=null )=>
@createMap() unless @map?
refresh = @refreshMap
if hide? then hide.fadeOut =>@map.container.fadeIn ->refresh()
else @map.container.fadeIn ->refresh()
mapClose: ( show=null )=>
return unless @map?
if show? then @map.container.fadeOut ->show.fadeIn()
else @map.container.hide()
clearTimeout @mapUpdateTimer if @mapUpdateTimer?
@mapUpdateTimer = null
mapUpdate: ( markers, timeout=10000 )=>
return unless @map?
return if !@map.container.is ":visible" # FIX HORRENDOOOOOOOOO...!!!
@map.setMarkers markers, @map.reposition
@map.reposition = markers?.length is 1
clearTimeout @mapUpdateTimer if @mapUpdateTimer?
if @mapUpdateTimer isnt false
func = @refreshMap
@mapUpdateTimer = setTimeout func, timeout
back: ( times=1 )->
@constructor.goBack times
########### CLASS METHODS ###########
@getInstance: ( options={} )-> # VERY important to be "->"
new @ options
@include: ( obj )->
throw("CRUDView.include(obj) requires obj") unless obj
for key, value of obj.prototype when key not in ["included", "extended"]
@::[key] = value
(included=obj.included).apply(this) if included
@
@goBack: ( times=1 )->
obj =
times: times
back: -> history.back() if @times-- <= 1
obj.back() if times is 1
obj
@parseDetailLink: ( opts={} )->
dataID = if opts.id? then "data-id=\"#{opts.id}\"" else ""
href = if opts.href? then "href=\"##{opts.href}\"" else ""
tag = if href.length then "a" else "span"
"<#{tag} #{dataID} #{href} class=\"link #{(opts.css||"")}\" title=\"#{(opts.title||"Abrir detalles..")}\">#{opts.text}</#{tag}>"
@parseIconizedStatus: ( status )->
@parseIconizedStatusColor @getIconizedColorFromStatus status
@parseIconizedStatusColor: ( color )->
return "" if !color
@parseIconizedStatusObject @getIconizedStatusObjectFromColor color
@parseIconizedStatusObject: ( obj )->
#obj.wrap("<div/>").parent().html()
obj[0].outerHTML
@getIconizedObjectFromStatus: ( status )->
@getIconizedStatusObjectFromColor @getIconizedColorFromStatus status
@getIconizedStatusObjectFromColor: ( color="unknown" )->
$("<div/>").addClass "iconizedStatus #{color}Status"
@getIconizedColorFromStatus: ( status )->
status = status.toLowerCase()
if status in ["online","libre","ocupado"] then color = "green"
else if status in ["offline","no_disponible"] then color = "gray"
else if status in ["suspendido","suspended"] then color = "red"
else color = "white"
color
@__elFromEv: ( ev )=>
$(ev.target).parent()
# Create alias "@__" for "@constructor" to call statics
#Object.defineProperty @::, '__', get:->@constructor
#@::$$ = @constructor
#@__ = @constructor
#__: @constructor
###
# DMC NOTA MENTAL: Las vistas que necesitan extender de CRUDDetailView
# es por que tienen alguna particularidad que no puede ser resuelta
# de forma genérica por CRUDDetailView con su CRUDModel asociado.
# CRUDDetailView tendría que ser capaz de manejar TODO con sólo un CRUDModel.
###
class window.CRUDDetailView extends CRUDView
_className: "detailView"
formClass: "Subclasses should override" #@TODO DMC: Volar esto.
viewEvents:
'click .edit' : 'showEdit'
'click .delete': -> @model.executeDelete()
'click .undelete': -> @model.executeUndelete()
postInit: ->
#if (model = viewsCommunication.get "objectForCRUDDetailView")
# @setModel model, true
@model.on "onDelete", -> CRUDView.goBack()
@model.on "onUndelete", =>
@model.fetchDetailData( => @render())
if ! @model?.isPersistent()
#TODO: SUPER HACK. Remover cuando se cambie el navigator por algo más poderoso. La navegación debería llamar a
#algún otro hook además de render, así no se pisan funcionalidades de render con navegación, por ejemplo
setTimeout (->CRUDView.goBack()), 500
doRender: ->
@$el.html @parseModule()
@renderFeatures()
renderFeatures: ->
for feature in this.crudFeatures() when _.isFunction(this["render_#{feature}"])
this["render_#{feature}"]()
render_notes: ->
if User.can("#{this.permissionsPrefix()}_NOTAS")
@$(".notesContainer").html @parseNotes()
render_files: ->
if User.can("#{this.permissionsPrefix()}_ARCHIVOS")
@$(".filesContainer").html @parseFiles()
render_log: ->
if User.can("#{this.permissionsPrefix()}_EVENTOS")
@$(".logContainer").html @parseLog()
render_currentAccount: ->
if User.can("#{this.permissionsPrefix()}_VER_CUENTA_CORRIENTE")
@$(".currentAccountContainer").html @parseCurrentAccount()
permissionsPrefix: ->
#TODO: Quizás queremos mover esto al CrudModel, pero como por ahora sólo se usa en el details... que quede aquí
throw "Subclasses should override"
parseCurrentAccount: ( opts )->
opts = _.extend {holder:@model}, opts
view = new CurrentAccountView opts
view.render()
view.$el
parseNotes: ( opts )->
opts = _.extend {entity:@model}, opts
view = new NotesView opts
view.render()
view.$el
parseFiles: ( opts )->
opts = _.extend {entity:@model}, opts
view = new FilesView opts
view.render()
view.$el
parseLog: ( opts )->
opts = _.extend {entityId: @model.get("id"), apiCall: "#{@model.entityApiName()}Log" }, opts
view = new EntityLogView opts
view.render()
view.$el
template: ( tpl=@tpl, view="detail_view" )->
super tpl, view
createFormView: ( options )->
new @formClass options
showEdit: ->
form = @createFormView {model:@model}
form.render()
App.navigator.changePage form
showForResponse: ( response, show=(->) )=>
#view = @constructor.getInstance {response, show}
@options.response = response
@options.show = show
@render()
showForID: ( id, show=(->), success=@showForResponse )=>
#@TODO DMC: Este método debería ser de clase..
onSuccess = ( response )=>
success response, show
onError = ( jqXHR )=>
App.errorMessage App.keyToText jqXHR.getResponseHeader("exception-key")
@entity().api.detail.call id, onSuccess, onError
#= require fwk/views/declarativeForm
###
# DMC NOTA MENTAL: Las vistas que necesitan extender de CRUDFormView
# es por que tienen alguna particularidad que no puede ser resuelta
# de forma genérica por CRUDFormView con su CRUDModel asociado.
# CRUDFormView tendría que ser capaz de manejar TODO con sólo un CRUDModel.
###
class window.CRUDFormView extends CRUDView
_className: "formView"
fieldDefinitions: [] # Should be overriden!
submitableProperties: [] # Should be overriden!
viewEvents:
"click .save": "save"
postInit: ->
embedded = @options.embedded
@form = new DeclarativeForm {@fieldDefinitions,@template,embedded}
doRender: ->
@$el.html @form.el
@form.bindTo @model
@model.bindForm @form # This binding is done here and NOT
# inside DeclarativeForm because we just want to do it for CRUD!
@addCheckExistanceEvent()
@form.render()
this.delegateEvents()
addCheckExistanceEvent: ->
identifyKeyField = @form.identifyKeyField()
if identifyKeyField
@events["change :not(.nested) ##{identifyKeyField}"] = -> @model.validateExistance()
delegateEvents: ->
super
@form.delegateEvents() if @form?
remove: ->
super
@form.remove() if @form?
template: ( tpl=@tpl, view="form_view" )->
super tpl, view
modelForm: # Should be overriden! @TODO DMC: Esto va a desaparecer!
model: @constructor._entity # @TODO DMC: Ver qué onda esto.
url: null
validate: ( callback )=>
#__log callback
@form.validate callback
getErrors: ->
@form.errors
save: =>
@validate ( validObject )=>
objectToSubmit = this.getSubmitableModel(validObject)
this.preSubmit(validObject, objectToSubmit)
@doSave validObject, objectToSubmit
getSubmitableModel: (validObject) ->
if(!validObject?)
validObject = @model
objectToSubmit = new Backbone.Model()
for property in @getSubmitableProperties() when (property.model?=property.field) and validObject.has(property.model)
objectToSubmit.set property.field, validObject.get(property.model)
#__log objectToSubmit, validObject
this.prepareForSubmit(validObject, objectToSubmit)
objectToSubmit
getSubmitableProperties: ->
propertyList = for property in @submitableProperties
if typeof property == 'string' then {field: property, model: property} else property
@calculateSubmitableProperties(propertyList)
calculateSubmitableProperties: (propertyList) ->
skippedProperties = @skipSubmitableProperties()
propertyList.filter (property) -> not (property.field in skippedProperties)
skipSubmitableProperties: -> []
prepareForSubmit: (validObject, objectToSubmit) -> #for subclasses to override
preSubmit: (validObject, objectToSubmit) -> #for subclasses to override
postSubmitSuccess: ->
@constructor.goBack 1
doSave: ( validObject, objectToSubmit )=>
func = if validObject.isCreating() then @doCreate else @doUpdate
func validObject, objectToSubmit
doCreate: ( validObject, objectToSubmit )=>
@entity().api.create.call {
model: objectToSubmit
onSuccess: ( response ) =>
# @TODO Internacionalizar!
App.logMessage validObject.entity()+" creado correctamente"
this.setModel validObject
this.postSubmitSuccess response
}
doUpdate: ( validObject, objectToSubmit )=>
@entity().api.update.call {
model: objectToSubmit
onSuccess: ( response )=>
# @TODO Internacionalizar!
App.logMessage validObject.entity()+" actualizado correctamente"
this.setModel validObject
this.postSubmitSuccess response
}
deletePhoto: ( apiFunc )->
onSuccess = =>
App.logMessage("La foto se eliminó correctamente")
@form.$('.currentPhotoImg').attr 'src', noPhotoURL
@form.$('.currentPhoto').hide()
onConfirm = =>
apiFunc( {
id: @model.get('id')
success: onSuccess
error: -> App.errorMessage("No se pudo eliminar la foto")
})
new ConfirmModal({onConfirm, title:"Confirmar eliminación de foto", message:"¿Está seguro que desea eliminar la foto?"}).show()
########### CLASS METHODS ###########
#= require fwk/views/tableView
class window.CRUDListView extends CRUDView
_className: "listView"
itemsPerPage: 25
itemsMaxPages: 20
viewEvents:
"keyup .quick-search": "doQuickSearch"
"click .quick-search": (e)->$(e.target).select()
"click .quick-search-clear": "doQuickSearchClear"
#@TODO DMC: xq esto acá y tb en parseTable? (si no, NO funciona)
"rowclick": "doRowClick"
columnDefinitions:
none: { title: 'None', type: ColumnTypes.none }
doInit: ->
@loadConfig()
@createSoftDeleteModel()
@objects = @createCollection()
@objects.on('prefetch', => this.saveConfig())
@fetch()
fetch: ->
@objects.softDeleteFilter = @softDeleteModel?.get('filter')
if @config.searchTerms
@doSearch(@config.searchTerms, @config.currentPage)
else
@objects.fetchSorted @config.sortBy, {ascendant: @config.ascendant}, @config.currentPage
bindSoftDeleteField: ->
@softDeleteField?.bindTo(@softDeleteModel)
createSoftDeleteModel: ->
@softDeleteModel = new Backbone.Model({filter: @config.softDeleteFilter})
@softDeleteModel.on('change', =>
@objects.softDeleteFilter = @softDeleteModel?.get('filter')
@doQuickSearchClear()
)
softDeleteFilterWidget: ( title=null )->
widget = $(JST['templates/soft_delete_filter']({title}))
@softDeleteField = new SelectField(
container: widget.find('.soft-select-container')
fieldId: 'filter'
definition:
type: FieldTypes.select
optionParams: {"id","title"}
values: [{id:'ACTIVOS',title:'A'},{id:'BORRADOS',title:'B'},{id:'TODOS',title:'T'}]
)
@softDeleteField.render()
widget
saveConfig: ->
@config.currentPage = @objects.currentPage
@config.sortBy = @objects.sortedBy
@config.ascendant = @objects.ascendant
@config.itemsPerPage = @objects.itemsPorPagina
@config.searchTerms = @objects.searchTerms
@config.softDeleteFilter = @softDeleteModel?.get('filter')
localStorage.setItem(@configKey(), JSON.stringify(@config))
loadConfig: ->
@config = _.defaults @storedConfig(), @defaultConfig()
storedConfig: ->
configString = localStorage.getItem(@configKey())
if configString then JSON.parse(configString) else {}
configKey: ->
"#{@constructor.name}_#{@entity().entityApiName}_listConfig"
defaultConfig: ->
if typeof @modelList.sortBy is "string"
sortBy = @modelList.sortBy
ascendant = true
else if @modelList.sortBy.field?
sortBy = @modelList.sortBy.field
ascendant = ((@modelList.sortBy.order||"ASC").toLowerCase().substr(0,1) is "a")
currentPage = if @modelList.currentPage? then @modelList.currentPage else 1
softDeleteFilter = if @modelList.softDeleteFilter? then @modelList.softDeleteFilter else "ACTIVOS"
{sortBy, @itemsPerPage, currentPage, ascendant, softDeleteFilter}
createCollection: ( options={}, collClass=CRUDListViewCollection )->
values = []
if options.values? # Collection values given!
values = options.values
delete options.values
opts =
model: @entity()
url: @modelList.url # @TODO DMC: Volar @modelList de una vez!
itemsPorPagina: @config.itemsPerPage
maxPaginas: @itemsMaxPages
opts = _.extend opts, options
new collClass values, opts
template: ( tpl=@tpl, view="list_view" )->
super tpl, view
doRender: ( template=@template() )->
quickSearchContainer = JST["templates/crud/list_view_search"]()
@$el.html JST[template] {view:@,quickSearchContainer}
@$('.quick-search').val @config.searchTerms
@tableView = @parseTable()
@tableView.render()
@bindSoftDeleteField()
@$el
buildColumnDefinitions: ->
if $.isFunction(@columnDefinitions) then @columnDefinitions() else @columnDefinitions
parseTable: ( values=@objects, columnDefinitions=@buildColumnDefinitions(), el=@$(".table")[0] )->
table = new EmbellishedTableView {el, columnDefinitions, values}
#@TODO DMC: xq esto acá y tb en viewEvents? (si no, NO funciona)
table.on "rowclick", @doRowClick
table
showLoading: ->
App.showLoading(@tableView?.el)
hideLoading: ->
App.hideLoading(@tableView?.el)
doQuickSearch: ( ev )=>
return true unless ev.keyCode is 13
return App.showError "Búsqueda no soportada." if !@entity().api.search?
input = $ ev.target
this.doSearch(input.val())
doSearch: (terms, page) ->
@showLoading()
@objects.search( terms, page, =>
@$(".quick-search-container .iconizedButtonWidget").show()
@hideLoading()
)
doQuickSearchClear: ( ev )=>
@objects.clearSearch();
@$(".quick-search-container input").val ""
@$(".quick-search-container .iconizedButtonWidget").hide()
@render()
doRowClick: ( rowObject )=>
@showDetailView rowObject
doOpenMap: ( ev )=>
ev.preventDefault()
$(ev.target).fadeOut =>@$(".actions .close-map").fadeIn()
@mapOpen @$(".table")
doCloseMap: ( ev )=>
ev.preventDefault()
$(ev.target).fadeOut =>@$(".actions .open-map").fadeIn()
@mapClose @$(".table")
class window.CRUDListViewCollection extends EmbellishedCollection
defaultData: ( options )->
data = super options
if @searchTerms then data.terminos = @searchTerms
if @softDeleteFilter?.id then data.estado = @softDeleteFilter.id
data
setDefaults: (options) ->
super
if(@searchTerms) then options.url = @model.api.search.remoteUrl
search: (terms, page, options) ->
if options && !options.success then options = {success: options}
this.searchTerms = terms
this.fetchPerPage(page, options)
clearSearch: ->
this.searchTerms = null
this.fetchPerPage(1)
#Any component that understands renderPage(pageNumber) can be paginated with this widget.
class Paginator extends Backbone.View
events:
'change .amountPerPage' : 'amountPerPageChange'
'click .nextPage' : 'nextPageClick'
'click .prevPage' : 'previousPageClick'
'click .page' : 'especifiedPageClick'
initialize: ->
# The currently selected page (1-based).
@currentPage = @options.currentPage ? 1
@changePerPage = @options.changePerPage ? true
@friendly = @options.friendly ? true
@list = @options.list
amountPerPageChange: (e)=>
el = $ e.target
val = 0 if (val=el.val()) is "all"
@list.setItemsPerPage parseInt val
@renderPage 1, el, =>@render()
nextPageClick: (e)=>
el = $ e.target
@currentPage = Math.min @pageCount(), @currentPage + 1
@renderPage @currentPage, el
previousPageClick: (e)=>
el = $ e.target
@currentPage-- if @currentPage isnt 1
@renderPage @currentPage, el
especifiedPageClick: (e)=>
el = $ e.target
pageNumber = parseInt el.data('pagenum')
return unless pageNumber > 0
@currentPage = pageNumber
@renderPage @currentPage, el
hasPage:(pageNumber)=>
1 <= pageNumber && pageNumber <= @pageCount()
pageCount: ->
return 1 if (perPage=@list.getItemsPerPage()) is 0
Math.ceil @list.itemCount() / perPage
getRange: ->
threshold = 0 # Distance from middle, to activate "friendly" mode
max = (@list.getMaxPages?()) or 10
from = ((Math.ceil(@currentPage/max)-1) * max) + 1
if @friendly and @currentPage-threshold > (mid=Math.ceil(max/2))
from = Math.max 1, @currentPage-mid
to = Math.min (from+max-1), @pageCount()
{from,to}
renderPage: (pageNum, el, callback=(->)) ->
App.showLoading el
success = (rs) =>
App.hideLoading el
@currentPage = pageNum
callback rs
@list.renderPage pageNum, {success}
renderAt: (anElement) ->
this.setElement(anElement)
this.render()
render: =>
@$el.html ""
return if (perPage=@list.getItemsPerPage()) > 0 and perPage >= @list.itemCount()
if @changePerPage
@$el.html "Ítems/Página"
@$el.append @parsePerPageSelect()
return if (pageCount=@pageCount()) <= 1 and @currentPage == 1
if pageCount > 0 and @currentPage > 1
prev = '<button class="pageChanger prevPage">prev</button>'
else
prev = '<button class="pageChanger prevPage" disabled="disabled">prev</button>'
@$el.append prev
if pageCount
range = @getRange()
for idx in [range.from..range.to]
if @currentPage is idx
button = "<button data-pagenum='#{idx}' class='pageChanger page pageNumber active' disabled='disabled'>#{idx}</button>"
else
button = "<button data-pagenum='#{idx}' class='pageChanger page pageNumber'>#{idx}</button>"
@$el.append button
#To ensure that the current page button is shown even if the page is not existent
unless @hasPage @currentPage
button = "<button data-pagenum='#{@currentPage}' class='pageChanger page pageNumber active' disabled='disabled'>#{@currentPage}</button>"
@$el.append button
if @hasPage @currentPage+1
next = '<button class="pageChanger nextPage">next</button>'
else
next = '<button class="pageChanger nextPage" disabled="disabled">next</button>'
@$el.append next
parsePerPageSelect: ->
sel = $("<select/>").addClass "amountPerPage"
total = parseInt @list.itemCount()
curr = parseInt @list.getItemsPerPage()
parseOptionCheck = ( amount, text=amount )=>
if curr isnt amount and amount <= total
sel.append parseOption amount, text
parseOption = ( val, text=val )=>
$("<option/>").val(val).text(text)
if curr is 0
sel.html parseOption curr, "Todos"
else
sel.html parseOption curr
sel.append $("<option/>").prop("disabled",true).text "------"
parseOptionCheck 5
parseOptionCheck 10
parseOptionCheck 25
parseOptionCheck 50
parseOptionCheck 100
parseOptionCheck 200
sel
window.Paginator = Paginator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment