Skip to content

Directive using short-hand syntax to generate hrefs #16

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

Closed
timkindberg opened this issue Feb 14, 2013 · 29 comments
Closed

Directive using short-hand syntax to generate hrefs #16

timkindberg opened this issue Feb 14, 2013 · 29 comments
Assignees
Milestone

Comments

@timkindberg
Copy link
Contributor

Add a directive that understands the same short-hand syntax as issue #15 and can be used to generate hrefs.

<a ui-state-ref="@edit">Edit</a>
@ghost ghost assigned ksperling Feb 14, 2013
@jeme
Copy link
Contributor

jeme commented Feb 15, 2013

We need to remember Parameters as part of this... Like in the state that i Bound to '/item/:itemId'...

@timkindberg
Copy link
Contributor Author

This needs more explanation, what is going on in your code example?

@jeme
Copy link
Contributor

jeme commented Feb 15, 2013

Like the state defined in the example:

 .state('contacts.detail.item', {
      // parent: 'contacts.detail',
      url: '/item/:itemId',
      views: { ... },
      },
    })

We can't just go at <a ui-state-ref="@item">{{item.title}}</a> or <a ui-state-ref="contacts.detail.item">{{item.title}}</a> because what "item" is it we wan't to open?... So it needs some way of binding that item parameter so it becomes:

<a href="#/item/2" ui-state-ref="@item">One</a>
<a href="#/item/3" ui-state-ref="@item">Two</a>
<a href="#/item/4" ui-state-ref="@item">Three</a>

or

<a href="#/item/2" ui-state-ref="contacts.detail.item">One</a>
<a href="#/item/3" ui-state-ref="contacts.detail.item">Two</a>
<a href="#/item/4" ui-state-ref="contacts.detail.item">Three</a>

In any case, contacts.detail.item requires an input parameter which is itemId
Normally you would do something like:

<a href="#/item/{{ item.id }}">{{item.title}}</a>

That would go in line with what $IgorMinar said in #7 :

we also need to be able to generate these urls so that they don't have to be handcrafted in templates.

Or have I completely misunderstood where you where going with this feature?...

@timkindberg
Copy link
Contributor Author

Oh ok, its like href but for states. In case you want to navigate to a state (without using urls) via a directive. So I think @ksperling was saying that if it started with a '@' it would be relative.

So this one would go to edit state relative to current state, just like a relative url. So if we were in contact.list.details, this would take you to contact.list.details.edit.

<a ui-state-ref="@edit">Edit</a>

<!-- I think I would prefer one of these more though -->
<a ui-state-ref=".edit">Edit</a>
<a ui-state-ref="/edit">Edit</a>

And this one would go to absolute state called "edit"

<a ui-state-ref="edit">Edit</a>

@jeme
Copy link
Contributor

jeme commented Feb 15, 2013

Still leaves the parameters though, edited the examples...

@timkindberg
Copy link
Contributor Author

ah ok I get it now, the parameters...We could have the ui-state-ref string be interpolated so the state name could be dynamic. Then a seperate directive for parameters (kind of yuck). Would the parameters be interpolated too?

Would think we could just do something like this:

<a ui-state-ref="{{$scope.someStateName}}" ui-state-ref-params="{id: item.id, otherParam: item.otherValue}">Edit</a>

Or maybe combine them, separated by a bar.

<a ui-state-ref="{{$scope.someStateName}} | {id: item.id, otherParam: item.otherValue}">Edit</a>

The truth is that people are most likely going to use URLs 95% of the time, so I think its ok that this is more verbose than that.

Also, I'm not entirely sure what Igor is getting at.

@nateabele
Copy link
Contributor

Keep in mind, if we're doing our own directive, we can do our own (simple) parser. We're not constrained by valid JavaScript. For example:

<a ui-state-ref="@edit(id: item.id, otherParam: item.otherValue)">Edit</a>

That should be pretty comprehensible and easy to parse.

@ksperling
Copy link
Contributor

Yes, parameters definitely need to be supported. I was thinking these would be resolved based on the current state, i.e. any parameter that the target state has but that's not specified keeps it's value from the current state.

I'm hoping this directive will be as easy to use as hard-coding an href, and it will have the advantage of being more DRY. If the directive is used consistently, you should be able to completely change the URL pattern of a state, and all links will change to match.

A custom parser is an option, but would probably not be my first choice. I think it should work in a way that somebody familiar with Angular expressions can just use without having to think too much about.

@michaelwinser
Copy link

Can you provide some scenarios for the parameter "rollover" you just
described. It feels like a debugging nightmare in the making.

Michael
On Feb 16, 2013 4:01 PM, "Karsten Sperling" notifications@github.com
wrote:

Yes, parameters definitely need to be supported. I was thinking these
would be resolved based on the current state, i.e. any parameter that the
target state has but that's not specified keeps it's value from the current
state.

I'm hoping this directive will be as easy to use as hard-coding an href,
and it will have the advantage of being more DRY. If the directive is used
consistently, you should be able to completely change the URL pattern of a
state, and all links will change to match.

