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

First pass at state directive for auto-generating link URLs #139

Merged
merged 1 commit into from
Jun 22, 2013

Conversation

nateabele
Copy link
Contributor

Hey guys,

So I was messing around and just went ahead and took a pass at implementing a ui-sref directive. Based on my comments from #16 (comment), and elaborating slightly, valid syntax is any of the following:

<a ui-sref="state (params)">...</a>
<a ui-sref="state(params)">...</a>
<a ui-sref="state()">...</a>
<a ui-sref="state">...</a>

...where state and params are any valid parameter pair for $state.transitionTo(), except that within the context of the directive, state is a literal, not a quoted string. The value for params can be an object literal, scope property, or the result of a scope method call. For example:

<li ng-repeat="item in items">
    <a ui-sref="edit({ id: item.id })">{{ item.name }}</a>
</li>

Binding & Events

The directive will auto-update href for links and action for forms. For links, the default click behavior is suppressed in favor of using $state.transitionTo().

Notes

  • This depends on Method for generating URLs from states #138, which implements $state.href().
  • I know there was some mention of a more dynamic way of referencing states. IMO that is out-of-scope for a shorthand syntax. If people want something more advanced, they can combine ng-href with $state.href().
  • I don't have any tests for this yet, as I still need to read up on the proper way to test directives (soon...)

Interested to hear your thoughts, cc: @timkindberg, @ksperling, @jeme, @ProLoser, etc.

@ProLoser
Copy link
Member

Thanks @nateabele!

@timkindberg
Copy link
Contributor

Ill check it out tonight

@timkindberg
Copy link
Contributor

Looks great! I'm so glad you decided to just take the initiative on this, I'm sure @ksperling appreciates the help.

Only couple of thoughts:

  • We should add a couple more tests in describe ".href()" for more route types, i.e. ones with ? type params, and also routes with regex params.
  • I think we should rename ui-state to ui-stateref. I think it conveys the purpose a bit more clearly. It's like href (hyperlink reference) but for states... stateref... state reference.

@timkindberg
Copy link
Contributor

Also just noticed for non-form objects we are adding the associated state url to the href attribute, couple thoughts there:

  • we then continue to preventDefault(e) anyway, so why is the href attribute needed at all on non-form objects?
  • also this assumes that a state has a url associated with it, what will happen if no url is defined? Will it throw the "State '" + state + "' is not navigable" error?

@nateabele
Copy link
Contributor Author

We should add a couple more tests in describe ".href()" for more route types, i.e. ones with ? type params, and also routes with regex params.

Yeah, that commit actually got carried over from #138, but, IMO those tests would be kind of redundant. The href() method itself is 3 lines of code. Additional tests for generating URLs would just be duplicates of UrlFactory tests.

I think we should rename ui-state to ui-stateref. I think it conveys the purpose a bit more clearly. It's like href (hyperlink reference) but for states... stateref... state reference.

I don't have a real strong opinion here, but I do love me some terse syntax. :-) Honestly, I don't think it conveys any information that couldn't easily be inferred from context. It might make more sense to me if we had multiple state-related directives. If multiple people feel strongly about this, I can change it.

we then continue to preventDefault(e) anyway, so why is the href attribute needed at all on non-form objects?

Bookmarks... general compatibility with the rest of the web.

also this assumes that a state has a url associated with it, what will happen if no url is defined? Will it throw the "State '" + state + "' is not navigable" error?

Currently it would, yeah. I can add a check to prevent attempts to generate a URL for non-navigable states. I can still see a case where people would want to use links to transition to states that aren't associated with URLs.

@jeme
Copy link
Contributor

jeme commented May 24, 2013

Is this PR going to need an update after pulling in #138 ??... (I can see that it includes the same change, so I was wondering how Git handles that, still sort of new to Git after all)

@nateabele
Copy link
Contributor Author

@jeme It shouldn't matter.

@ksperling
Copy link
Contributor

@nateabele Great work! I'm still not entirely convinced a custom syntax is the way to go, but I guess with scope.eval() doing most of the work it's actually not that much of a problem.

I do think ui-state is somewhat generic as a directive name. What about ui-ref or ui-sref?

Support for form elements is interesting... how would that actually be used?

@ksperling
Copy link
Contributor

I think for states without a URL href an option might be to generate a link to the nearest navigable ancestor, but have the onlick transition to the state itself. We would need a flag or options parameter on $state.href() for that -- I think the default behavior of $state.href() should remain to fail on non-navigable.

