Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for string-based itemEvents #875

Closed
wants to merge 1 commit into from

Conversation

jamesplease
Copy link
Member

There’s a new Marionette.helper in town, and it allows you to specify
function names as strings in the itemEvents hash as well as the usual
function references.

Fixes #872

@jamesplease
Copy link
Member Author

I put the helper function that allows this directly on Marionette as it seems like something that could be used elsewhere. If I remember correctly it is only used in collection views in the Marionette core, so maybe it'd be better if it went there.

cv.render();
});

it("should not break", function() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Y U So empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't really think of anything to test, you know? No function exists to test (which is the point), and nothing happens (which is also the point); I just wanted to verify that the code runs and doesn't 'splode. One might think to test that the eventsHash doesn't try to attach the function that doesn't exist, but it seems like that's all handled internally, so there's no way to extract it.

This seems like a significant-enough test for what I was trying to do. If you can think of another way to show this that you think is more substantial, I'd be happy to implement it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Yea I was confident that was the case, just good to get clarification. Might be worth commenting something along the lines of 'Intentionally left blank' in the test?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea, @cobbweb. I added the comment.

@jamesplease
Copy link
Member Author

Added the comment for that 'empty' test, tidied up some spacing, and removed unused variables in the tests. If either of you see anything else lemme know.

@jamesplease
Copy link
Member Author

Fixed it up according to @samccone's suggestions. The function is now normalizeMethods, and the returned value is now normalizedHash

@samccone
Copy link
Member

Update Readme spec 👍

@jamesplease
Copy link
Member Author

Done!


// Transform a mapping of events => functions or function names
// and return a mapping of events => functions
Marionette.normalizeMethods = function(hash) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have some tests for this function directly too? ping @samccone

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah unit test time (:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add some @jmeas?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. I'll have time tomorrow to get to it, I think. Sorry, should have said something 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sweet!

@cobbweb
Copy link
Member

cobbweb commented Jan 22, 2014

NPM issues in the build, re-ran = green :)

@jamesplease
Copy link
Member Author

Patience, friend! It'll be squashed once it's ready. I haven't added those tests yet.

@samccone
Copy link
Member

heh no worries 👍 🅰️

@samccone
Copy link
Member

@cobbweb you should be able to manually trigger a rebuild on travis

@jamesplease
Copy link
Member Author

So I'm writing these tests, and (I should have seen this sooner), but the context of the normalizeMethods function isn't right. It's being called with this as Marionette, which breaks the function, since it relies on this being the object that's calling the fn. It's a bit tedious to always _.bind the function when you want to call it so something should change, I think.

One option is to attach it directly on Marionette.View, which makes it accessible to all of those things that extend from it (collectionViews being one of them). But since the function is just for general-purpose event handling, I can see people wanting to use it for Controllers or Modules (as components).

Another option (maybe better) would be to pass the context as the second argument. Marionette.normalizeMethods(hash, this);

I'm leaning more toward the 2nd of these options, but I'd like to know what y'all think.

There’s a new Marionette.helper in town, and it allows you to specify
function names as strings in the itemEvents hash as well as the usual
function references.

s#Updated docs
@jamesplease
Copy link
Member Author

All patched up.

  • Added more docs
  • Added a context parameter
  • Added more unit tests
  • Fixed the broken unit test

@lukesargeant
Copy link
Contributor

Is it worth mentioning pull request #790 whilst there is interest in item event bubbling stuff?

@samccone
Copy link
Member

@lukesargeant this is not introducing new bubbling, rather a declarative/extendable method to react to events that are already moving up the view chain.

@lukesargeant
Copy link
Contributor

Ok, I thought it might be worth a mention as item event bubbling is the more general topic but you're right it's not strictly related to the functionality this offers.

@cobbweb
Copy link
Member

cobbweb commented Jan 23, 2014

@cobbweb you should be able to manually trigger a rebuild on travis

Yea that's what I did :)

but the context of the normalizeMethods function isn't right.

This is the same pattern that triggerMethod uses. Each class adds it to their prototype like this.

@jamesplease
Copy link
Member Author

Makes sense. I'll go ahead and do that.

@jamesplease
Copy link
Member Author

Okay, @cobbweb, I updated the source. normalizeMethods no longer accepts a 2nd argument, but instead is added to the prototype of Application, View, Module, and Controller.

@Anachron
Copy link

@jmeas great, looks much better than before.

@cobbweb
Copy link
Member

cobbweb commented Jan 23, 2014

Sorry for the confusion, I don't think we want the normaliseMethods method attached to the prototype unless that prototype uses it. ping @samccone

@jamesplease
Copy link
Member Author

Hmm, you're right, @samccone, that in most cases that code above would work, but I'm wondering if relying on the getOption method is the best idea here.

One concern I have is whether or not that method will be changed when this.options support is removed (per #769)? It'd be strange, I think, for Marionette to be sifting through a property that the user only optionally attaches. At that point, the user is really free to name options whatever they want, as that name will no longer even have a special meaning within the framework (outside of getOption), right? But maybe this situation is not actually as weird as I'm thinking it to be.

Another concern of mine is that relying on getOption here adds an extra level of complexity onto the normalizeMethods that doesn't seem necessary. Conceivably one could have an options.someFn that differs from this.someFn. Were I a person in that situation, I'd definitely expect normalizeMethods to grab the this.someFn, and not the options.someFn. Sure, this could be documented, but I still don't think the behavior of normalizeMethods there is ideal.

If code reduction is a concern one idea would be to separate out the value-searching functionality into a method shared between both getOption and normalizeMethods, though I haven't completely thought through how that might look.

@jamesplease
Copy link
Member Author

To summarize what's up with this issue right now:

There's currently inconsistency between the structure of the internal methods of Marionette. If consistency is a desirable quality then some of them need to be changed to behave differently. This PR is hung up because there has no been a decision made on which implementation we ought to go with.

One option to pass the context/target object of the helper, like hasOption. In this case, you don't need to attach the method to any prototypes, as you call it directly from the Marionette object. The other option is to not pass the context and instead attach it to prototypes. This behavior can be seen with the triggerMethod function.

Here are two reasons I think converting things to behave like hasOption makes sense:

The first is that because they're internal most users shouldn't have to overwrite them or call them directly. If that's true, then it's also true that it's less lines to just pass the context instead of directly attaching it to the prototypes. Further, as the number of internal methods increases the number of this.someMethod = Marionette.someMethod lines increases on all of the prototypes, which seems like something I'd want to avoid. The counter-argument to this is that there really aren't that many internal methods, and there probably won't ever be.

Another reason to go with passing the context is that you don't need to decide which methods are attached to each prototype. This normalizeEvents function is pretty useful, I think, to anyone who wants to work with events beyond the basics that Marionette comes with. However, it only makes sense to attach it to the View prototype right now because only Collection Views explicitly use them. The counter-argument to this is that there's not really any reason to design the framework with alterations to it in mind. If the framework is intended to work in a given fashion, all you need to do is worry about it working like that.

Neither of those are incredibly strong reasons that I think passing the context really ought to be the way we go, but they are reasons to sway my opinion in that direction, which is more than I can say for the other option. But I'm sure there are a number of benefits to the other way, as well.

@samccone
Copy link
Member

@jmeas you are really spot on.

The best way is to set the context on call, not to pass it in as I previously said.
triggerMethods is a perfect example of what we should be shooting for.

I agree with you that the abstraction is valuable and useful.

@jamesplease
Copy link
Member Author

So, wait, maybe I'm confused, but I was suggesting the hasOptions has a few benefits over the triggerMethods implementation. In other words, having signatures along the lines of Marionette.someFn = function(context, hash); instead of having lots of this.someFn = Marionette.someFn lines.

Do you think otherwise, or did you just make a slip up in typing? :)

@samccone
Copy link
Member

Ah I do think otherwise.

By using this pattern this.someFn = Marionette.someFn we are building our classes by composition, allowing for decoupled utility methods.

Passing the context in via an argument is also a valid way to do this, it is again just inconsistent.

As to your argument about polluting the prototype, we are really not polluting it per say, rather just storing a reference to the util method with the correct context maintained (but true we are adding a key onto it).

So your points are valid, however instead of breaking from the way we are doing it in this project let's just stick with the pattern in place.

@jamesplease
Copy link
Member Author

I wasn't really worried about pollution, but rather just than just having lots of this.x = Marionette.x lines. But I think either way's fine, really.

@cobbweb, thoughts?

@bobbyrenwick
Copy link

👍 excited for this!

@cobbweb
Copy link
Member

cobbweb commented Jan 28, 2014

I'm fine with either way, but prefer calling the function in the right context versus passing an argument.

@jamesplease
Copy link
Member Author

Alright, cool. It sounds like you and @samccone agree, and I don't feel too strongly either way, and that's what this PR is currently doing. If there's anything else you want me to change, let me know, but it looks good to me. 👍

@@ -18,6 +18,7 @@ Marionette.Module = function(moduleName, app, options){
this.startWithParent = true;

this.triggerMethod = Marionette.triggerMethod;
this.normalizeMethods = Marionette.normalizeMethods;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this to be here right now right?

@jamesplease
Copy link
Member Author

Ah, right, @samccone. I've removed the new function from every prototype except for view.

@funkatron82
Copy link

Will this work for namespaced events like do:something?

@jamesplease
Copy link
Member Author

Yessir

eventOne: "someFn", // This will become a reference to `this.someFn`
eventTwo: this.someOtherFn
};
this.normalizedHash = Marionette.normalizeMethods(hash);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this line be either this.normalizeMethods(hash)? Can you add that normalizeMethods is already added to Marionette.View but has to be added by the user if they want to use it in non-view classes?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, it does need to be this.normalizeMethods(hash)

@jamesplease
Copy link
Member Author

Good catch, @cobbweb. Updated! 👍

@jamesplease
Copy link
Member Author

YOU EVER GONNA MERGE THIS, @samccone?

@samccone
Copy link
Member

samccone commented Feb 3, 2014

ha 💃 it is on

https://github.com/marionettejs/backbone.marionette/tree/dev

going to run it through a few apps

@samccone
Copy link
Member

samccone commented Feb 7, 2014

merged in dev and into master

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for string based itemEvents
7 participants