A custom parser is an option, but would probably not be my first choice. I
think it should work in a way that somebody familiar with Angular
expressions can just use without having to think too much about.


Reply to this email directly or view it on GitHubhttps://github.com/angular-ui/router/issues/16#issuecomment-13674818.

@ksperling
Copy link
Contributor

Obviously parameter values need to be able to be computed via expressions. I think it's probably OK if the target state name is just taken literally.

@ksperling
Copy link
Contributor

@michaelwinser the best example is probably linking between states that share a parent state. In that case the parameters that belong to those shared ancestors should not need to be specified.

In the current sample app for example, I'd like to be able to link from contact.details to contact.details.item without having to explicitly say ... contactId: $stateParams.contactId...

In fact it may be a good idea to restrict this 'rollover' to only parameters of shared ancestors of the current and targeted states.

@michaelwinser
Copy link

I think we're thinking along the same lines. I think parent parameters
should be available to all active child states.

Michael
On Feb 16, 2013 4:15 PM, "Karsten Sperling" notifications@github.com
wrote:

@michaelwinser https://github.com/michaelwinser the best example is
probably linking between states that share a parent state. In that case the
parameters that belong to those shared ancestors should not need to be
specified.

In the current sample app for example, I'd like to be able to link from
contact.details to contact.details.item without having to explicitly say ...
contactId: $stateParams.contactId...

In fact it may be a good idea to restrict this 'rollover' to only
parameters of shared ancestors of the current and targeted states.


Reply to this email directly or view it on GitHubhttps://github.com/angular-ui/router/issues/16#issuecomment-13675034.

@ludinov
Copy link

ludinov commented Feb 16, 2013

Could it be just like:

ui-href="@edit/{{itemId}}"
ui-href="admin.user.edit/{{itemId}}"
ui-action="@submit" // for forms to add fields values to params, but not in the url

@ksperling
Copy link
Contributor

I think the directive should deal with the parameters as an object, rather then via string interpolation (which would then have to be parsed back into a structured representation).

Can you elaborate on how/why you'd want to do "form submission" into state parameters?

@ludinov
Copy link

ludinov commented Feb 17, 2013

Then maybe $state.href as compliment to $state.go

ng-href="{{$state.href('@edit', {id: item.id, otherParam: item.otherValue})}}"
ng-click="$state.go('@edit', {id: item.id, otherParam: item.otherValue})"

Form submission flow very clear (or just familiar) with old-style server-side mvc :

  • we have some route '/entry/submit'
  • send http post to it with form fields into params
  • server responds with error message or new view or redirect
  • response logic placed into separate action function

In my js app I have wizard with states 'app.entry'(abstract) => 'app.entry.edit'(default) => ..(several steps) => 'app.entry.preview'(with submit button) => 'app.entry.submit' => 'app.thanks'
So in separate state 'entry.submit' I have just onEnter function with process data logic (saving data by ajax call) and then redirect to other state ('app.entry.error' or 'app.thanks').
But I'm not sure this is right approach with Angular or general in js mvc. I can have all this logic in the same app.entry.controller
So it's maybe just for consistency..

@timkindberg
Copy link
Contributor Author

I think we need to think of what is best (and easiest) for the user. I see the point about keeping parameters as an object, but it makes the syntax long and cumbersome. Maybe it's not worth it. Maybe let's use a syntax similar to search parameters... ui-state-ref="contacts.detail?contactId=42&color=red"

Actually that's ugly, but I'll leave it so we can at least see that this idea was suggested...

Looking back at all the suggestions, I think the one that seems best to me is @ludinov's latest href and go suggestion. I don't love it though; wish there was something as elegant as the equivelent of this url: href="contact/detail/{{ item.id }}"

@ksperling
Copy link
Contributor

Yeah I think replicating web 1.0 MVC one-to-one in states would be a mistake -- I think it normally makes more sense for the controller thats associated with the form to handle the submit, and then (maybe) transition to another state.

A state isn't the same as a controller, so I don't think you should have a separate state purely to "submit to" and then redirect somewhere else. Besides, entering a state should in general be side-effect free, or at least idempotent. We're not trying to do POST-redirect-GET here. Whatever would happen during POST in web 1.0 should be done by a controller, which then navigates to the correct state (what would have been the GET).

I think wanting to submit a form directly into parameters of a new state is so much of a design smell that we maybe shouldn't support it explicitly -- you can always just have a controller method do that for you manually in about a line or two of code if it's really the right thing to do in a particular case.

@ludinov
Copy link

ludinov commented Feb 17, 2013

@ksperling - agree. thanks.
@timkindberg - just thoughts - what about filter:
<a ng-href="{{ '@edit' | ui-href : {id: item.id} }}></a>
or <a ng-href="{{ '@edit' | ui-href : item.id }}></a> for simple cases
actually still ugly...

@ksperling
Copy link
Contributor

@ludinov that actually looks pretty neat... maybe a bit of an abuse of filters though

@jeme
Copy link
Contributor

jeme commented Feb 18, 2013

Personally I think @nateabele still has given the most simple and intuitive approach in:

