There's a lot of documentation about opening modal windows when going to a specific state, some people use 'onEnter', others' use the '$stateChangeStart' event.
But the issue I'm having with both of those is that when going to that url directly, there isn't any content loaded underneath the modal window.
For example:
1. Users goes to www.mysite.com
2. User click the link that opens a model: www.mysite.com/#/signup
3. User sees the signup modal window with the original content underneath
But when a user goes directly to www.mysite.com/#/signup, this doesn't happen.
Currently I have a parent route that handles all my modal windows, as following:
$stateProvider
.state('modal', {
parent: 'home',
abstract: true,
url: '',
data: {
modal: true
},
onEnter: ['$state', '$modal', function($state, $modal) {
console.log('open modal');
$modal.open({
template: '<div ui-view="modal"></div>',
backdrop: true
}).result.then(function() {
$state.go('home');
}, function(reason) {
if(!reason || reason == 'backdrop click') $state.go('home');
});
}]
});
While this works, I want to be able to dynamically set the parent route, as this will be the content that is underneath my modal window.
Is there any way to do this? Or is there any other method that allows me to open the modal window with whatever content is underneath?
Using UI-Router Extra - Sticky States has helped me solve my problem, there's a few issues that there's no underlaying state when you open a modal directly, but those are issues that can be solved.
http://christopherthielen.github.io/ui-router-extras/#/sticky
Related
I have done some research but couldn't find a definitive answer. I have main application area where I load different screens. From one screen I want to open a page that would cover the whole screen. So, navigating to 'viewreport' does exactly that. And when I click on Browser's Back button or have my own Back button on the whole screen page I want to get back to the previous state without reloading its template and controller. Another words, I want to see all selections I have done prior opening the whole screen page. Here is my state configuration:
$stateProvider
.state('body', {
url: '/',
abstract: true,
template: '<div ui-view />'
})
.state('viewreport', {
url: 'viewreport',
templateUrl: 'wholescreen.html',
controller: 'wholescreenController'
});
I am loading different modules into the main 'body' state which might look like this:
function ($stateProvider) {
$stateProvider.state('body.htmlreports', {
templateUrl: function ($stateParams) {
return 'htmlReports.html';
},
controller: 'htmlReportsController',
url: 'htmlreports',
}).state('body.htmlreports.reportarea', {
templateUrl: 'htmlReportParams.html',
controller: 'htmlReportParamsController',
});
I am navigating to viewreport state from htmlReportParamsController controler. The new page then opens into the whole screen. That part works fine. But navigating back to htmlreports when clicking on the Browser's Back button will reload 'body.htmlreports' state. Is there a way of getting back to it without reloading its template?
Update. Why I think it's not a duplicate.
I tried what's suggested in it before posting. This: $state.transitionTo('yourState', params, {notify: false});
still reloads 'yourState'. Also the use case in the provided link is not exactly as mine. Because the OP uses edit mode for already loaded view while I am loading a new view over the the whole screen.
Thanks
Use
$window.history.back();
Add $window in dependency injections of your controller. This will refresh your page and wont reload data we selected.
Please maintain states like this
function ($stateProvider) {
$stateProvider.state('body.htmlreports', {
templateUrl: function ($stateParams) {
return 'htmlReports.html';
},
controller: 'htmlReportsController',
url: 'htmlreports',
}).state('body.htmlreports.reportarea', {
templateUrl: 'htmlReportParams.html',
controller: 'htmlReportParamsController',
}).state('body.htmlreports.reportarea.viewreport', {
url: 'viewreport'
});
In an Angular 1.5 component I'm showing a Material mdDialog, which itself has an embedded component:
$mdDialog.show({
controller: function($scope, $log) {
$scope.notify=function() {
$log.debug("need to cancel");
$mdDialog.hide();
}
},
template: "<md-dialog aria-label='Add Foobar'><new-foobar-panel on-cancel='notify()'></new-foobar-panel></md-dialog>",
targetEvent: ev,
clickOutsideToClose: true,
fullscreen: true
});
When the embedded component invokes the onCancel() callback, the dialog's controller closes the dialog by using $mdDialog.hide().
But that seems sort of messy. The documentation for $mdDialog says that it will close the last opened dialog, but that seems sort of lazy and wishy-washy. I'm sure there is some case with many components and dialogs that would cause this to fail. I'm inside the controller of the dialog after all --- shouldn't there be a reference to the dialog instance I can use to close it directly?
I tried this:
var dialog = $mdDialog.show({
controller: function($scope, $log) {
$scope.notify=function() {
$log.debug("need to cancel");
dialog.hide();
}
But that doesn't work; perhaps a promise is returned instead of a dialog.
So am I stuck with calling $mdDialog.hide() (the global service!!) and hoping that refers to the dialog I'm working with? (That's not very good modularization.)
Actually the documentation says
// Close the specified dialog instance and resolve with 'finished' flag
// Normally this is not needed, just use '$mdDialog.hide()' to close
// the most recent dialog popup.
function closeAlert() {
$mdDialog.hide( alert, "finished" );
alert = undefined;
}
where alert is the dialog instance previously build. So if you actually need to refer to a specific instance of dialog and not the last one, you can use the above method to close it.
I want to temporarily change the browser url when the ui bootstrap modal is opened ( The page behind should remain as is, only the url changes ). When the modal is closed the url should be reverted back to the original one.
Steps :
User loads the page
url : xyz.com/home
User clicks a link opens a modal
url : xyz.com/detail/123
possible solution : changing url with html5 push state
problem : Angular ui-router tries to run its routes as per the changed url, eventually changing the background page.
User closes the modal
url : xyz.com/home
possible solution : html5 pop state
problem : Reloads the background page, which kills the purpose
Example implementation : Pinterest pins and their pin details popup.
You can use ui-router-extras sticky state to solve your problem. There is simple example with modal by the link. You should create two named views, one for main content (background) and one for modal.
<div ui-view="app"></div>
<div ui-view="modal"></div>
Mark the state, from what you want to access to modal as sticky: true in route definition.
.state('main', {
abstract: true,
url: '/',
templateUrl: '_layout.html'
})
.state('main.index', {
url: '',
sticky: true,
views: {
'app': {
templateUrl: 'index.html'
}
}
})
.state('main.login', {
url: 'login/',
views: {
'modal': {
templateUrl: 'login.html'
}
}
})
Also add an event for stateChangeSuccess:
$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
if ((from.views && !from.views.modal) || !from.views) {
$rootScope.from = from;
$rootScope.fromParams = fromParams;
}
});
so, when you need to close modal, you can just
$state.go($rootScope.from, $rootScope.fromParams);
There is small problem for that solution. If you reload page on the modal state, then the app ui-view will be empty.
This can be achieved by having a nested state and triggering the modal using onEnter callback:
$stateProvider
.state('contacts', {
url: '/home',
templateUrl: 'home.html',
controller: function($scope, MyService){
$scope.contacts = MyService.getContacts();
}
})
.state('contacts.details', {
url: "^/details/:id", // using the absolute url to not have the "/home" prepended
onEnter: function($state, $uibModal) {
var modal = $uibModal.open({
templateUrl: 'details.html',
controller: function($scope, $stateParams, MyService) {
// get data from service by url parameter
$scope.contact = MyService.getContact($stateParams.id);
}
});
modal.result.finally(function() {
$state.go('^'); // activate the parent state when modal is closed or dismissed
});
}
});
This technique is described in the ui-router's FAQ.
Here the plunk. In this example the modal's scope is created as a child of the $rootScope - the default $uibModal's behavior when no scope is passed to it. In this case we should use the service in the modal's controller to obtain the data by url parameter.
To have master and details URLs look like these - xyz.com/home and xyz.com/detail/123 - we should use the absolute URL (^/details/:id) in the child state.
Using this solution you can open the detail URLs directly and still have both, master and detail states, activated properly, so sharing the detail URL is possible.
I think you can achive that with ngSilent module
https://github.com/garakh/ngSilent
using $ngSilentLocation.silent('/new/path/');
(once you open modal and again after closing it)
Managed to implement this using https://github.com/christopherthielen/ui-router-extras/tree/gh-pages/example/stickymodal
I have a map with markers on it. When you click a marker a child view opens with the marker details.
.state('map', {
url: '/map',
...
})
.state('map.detail', {
url: '/:markerId',
...
})
If you open a marker's details, navigate away from the map, then back to the map using ui-sref="map" the url will change to /map/<markerId> with the child view open.
I'd like to go straight to /map when clicking on ui-sref="map" but don't want to reload the 'map' just hide any child view.
Is there a simple way to do this? I've tried combinations of cache: false on the two states but that doesn't do what I want.
I am not fully sure, what is your issue. Because this
...I'd like to go straight to /map when clicking on ui-sref="map" but don't want to reload the map just hide any child view...
is the default behavior of UI-Router. There is a working plunker
These links, will not trigger RE-init of parent controller ('map' state)
<a ui-sref="map">
<a ui-sref="map.detail({markerId:1})">
<a ui-sref="map.detail({markerId:22})">
And states like this:
.state('map', {
url: "/map",
templateUrl: 'tpl.html',
controller: 'ParentCtrl',
})
.state('map.detail', {
url: "/:markerId",
templateUrl: 'child.html',
controller: 'ChildCtrl',
})
And both controllers:
.controller('ParentCtrl', ['$scope', function ($scope) {
$scope.timestamp = new Date().getTime();
}])
.controller('ChildCtrl', ['$scope', function ($scope) {
$scope.timestamp = new Date().getTime();
}])
And we can see, that 'map' state is still not changed
Check it here
I have a home page with a classes section. I have a navbar with a classes link. If you are on the page , clicking the link should scroll down to the classes section. If you are on another page, clicking the link should bring you back to the home page and then scroll to the classes section.
I am having issues with the way I handle my state - for now if I am on another page my OnEnter function blocks by saying Cannot 'read property 'top' of undefined'. This being said, I am probably mo writing my states properly.
$stateProvider
.state('home', {
url : '/',
templateUrl:'components/home/home.html',
controller: 'HomeCtrl'
})
.state('classes', {
url: '/',
templateUrl:'components/home/home.html',
controller: 'HomeCtrl',
onEnter: function () {
$('html, body').animate({
scrollTop: $("#classes").offset().top
}, 1000);
}
})
This happens because when you enter the state, the view (HTML) might not yet be loaded and added to the DOM. You may fix the issue with viewContentLoaded event. See:
https://github.com/angular-ui/ui-router/wiki#view-load-events
I think you can also use ui-router's own autoscroll directive to achieve the same effect: https://github.com/angular-ui/ui-router/wiki/Quick-Reference#autoscroll