In terms of tests it might be nice to have a test for testing that changes in the parmeter value are handled, i.e. that href is updated.

@nateabele
Copy link
Contributor Author

I'm still not entirely convinced a custom syntax is the way to go [...]

It was the only way I could think to do a reasonably terse syntax. I'm open to other options.

I do think ui-state is somewhat generic as a directive name. What about ui-ref or ui-sref?

I'm down with ui-ref.

I think for states without a URL href an option might be to generate a link to the nearest navigable ancestor, but have the onlick transition to the state itself [...]

Agreed with all this. I'll try to spend some time on it later today.

@nateabele
Copy link
Contributor Author

Support for form elements is interesting... how would that actually be used?

My initial thought was that it could be used for doing state transitions if the form submit is successful, but that might need to be too tied into how form submits are handled. It would be nice if you could do something like this in an ng-submit handler:

submit: function($event) {
    this.item.$save(function() {
        // Pseudo-code to trigger the transition in the form's ui-ref:
        $event.$success();
    });
}

That way, the controller code would not have to be tightly coupled to state reference information, which logically belongs with the form.

Also, there are generic benefits to generating form actions. For example, in most browsers I can hit Cmd+Enter to target the form submission at a new tab. Obviously this would not work without a URL.

@timkindberg
Copy link
Contributor

I like ui-sref better than ui-ref which is too generic still. Needs the 's' to represent state, in my opinion.

@ksperling
Copy link
Contributor

@nateabele presumable in the cmd-enter case you'd have to handle the form submit server side? Or are we talking about GET only?

@nateabele
Copy link
Contributor Author

@timkindberg I just decided a few minutes ago that I like it better, too. ;-)

@nateabele
Copy link
Contributor Author

@ksperling Correct, the submit would have to be handled server-side. What? Don't all your apps degrade gracefully, too? ;-)

@tamtakoe
Copy link

http://plnkr.co/edit/pwACBH?p=preview
Controls work only one time. On my local machine controls didn't work always

@nateabele
Copy link
Contributor Author

After a bit of refactoring, I believe this is ready to roll. It is dependent on #192, which should be merged first. You'll notice the test that verifies the click behavior is a little weird. Unfortunately the methods that are supposed to exist to test events... don't. I have no idea why this is, but I was able to work around it.

Let me know if anybody has any questions.

if (!nav) return;

try {
element[0][attr] = $state.href(ref.state, params, { lossy: true });
Copy link
Contributor

Choose a reason for hiding this comment

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

lossy is already the default

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought it better to make that explicit, especially since there was discussion around reversing that.

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you wrapping this in a try block because href() may throw an error? I'm wondering if it would be more helpful if href() returned undefined or maybe "#" on invalid states? Devs might use the method in a loop through states that may or may not have urls, might be annoying to always have to wrap it in a try block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay. I think null would probably be best then.

Copy link
Contributor

Choose a reason for hiding this comment

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

That works too

Copy link
Contributor

Choose a reason for hiding this comment

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

Right ok. Hmmm. I think it would be more convenient as null in use. It
doesn't seem fatal enough to throw an error in my opinion. I wonder what
@ksperling thinks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If the consensus is that null is the way to go, that's fine with me. @ksperling?

Choose a reason for hiding this comment

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

This is exciting. I hope sref can degrade gracefully if it is used in
unexpected places or can't do its job due to bad state definitions (or
whatever) as would fit html norms.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess returning null from href() seems OK for a non-navigable state.

Copy link
Contributor

Choose a reason for hiding this comment

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

Even though I generally also like things to fail at the point where something is actually wrong, instead of silently passing nulls or other invalid data around and having it blow up later at a completely unrelated place. But in this specific case it's probably benign enough.

@ksperling
Copy link
Contributor

just put a few comments in the diff

@nateabele
Copy link
Contributor Author

@ksperling I replied to everything with what I think were adequate explanations. Let me know if you have any follow-ups.

ksperling added a commit that referenced this pull request Jun 22, 2013
First pass at state directive for auto-generating link URLs
@ksperling ksperling merged commit c4d1403 into angular-ui:master Jun 22, 2013
@nateabele
Copy link
Contributor Author

@ksperling Hey, I was gonna change $state.href() to return null instead of throwing an error. Do you still want that done?

@nateabele nateabele deleted the directive branch June 23, 2013 00:03
@ksperling
Copy link
Contributor

Yeah go ahead... I thought I'd just merge this to get the ball rolling.

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.

7 participants