<a ui-state-ref="@edit(id: item.id, otherParam: item.otherValue)">Edit</a>

Finding something inside angular that could direct us towards how this should be is sort of difficult...
Personally what examples I have been able to find could be that from ngPluralize but that has a completely different purpose, so that's not even a good indication of syntax...

There is plenty of directives using multiple attributes though, so there wouldn't be anything in angular that wouldn't fit into the example from @timkindberg

<a ui-state-ref="@state" ui-state-ref-params="{id: item.id, otherParam: item.otherValue}">Edit</a>

Except that the consensus would properly be to just call the last attribute params... So:

<a ui-state-ref="@state" params="{id: item.id, otherParam: item.otherValue}">Edit</a>

Or even more crazy generic:

<a ui-ref state="@state" params="{id: item.id, otherParam: item.otherValue}">Edit</a>
<ui-ref state="@state" params="{id: item.id, otherParam: item.otherValue}">Edit</ui-ref>
<ui-ref url="http://github.com">Edit</ui-ref>
<ui-ref url="/regular/:id" params="{id: item.id}">Edit</ui-ref>

In the long run this might fit into the htmlAnchorDirective angular already has:

https://github.com/angular/angular.js/blob/master/src/ng/directive/a.js

@robschley
Copy link

For what it's worth, I think ludinov's suggestion is the most intuitive and least over the top magical. I like the idea that you could link to a state by name which has a URL associated with it. If you ever need to change URLs, all you have to change is the state definition.

In my apps, I use a custom, global $router object that can handle building various routes for different resources such as $router.page(), $router.service(), $router.layout(), etc. which handles issue like base path adjustment (when developing in a subdirectory, etc.). If you link to a state, you don't have to worry about that problem from within layouts which is nice.

The idea of using two attributes seems really clunky to me.

@michaelwinser
Copy link

Since we're chiming in on the syntax, I prefer Nate's approach. To my
thinking the state change is the dominant aspect and it's reflection in the
URL is something that is managed centrally. Changes to the URL mapping
shouldn't require changes to every A element that initiates a state change.

ui-state-href="@edit(param1, param2, ...)"

I'm unsure about the @ prefix. What does it mean? Are values without the
@ valid?

Michael

On Tue, Feb 19, 2013 at 1:20 PM, Rob Schley notifications@github.comwrote:

For what it's worth, I think ludinov's suggestionhttps://github.com/angular-ui/router/issues/16#issuecomment-13679458is the most intuitive and least over the top magical. I like the idea that
you could link to a state by name which has a URL associated with it. If
you ever need to change URLs, all you have to change is the state
definition.

In my apps, I use a custom, global $router object that can handle building
various routes for different resources such as $router.page(),
$router.service(), $router.layout(), etc. which handles issue like base
path adjustment (when developing in a subdirectory, etc.). If you link to a
state, you don't have to worry about that problem from within layouts which
is nice.

The idea of using two attributes seems really clunky to me.


Reply to this email directly or view it on GitHubhttps://github.com/angular-ui/router/issues/16#issuecomment-13788409.

@ksperling
Copy link
Contributor

The example of ngPluralize is interesting -- it's a structured syntax that contains (potentially) multiple angular expressions. Interesting (and somewhat clunky) that they need to be quoted though. Taking the ngPluralize approach, you'd end up with

<a ui-state-ref="@edit(id: 'item.id')">

which is very counter-intuitive as it looks like you're assigning a literal to the parameter. It would look even more convoluted if you're actually assigning a literal: "@edit(id: '\'bob\'')". If you're unfortunate enough to have a "" in your expression itself, it's straight to double-escaping hell. By that point your average developer is probably in for a 1 hour debugging / stackoverflow session trying to figure out why there expression isn't parsing.

You'd need pretty deep integration with the $interpolate expression parser to do away with the need for having a delimiter around each parameter expression (otherwise it's difficult to know where an expression ends). At the minimum you'd need to reuse the tokenizer from $interpolate. Without having investigated if it would be feasible, this sounds like quite a bit of complexity, for merely cosmetic benefit. For me the added learning curve for the user of having to learn the custom syntax is a drawback as well.

@ksperling
Copy link
Contributor

As per my comment on #19, combining a link with the highlight directive sounds like a common use-case that should be supported without having to repeat the state name and parameters.

@pdeslaur
Copy link

pdeslaur commented May 8, 2013

👍

@ajoslin
Copy link
Contributor

ajoslin commented May 13, 2013

$state.href('myState', {params: 'awesome'}) definitely seems the best. It's a simple function, not too verbose, and easy to use :-).

I think a ui-href-relative directive or something could possibly be useful too, but $state.href would definitely be the first thing to have.

@fxck
Copy link

fxck commented May 21, 2013

Is there any progress on this?

@nateabele
Copy link
Contributor

I'm gonna call it, since we now have a working state URL / transition directive in master. We can continue to evolve it once I've finished implementing $state.go().

@christopherthielen christopherthielen modified the milestones: 0.3.0, 0.2.11 Aug 27, 2014
@airtonix
Copy link

so what's the function for this ?

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

No branches or pull requests