-
Notifications
You must be signed in to change notification settings - Fork 3k
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
State transitions similar to Pinterest's overlay. #317
Comments
See the ui-bootstrap |
Hey @nateabele, I did have a look at that example. But would it work with the browser's back button? I would also like to move that logic to a controller not an custom onEnter function. There doesn't seem to be any documentation on the onEnter itself. Furthermore that example did not look into how you would differentiate between the overlay and directly accessing the item. |
Yes.
Nope. Can't do that without a |
Sorry, what item? |
That's fine. I can handle a child ui-view on the homepage. But could you elaborate? Regarding the item. As I said for Pinterest. There is the homepage and the items. When you click on the items, they open up an overlay for that item. But when you access the item's URL directly, it goes to a separate page which only houses the item. The URL of course is shown when the overlay is active. It's easier to understand if you just go to Pinterest and click on one of the items. |
@CMCDragonkai I went to pinterest.com. It wants me to sign up. I am not signing up for Pinterest. Sorry.
This is up to your server implementation, and unrelated to Angular and UI-Router.
I guess you could put a hidden |
Facing this same challenge. For
I'm thinking I'll probably have to check the state of the page onEnter:... and either pop up a modal or render the entire page there. If nothing else, there could just be two states.
when it shouldn't be a modal |
The main problem here is that we need two states on one url. One of the states can only be activated during a click event, the other should be the default. The template layouts may be radically different, so they need their own config tree. Is there an obvious way to do this that I'm missing? |
Again, I believe this is the responsibility of your server implementation. |
Why would one state be separated from everything else? I must be missing something. I imagine that there would be exceptions where you want to link to a full details page and not the modal, in which case the logic is still up in the application and state machine, not on the server. Is there no way to do what I described? |
I've seen other apps that do what you describe. You're not talking about a separate state, you're talking about a separate page-load. |
I just went with not using ui-router for the overlay. Seemed difficult. Instead the overlay is manually triggered on a click and the bootstrap dialog service. I then set the url to the full page state and cancel the state transition. But I think ui-router should provide a more elegant capacity for this. |
@CMCDragonkai If you'd like to patch ui-router to create a demo/proof-of-concept, I'd be happy to take a look. |
@CMCDragonkai Could you share your code for this?:
|
Oh I haven't wrote it yet. I intend to very soon. |
Hey @braco I figured it out. And with the help of ui-state too! Basically all you need is an anchor link with:
The ng-click should be a function that opens up the overlay. This can be done using ui-bootstrap's dialog service. Kind of like this: ////////////////////////
// OVERLAY HANDLING //
////////////////////////
//setting up the overlay options
var dialog = $dialog.dialog({
backdrop: false,
keyboard: true,
dialogClass: 'modal idea_overlay',
templateUrl: 'idea_overlay.html',
controller: 'IdeaOverlayCtrl'
});
//we want to cancel the state change to ideas, and instead launch an overlay
//however we must keep the URL to the idea
$scope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
//state change is prevented from home to ideas
//but the URL is preserved
if(fromState.name === 'home' && toState.name === 'ideas'){
event.preventDefault();
}
});
/**
* Opens the overlay. This modifies the dialog options just as it is
* opening to inject the ideaId and locationParams. This also registers
* a closing callback when the overlay closes.
* @param {Number} ideaId Id of the Idea
* @return {Void}
*/
$scope.openIdeaOverlay = function(ideaId){
//ideaId is to be injected to the overlay controller to get the correct idea
//locationParamsAndHash is the current URL state before the overlay launches
//it will be used when the overlay closes, and we want to return to original URL state
dialog.options.resolve = {
ideaId: function(){ return ideaId },
locationParamsAndHash: function(){
return {
path: $location.path(),
search: $location.search(),
hash: $location.hash()
};
}
};
//closing callback will receive the previous locationParams through the overlay
dialog.open().then(function(locationParamsAndHash){
$rootScope.viewingOverlay = false;
$location.path(locationParamsAndHash.path);
$location.search(locationParamsAndHash.search);
$location.hash(locationParamsAndHash.hash);
});
}; Now, the trick is '$stateChangeStart' event. Interestingly when this event is broadcasted. The URL in the browser has changed, but the state has not yet been transitioned. So it's possible to do a preventDefault on the state change and persist the URL. Now the greater trick is closing the overlay. The following is the overlay controller used by the dialog service. .controller('IdeaOverlayCtrl', [
'$scope',
'$rootScope',
'dialog',
'ideaId',
'locationParamsAndHash',
'IdeasServ',
function($scope, $rootScope, dialog, ideaId, locationParamsAndHash, IdeasServ){
$rootScope.viewingOverlay = true;
$scope.closeOverlay = function(){
//this can only pass in a single param
dialog.close(locationParamsAndHash);
};
//and load in the appropriate data
$scope.idea = {};
IdeasServ.get(
{
id: ideaId
},
function(response){
//console.log(response.content);
$scope.idea = response.content;
},
function(response){
//error message
console.log(response.data);
}
);
}
]); When I close the overlay using ui-bootstrap's dialog service. I pass in the previous URL state as "locationParamsAndHash". This basically restores my original URL's state. So when if I had something like e.com?blah=blah, then opened the overlay which changed to e.com/item/id, then closed it, it would go back to ?blah=blah. Now this isn't the prettiest solution. I still think if something could expose the URL to be changed without any sideeffects, that would be the best. In fact I believe HTML5 history provides exactly that. The problem is that AngularJS is already using history state. And I believe (I'm not sure) that AngularJS is watching this, so even if I use history state, AngularJS will decide to change the page. But I haven't tested. For now, have fun using $stateChangeStart. |
@nateabele Any chance we could somehow implement what I did above elegantly into ui-router? |
@CMCDragonkai You're welcome to propose a specific solution. However, be aware that this behavior:
...is probably going to go away, since it is the subject of some open bugs. |
Oh, forgot to mention, in future versions we're planning on exposing a |
It'd be the opposite. Basically prevent the transition but allow the URL change. But definitely, more flexibility the better. I would like to be able to do it both ways. |
I am facing with exactly the same challenge and used a solution similar to @CMCDragonkai 's. I listened to $stateChangeStart event, capturing specific route change (which was implemented as controller function in @CMCDragonkai 's solution) and cancels route change when needed, and finally use $location.path to change the route. However, in my case the pages in the dialog is linked to other pages. I spend a bunch of code in handling the states and it is quite buggy. I thought this maybe easier if |
Looks like the bug this was exploiting was fixed? Doesn't work now. Any ideas? Even something like this would be acceptable: angular/angular.js#1699 (comment) |
Just use the older version of ui-router. |
What would be the best way to implement Pinterest's overlay mechanism.
If you go to Pinterest and you click on one of the items, it brings up an overlay of the item. It also does a couple other things:
Obviously I cannot just match the unique item URL, because that would prevent the unique item page from being loaded, only the overlay.
My ideas are currently use ui-bootstrap's dialog service along with 3 states. Home state, item child state of the home that doesn't have a URL parameter. And an item state. The item state would match the URL, so when a user accesses the URL directly it will navigate to the item's page. But whenever I want to bring up the overlay, I'll manually use transitionTo() to get the item child state. I haven't tried just yet, but I feel this may have problems regarding the back button.
The text was updated successfully, but these errors were encountered: