chaplinjs / chaplin Goto Github PK
View Code? Open in Web Editor NEWHTML5 application architecture using Backbone.js
Home Page: http://chaplinjs.org
License: Other
HTML5 application architecture using Backbone.js
Home Page: http://chaplinjs.org
License: Other
Probably, to moviepilot/chaplin-facebook-example etc. I propose to put instead hello world project boilerplate like I did in brunch-with-chaplin.
Great effort. Thanks a lot.
After some deliberation we decided to use the "simplified CommonJS wrapping" notation as it is advocated by J. Burke of requirejs: http://requirejs.org/docs/whyamd.html#sugar for our project. We actually prefer it because it feels somewhat easier to read and it may help to avoid unnecessary module loading as it seems to happen in your view.coffee class.
So we were wondering if there are some factors that we are overlooking or not taking into account. And since you really seem to know what your are doing here we would very much appreciate it if you could briefly explain to us why your are sticking to the classic notation.
What would you think of versioning Chaplin like so:
<major>.<minor>.<fix>
major may not be backward compatible
minor must be
fix also
Forgive me if this goes a bit against the philosophy of Chaplin, but after working with it for a while it is clear that Chaplin contains about 50% generic, reusable code and 50% sample application code (sessionController, likes controller, posts controller, etc.)
Lets say someone has 5 smallish sites running under the Chaplin structure. Right now, it would be cumbersome to merge in changes and optimizations to the base classes, views, and application logic because they are interspersed with sample code.
I would propose that Chaplin could be split into a framework-esque base set (for the sake of discussion lets call it "Bowler'), and then a sample implementation of that base set called 'Chaplin'.
Bowler can contain the following:
Essentially, boil it down to a glorified 'Hello World'. That allows developers using it to more easily incorporate updates and optimizations to the core logic into their app. In an ideal world, this core would even be stored in a bowler/
or core/
directory that developers don't touch. They instead extend it where necessary.
Chaplin then can happily continue to demonstrate how one might handle authentication, advanced views interactions, proper use of collections, etc.
Thoughts?
For now Backbone do not return a Backbone value to require.js need.
http://backbonejs.org/backbone-min.js
So the usage in chaplin like this:
define [
'jquery',
'underscore',
'backbone',
'chaplin/lib/utils',
'chaplin/lib/subscriber',
'chaplin/models/model'
], ($, _, Backbone, utils, Subscriber, Model) ->
'use strict'
class View extends Backbone.View
...
Maybe got a error of Backbone.VIew do not exist.
So user need add a return value (Backbone) to Backbone.js.
And this is also be done for underscore.js.
Say I have this route:
match 'events', 'events#index'
And an anchor with an href
= /events?location=City%2C%20Region
.
How do I get the location
parameter to be passed on when clicking the link? Am I missing something?
Should chaplin really hardly depend on AMD?
Marionette provides non-amd version which is a great advantage.
where it's supported.
js anims are quite slow on ios etc.
Code context: https://github.com/moviepilot/chaplin/blob/development/coffee/chaplin/views/view.coffee#L97
Currently Chaplin disallows the use of Backbone’s delegateEvents
and the declarative events
approach. We did this on moviepilot.com to enforce the usage of @delegate
alone for consistency reasons. (Enforcing conventions and consistency was one of the main goals of this structure.) @delegate
has several advantages: In contrast to the events
hash, it works fine when two views inherit from each other and both register handlers. Also, @delegate
automatically binds the handlers to the view.
I would still advice the programmatic approach with @delegate
, but there’s no reason to disable the standard Backbone approach completely. People like the declarative approach, and if this is good for you, Chaplin shouldn’t render the Backbone feature completely defunct.
Also, Backbone’s delegateEvents
got better. events
might be a function, so it is possible to programmatically create the hash. This allows to
Sure, this would still be a bit hacky compared to a pure programmatic approach, but one could argue in favor of Backbone’s standard mechanism.
I will come up with a PR soon. Probably it’ll suffice to remove the overwritten delegateEvents
method.
And the cause is this https://github.com/moviepilot/chaplin/blob/master/coffee/views/application_view.coffee#L182 function
On L198 there's controllerName
but it's undefined variable.
I saw some tests being added for the core pieces of functionality, which I appreciate!
I've wondered how others have written their unit tests for application code that they've written, especially with require.js/jasmine ( e.g. How do you isolate the modules from their dependencies so that the tests are proper unit tests and not integration tests? )
Will you also be adding some tests for the boilerplate app that show your testing strategy?
Great work by the way!
CoffeeScript is doing weird things because of the implicit return. Maybe we should explicitly return undefined unless we want a function to return something.
Also CS is generating horrible inefficient and verbose code in some cases:
http://awardwinningfjords.com/2012/05/08/beware-coffeescript-comprehensions.html
I already removed some of these from Chaplin. There are still some in current Chaplin, like Collection::update.
We should remove application specific files (#38) and package chaplin for npm.
I've started working on it on this branch:
https://github.com/moviepilot/chaplin/tree/features/packaging
Controllers can be very tiny and repetitive (they are for me).
What would you think of making the controller file optional and generate it dynamically to instantiate the view if it is not present?
Was wondering if there is a reason chaplin does not use the Require.js coffeescript plugin? I have found it to work out pretty good, no need to compile the coffeescript in development. When the build is ran for production the cs plugin gets omitted (its a param in the build file for r.js) since you are compiling into a single javascript file.
Currently chaplin has a hard dependency of require.js and was just wondering if this is on the roadmap or has even been discussed.
Chad
#57 introduced the serialization of model attributes. This basically creates a new object and copies/references the properties from model.attributes, now with a bit of conversion.
This is contrary to the delegation approach using utils.beget, so now beget is unnecessary since a new object is already created.
A faster, memory-saving way is to use delegation and only set the transformed properties on the object itself.
This needs to work recursively, that is on the second, third etc. serialization level, too.
If you’re using SyncMachine, you have to call @beginSync()
and @finishSync()
manually. Same goes for @resolve()
when using Deferreds. We need to investigate if it makes sense to do that automatically by providing a Model#sync
which adds a success
handler to the options. The handler would call the original handler and then @finishSync()
or @resolve()
.
Don’t know if that makes sense because there might be cases where a different order might be needed.
See discussion in #47
In production, we found it important to safe jQuery Ajax Deferreds and reject them once the model/collection is disposed. Otherwise some handlers will be fired on disposed objects, which may throw exceptions and which foils our whole event handler cleanup and memory management.
Chaplin doesn’t provide a solution for that yet. @Rendez is working on a solution for moviepilot.com which stores the Ajax Deferreds and rejects them on disposal.
[Edit, from other issue:]
Deferreds and SyncMachines might integrate better with fetch/save/sync
If you’re using SyncMachine, you have to call @beginSync() and @finishSync() manually. Same goes for @resolve() when using Deferreds. We need to investigate if it makes sense to do that automatically by providing a Model#sync which adds a success handler to the options. The handler would call the original handler and then @finishSync() or @resolve().
First off, thank you so much for releasing this code and for the excellent write up. I've been using backbone.js, require.js, coffee script, jquery, etc. on a single page application for about 6 months. I am embarrassed to say that I can't seem to get your sample app running. I'm working in Windows 7 environment. Basically, I took the download and put the index.html file (along with all folders) and copied them to my default web site. I added the line 127.0.0.1 chaplin.moviepilot.com to my hosts file. I navigated to chaplin.moviepilot.com and I got the page to render, but, it was chock full of errors. I apologize, I'm not at home right now, so, I don't have access to the exact errors. But, I'm curious, am I going about it the right way? I am excited and eager to start working with Chaplin. I've tried Marionette and found it too difficult to migrate to AMD (requirejs). I was so happy to see your sample app and I'm busy studying it to see how I can get it to work.
Also, you mentioned in your writeup about including backbone, underscore as regular scripts tags. I was able to get around this and use them as AMD modules with this nice plugin called use.js. It is written by one of the core backbone JavaScript engineers tbranyen https://github.com/tbranyen/use.js. Just thought I would mention it. It worked out well.
Chaplin looks great and really does fill a need that became evident to me the more I worked with backbone.
Best regards,
Chris
Could you please provide an example of how to properly use the Google JS API ServiceProvider
? I'm struggling to get it working properly. Thanks!
Update to RequireJS 2.0
Update to vanilla Backbone and Underscore
Use RequireJS 2.0 shim
Test with Curl module loader
Should it be possible to make non-model views with Chaplin?
Imagine the scenario where you have a <ul>
of <li>
's that contain clickable <a>
tags. As far as I an tell the convention in Chaplin would be to have a Collection view for the <ul>
's, an 'item' view for the <li>
's, and then to @DeleGate the <a>
clicks from the 'item' view.
The problem with this is that is binds events to every <li>
view. This doesn't really take advantage of delegation at all, where you would ideally delegate from the <ul>
to the selector: 'li a'
. This would be only one event handler for the whole collection view. The problem is: you want the handler to be scoped to the item view that was clicked.
Is there an already established patter to do this? The challenge is the event handler is on the collection view, and it needs to somehow (from the DOM element passed back by jQuery) establish which item view instance to bind to. I suppose the id
attribute could be used?
Because the history is started in the ApplicationSpec
and Backbone immediately redirects from /test/
to /#/test
. :(
We need to investigate. Probably setting {root: '/test/'}
might help. But we need to change the Chaplin Router
to pass this option through. Router#startHistory
might just pass all @options
through.
Just realized that ###console.debug 'foo'###
doesn’t make much sense. In the compiled JS, I cannot just remove the sorrounding /* */
because console.debug 'foo'
is CoffeeScript, not JS.
We could write proper JS in the comment, i.e. ###console.debug('foo');###
– But I don’t like that much. Looks ugly and misleading in CS code.
Actually I’m not convinced any more that there is a huge benefit in having these comments in the compiled Chaplin lib. Libraries don’t offer such comments normally.
If someone wants to learn and debug, the plain CoffeeScripts instead of the super-module are the best choice, so #console.debug 'foo'
would totally suffice for them. In order to add logging or debug output, one can also edit the application-specific base files we’re going to provide in the example repos.
Are we into old stupid shit like ie6-8?
If you don't mind, i'll ask in the issue some questions because there're no other ways.
Consider twitter sidebar
I want to create SidebarView
and StatsView
with StatusView
inside of it. What's the idiomatic way to do this?
I mean, if I set StatusView
containerSelector
to some element from SidebarView then it's not rendered because it's too early and the element wasn't created. Should I publish a global event statusViewRender
and listen for it in children composite views in this case? Should I init composite views in SidebarController
?
The dispatcher (application_controller) initialize controller twice.
controller = new ControllerConstructor()
> the constructor call initialize alreadycontroller.initialize params, currentControllerName
Current way of debugging is not that great because if you're using js version of chaplin, all commented console.debug
s are stripped.
What do you think about such debugging technique?
debug
param.console
calls are uncommented. But they check for this parammediator = require 'mediator'
class View
render: =>
if mediator.debug
console.debug 'meh'
Re-defining console.debug
isn't an option because in this case inspector wouldn't show lines where console.debug
was done.
I've been implementing a lot of chaplin into a personal library and I've had a thought about the disposal of objects. Chaplin is explicitly stating which properties to remove:
properties = [
'collection', 'attributes', '_escapedAttributes', '_previousAttributes',
'_silent', '_pending'
]
delete this[prop] for prop in properties
Is there any reason you couldn't do the following instead?
delete this[prop] for prop in _.without(_.keys(@), _.functions(@))
This removes every property that isn't a function. This way subclasses don't need to setup their own disposal methods in most cases and can rely on the parent disposal method to take care of most of the work.
Hi. What are the possible ways to implement the routing after an user refreshes a page? In most cases a server will return '404-Not found'. Please advice.
I'm not sure but it seems that it's useless.
For example:
# Routes
match 'feeds/favorite', 'feeds#show_favorite'
match 'feeds/popular', 'feeds#show_popular'
# Controller
class FeedsController
historyURL: 'feeds'
# ...
What's its purpose in this case? User will be redirected to feeds/
if he'll navigate to /
etc.
when it's nice to be explicit, View::template would be better and much more readable. Don't you think so?
View#afterRender
is called always when View#render
is wrapped, even if the view was disposed.
For example, this logs nothing:
class FirstView extends View
afterRender: ->
super
console.log 'FirstView#afterRender'
view = new FirstView
view.dispose()
view.render()
but this logs SecondView#afterRender
to console:
class SecondView extends View
render: ->
return unless super? # Do not render if the object was disposed
console.log 'SecondView#render'
this
afterRender: ->
super
console.log 'SecondView#afterRender'
view = new SecondView
view.dispose()
view.render()
Most of the utils in Chaplin core are not used by Chaplin core itself and just blow up the lib size, so we should move them into the app-specific utils in the example repository (don’t know if this is the best place in the end, but that’s the place where they are needed at the moment).
Also, I’d like to change the signature of some of the Deferred helper methods to config hashes instead of 27 arguments.
Base page view: renders & encapsulates subviews (without getnavdata
).
Base form view: saves model / collection on server.
Their child views example.
What do you think about the idea?
Hi,
I just started digging in this branch and I thought I would share some thoughts.
File structure: controller
I'm not sure I like the application_controller.coffee
to be in /chaplin/controllers
. I would probably refactor controller.coffee
to take more of application_controller.coffee
and then move application_controller.coffee
to the /controllers
folder to allow the user to add stuff to it (while controller.coffee
should never be touched).
I also noticed that both controller.coffee
and application_controller.coffee
extended the Subscriber. As the second inherit from the first one, is it really necessary?
File structure: view
As for application_controller
, I would move application_view.coffee
back into the application, maybe refactoring it to remove anything that could be abstracted in a parent it would inherit from.
Also what would you think of naming it layout
like in Rails? A layout load views. I don't really calling it application_view
.
Naming
This one is silly and really a matter of taste. I would probably set the folders in /chaplin
to singular: controller
instead of controllers
. They contain the concept not "instances".
It is about it for now. I'll keep digging in and let you know if there is anything else.
So, the idea is to add named routes support. It would be pretty useful in templates, for example, with helpers.
{{#route users show}}{{username}}{{/route}}
Is there a reference anywhere?
It is useful, for example, in with_user
Handlebars helper:
# Evaluate block with context being current user
Handlebars.registerHelper 'with_user', (options) ->
context = mediator.user.getAttributes() # <—————— need to serialize this
Handlebars.helpers.with.call(this, context, options)
"Rendering collections on the UI would be painful without it since Backbone does not provide a clean mechanism to render collections."
Would you like to clarify this statement..? It seems to me you're using exactly the mechanism Backbone provides, that is, creating sub-views for each Model in a Collection and appending them to a CollectionView. Do correct me if I'm wrong, but I think it's both quite clean AND the way demonstrated by the (now legendary) ToDo-sample app.
(Thanks for an awesome write-up on your architecture BTW)
Hi,
I'm not pretty sure if it's a right place to discuss about it, but I would like to ask you about one of your concepts, and consider it at my use case.
In your app flow after the route is matched, the controller is initialialized, where next are initalized model, and collections. However, I would like to ask you what do you do in case, you want to keep data between routes changes?
Eg. imagine a case, when you want to present one collection on two views - one is a grid in table form, and second is a map, when you mark collection items. You want to store selected view, so you need routes to be involved. However there is no need to initialize collection each time. How would you solve this case?
PS. Do you have any discussion list or sth. to talks about chaplin patterns ?
I am making good progress with my application built using Chaplin. I am getting lists to display and I am currently working on editing my objects. I want to thank you for building such an elegant framework. I love backbone.js, but, I kept finding the need to fill in the missing gaps (e.g. controllers, disposing objects, pub/sub, etc.). I am so happy I discovered Chaplin.
I develop in visual studio and I built a solution and migrated all the sample chaplin application files into it. Because the sample Chaplin application has to be run using the mock domain chaplin.moviepilot.com I can not get the Facebook service provider to work when I run my visual studio application. When running a visual studio application it just runs in localhost with a port number. Eventually, I want to authenticate my users by using either Facebook, Gmail, Twitter, or allow them to create a user account. Can you give some guidance on how I might run Facebook authentication without the need to use the mock domain chaplin.moviepilot.com? So much depends on having an authenticated mediator.user that it is difficult for me to move forward.
I have looked at the Facebook provider, however, I have to admit that it is beyond my skillset to understand right now. I don't want to change the provider, I just want to enable my application to use the provider without running under the domain chaplin.moviepilot.com.
Best regards,
Chris
I'm trying to use Chaplin as a sample scaffold to model my application on. In your Facebook sample you use a service provider to get "likes" and "posts". But, when if I wanted to load an object from a restful api instead? In my current app, that I'm migrating over to using Chaplin's architecture, I have a backbone collection called Notes. By simply calling notes.fetch the collection automatically fetches a json list from my restful api. For some reason, I can't seem to figure out the steps to trigger the fetching of a collection using Chaplin's architecture. In the case of "likes" in the initialize method "@getlikes()" ... which in turn calls the service provider with a callback to @processLikes(). This is all good. But, when how would I go about utilizing the built in capability of a collection to fetch its list from a restful api? I'm writing my application using vs.net 2010 and my backend is the new MVC 4 webapi (that supports restful services).
I'm really loving the architecture of Chaplin. It is quite elegant.
Best regards,
Chris
I've been working to implement a Chaplin-style architecture in an app I'm working on and I've discovered a missing piece that could be helpful. Views often have two collections with the same models in them. If the collections aren't using the same model instance, changes to the model won't reflect in both (or more) views without some sort of manual syncing.
The idea would be have a datastore object alongside the mediator. It's used to retrieve models and fetch them if they don't exist, returning them to whatever requested the model. The datastore would return a deferred object when requesting a model, in case it needs to fetch the model from the server first. This allows multiple collections to use the same model.
Before you add anything to a collection, you request the model from the datastore rather than creating a model instance manually and add that to the collection.
An alternative solution with the current setup would be to create a collection on the mediator and any sub-collections simply apply a filter. I'm not sure about the best way to approach this problem because I wasn't sure if the filter should be used this way.
Hi,
I can help with any of the following recommendations. I just wanted to discuss it first.
Disclaimer: I use the development
branch.
Vendors as modules
I highly recommend that all the vendors are turned into AMD modules. Here is a list of links to AMDified vendors:
Complete dependency declaration
A module should list all its dependencies to be as self-contained as possible. create_mediator.coffee
should be:
define [
'underscore',
'chaplin/lib/support'
], (_, support) ->
//...
We don’t need the individual compiled JS files any longer in the repo since we can build Chaplin into one JS now.
This will break the tests but I think I’ll add the CoffeeScript compiler as a RequireJS plugin there so the source and the specs can be compiled on-the-fly.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.