How can I achieve AngularJS sub view page routing concept? - angularjs

I have a question on routing. I have attached on sample Image in the first reply of the question which functionality I want to achieve.
I have a single page app with ng-view tag. Under ng-view, I have 3 links: :Link1, Link2, and Link3. Clicking on each link should show the results on the right side of the screen under the ng-view only. I want to change my url as well, eg: websitename/home/link1, websitename/home/link2, websitename/home/link3, based on a default url of websitename/home. I don't want to refresh the left side panel while changing the links.
How can I achieve this feature with AngularJS? Should I use ngInclude/ngDirectives/ngview/ngSwitch? I am totally clueless. From online tutorials, I've learned that I should not use multiple ng-view tags for the same application. Is it true?
Moreover, I can't move ng-view to right hand panel because in my header there are many page navigations. This view is one of them.

You will have to make child url for a parent url and then access that child url so that the view for that child url can render under the view of the parent url.
For this you can use ui.router module.
Ex:
$stateProvider.state({
name: "parent-view",
url: "/parentView",
templateUrl: "templates/parentView.html"
});
$stateProvider.state({
name: "parent-child-view",
url: "/childView",
templateUrl: "templates/childView.html",
parent: "parent-view"
});
now from the controller you have to call $state.transitionTo("parent-child-view");

Related

Angular UI-Router: How to call child state without its parent?

We have recently move our application to Ui-Router from ng-route. And we are trying to refactor some points in our code regarding routing.
We have a list view and a detail view in our application. The detail view will be shown as a pop-up on top of the list view, so in order not to initialize all list controller logic again and again, we have defined our detail view as a child view-state. It looks like this:
$stateProvider
.state('list',
{
url: "/",
template: '<list-directive></list-directive>'
})
.state('list.detail',
{
url: "/detail/{item_id}",
template: '<detail-directive></detail-directive>'
})
It actually works as expected. When i open a detail view from a list view, the list view (i mean the controller) does not run again, and when i close the detail view, the list view remains.
But now we would also like to call the detail view DIRECTLY, without revoking the parent. Currently when i directly call the detail state, the parent controller runs also.. How can i achieve this?
Is a parent - child relationship not a appropriate one for our scenario?
According ui-router doc :
Child states can be used to drill down from a more general feature to
more specific one, or to implement a master/detail pattern. (...) The child state’s view usually renders inside a viewport that the parent state created. This is referred to as Nested Views (...) These substates share the common parent state contacts and inherit data and behavior from the parent.
So I think you should use it if you don't want these behaviour .
It did work like this:
$stateProvider
.state('list',
{
url: "/",
template: '<list-directive></list-directive>'
})
.state('detail',
{
url: "/detail/{item_id}",
template: '<detail-directive></detail-directive>'
})
.state('list.detail',
{
url: "detail/{item_id}",
template: '<detail-directive></detail-directive>'
})
But the angular is very fragile.. and behaves very different with very small changes.. the slashes are the problem.
Child state list.detail does not have a leading slash in its URL. But our new state detail (which has exctly the same content as the list.detail) should have a leading slash so it can be called directly. So We have the same state both as child and as parent.
Now when we call directly the URL "host/detail/5" we go to detail view without revoking the parent. And we we call ui-sref=".detail(5)" we go to child state (list.detail) within the parent

Angular JS Scrolling to an element in another page

I am building a SPA using Angular 1.4 and Grails. In my home page I have 3 sections namely section1, section2 and section3 with the same ID. I have a dropdown menu on the top and I have used the scroll-to plugin to scroll to the respective sections on click.
<li ><a href scroll-to="section1">History</a></li>
My Problem is that when I move to another route, the dropdown menu will still be at the top and I am not sure how to get this to scroll to that element in the home route from another route. This is what I have tried.
<li >Economic Weight</li>
And in the route config I did this
when('/home/:section', {
templateUrl: '/euribron/js/app/templates/home.html',
controller: 'HomeController'
}).
and in controller I did this
if($routeParams.section) {
var element = document.getElementById($routeParams.section);
smoothScroll(element);
}
But this does not work when I am on the same route and it does not seem elegant. Is there an easier way to achieve this in an efficient way.
Instead of scroll-to plugin, you can use $anchorScroll service to manage autoscrolling. Please see this post's answer How to handle anchor hash linking in AngularJS. Basically, you need to set $location.hash() to the section's id that you can get from $routeParams and call $anchorScroll() function to handle the rest. Use different id for the sections.

UI Router event on navigate to self page

I'm using ui-router for my page routing in my AngularJS app. I'm encountering an issue where if I click a hyperlink on a page that refers to the page/route I'm already on (i.e. self), nothing will happen.
For example, if you're scrolled halfway down a page and you click a hyperlink that refers to the current page, it will seem as though the link doesn't load, when in actual fact nothing needs to be loaded as the user is already on the right page.
The goal in this situation is to have the viewport moved to the top of the page, but I cannot get any event from ui-router for this event. I've tried $stateChangeStart and $stateChangeSuccess, but as no route change occurs, these do not fire.
My urls are of the form http://localhost:3000/projects/myproject and ui-router is configured as follows:
state('app.projects', {
url: '/projects/:projectname',
templateUrl: 'projects.html',
controller: 'ProjectsCtrl'
}).
Without adding extra logic in the controller (which would obfuscate the natural href="projects/myproject" format), what can be done to achieve this?
Without controller:
<a ui-sref="stateName({reload: true})" target="_blank">Link Text</a>
With controller:
$state.go($state.name, {}, {reload: true});
You can simply use # at the end of the link. href="projects/myproject#"

Confused in how to implement tab based app with angular ui-router (like gmail)

I was thinking last night about how to port our crud application to a tab based app, like gmail. First, reading a lot about ui-router, i thought in create a tabService that will create a new tab for each state change (listen $rootScope.stateChangeSuccess), the new tab would include the corresponding view (ui-view="bancos") that will display the state template.
My first test and my first problem, when shown the list of items, a click over one of the item (itemId=4 for example) should open a new tab and display the item with id=4 in this tab, inside the corresponding (ui-view="bancos/4"). Note how i'm trying to map named ui-view with states to display the state defined templates in the corresponding ui-view.
I know that ui-router sample seems to do what i'm trying, but they are using nested states with unamed ui-view inside parent state template. In my case, the parent state ui-view element and his child state ui-view element should be sibling.
Considering the nature of angular, tree structured, and the nature of ui-router states, tree structured too, can i use ui-router to implement my requirements (crud application with tab based design).
Regards
Danny
Here is me example, if you have questions i can answer
http://plnkr.co/edit/tqfPVFaesSe9Q6ZkPMzE?p=preview
Short explain: don't use tabs instead of that create a fake, works well and you save a lot of work.
Best
I was looking for the same info, does the following example help you?http://plnkr.co/edit/gMsKK9l7J0B8vZIMIVfD?p=preview (Not my coding.)
Edit:
I've found more useful data.
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
In my code, I have set it up like this:
.state('surveys.list', {
url: '/list',
templateUrl: "survey/surveys_overview.tpl.html",
controller: 'SurveyOverview'
})
.state('surveys.create', {
abstract: true,
templateUrl: "survey/survey.create.tpl.html",
controller: 'SurveyManageController'
})
.state('surveys.create.views', {
url: '/create',
views: {
'details': {
templateUrl: "survey/survey.create.details.tpl.html"
},
'steps': {
templateUrl: "survey/survey.create.steps.tpl.html"
}
}
});
The surveys.create.tpl.html just has a and two tabs with each one div:
<div ui-view="details"> & <div ui-view="steps">

Updating URL in Angular JS without re-rendering view

I'm building a dashboard system in AngularJS and I'm running into an issue with setting the url via $location.path
In our dashboard, we have a bunch of widgets. Each shows a larger maximized view when you click on it. We are trying to setup deep linking to allow users to link to a dashboard with a widget maximized.
Currently, we have 2 routes that look like /dashboard/:dashboardId and /dashboard/:dashboardId/:maximizedWidgetId
When a user maximizes a widget, we update the url using $location.path, but this is causing the view to re-render. Since we have all of the data, we don't want to reload the whole view, we just want to update the URL. Is there a way to set the url without causing the view to re-render?
HTML5Mode is set to true.
In fact, a view will be rendered everytime you change a url. Thats how $routeProvider works in Angular but you can pass maximizeWidgetId as a querystring which does not re-render a view.
App.config(function($routeProvider) {
$routeProvider.when('/dashboard/:dashboardId', {reloadOnSearch: false});
});
When you click a widget to maximize:
Maximum This Widget
or
$location.search('maximizeWidgetId', 1);
The URL in addressbar would change to http://app.com/dashboard/1?maximizeWidgetId=1
You can even watch when search changes in the URL (from one widget to another)
$scope.$on('$routeUpdate', function(scope, next, current) {
// Minimize the current widget and maximize the new one
});
You can set the reloadOnSearch property of $routeProvider to false.
Possible duplicate question : Can you change a path without reloading the controller in AngularJS?
Regards
For those who need change full path() without controllers reload
Here is plugin: https://github.com/anglibs/angular-location-update
Usage:
$location.update_path('/notes/1');
I realize this is an old question, but since it took me a good day and a half to find the answer, so here goes.
You do not need to convert your path into query strings if you use angular-ui-router.
Currently, due to what may be considered as a bug, setting reloadOnSearch: false on a state will result in being able to change the route without reloading the view. The GitHub user lmessinger was even kind enough to provide a demo of it. You can find the link from his comment linked above.
Basically all you need to do is:
Use ui-router instead of ngRoute
In your states, declare the ones you wish with reloadOnSearch: false
In my app, I have an category listing view, from which you can get to another category using a state like this:
$stateProvider.state('articles.list', {
url: '{categorySlug}',
templateUrl: 'partials/article-list.html',
controller: 'ArticleListCtrl',
reloadOnSearch: false
});
That's it. Hope this helps!
We're using Angular UI Router instead of built-in routes for a similar scenario. It doesn't seem to re-instantiate the controller and re-render the entire view.
How I've implemented it:
(my solution mostly for cases when you need to change whole route, not sub-parts)
I have page with menu (menuPage) and data should not be cleaned on navigation (there is a lot of inputs on each page and user will be very very unhappy if data will disappear accidentally).
turn off $routeProvider
in mainPage controller add two divs with custom directive attribute - each directive contains only 'templateUrl' and 'scope: true'
<div ng-show="tab=='tab_name'" data-tab_name-page></div>
mainPage controller contains lines to simulate routing:
if (!$scope.tab && $location.path()) {
$scope.tab = $location.path().substr(1);
}
$scope.setTab = function(tab) {
$scope.tab = tab;
$location.path('/'+tab);
};
That's all. Little bit ugly to have separate directive for each page, but usage of dynamic templateUrl (as function) in directive provokes re-rendering of page (and loosing data of inputs).
If I understood your question right, you want to,
Maximize the widget when the user is on /dashboard/:dashboardId and he maximizes the widget.
You want the user to have the ability to come back to /dashboard/:dashboardId/:maximizedWidgetId and still see the widget maximized.
You can configure only the first route in the routerConfig and use RouteParams to identify if the maximized widget is passed in the params in the controller of this configured route and maximize the one passed as the param. If the user is maximizing it the first time, share the url to this maximized view with the maximizedWidgetId on the UI.
As long as you use $location(which is just a wrapper over native location object) to update the path it will refresh the view.
I have an idea to use
window.history.replaceState('Object', 'Title', '/new-url');
If you do this and a digest cycle happens it will completely mangle things up. However if you set it back to the correct url that angular expects it's ok. So in theory you could store the correct url that angular expects and reset it just before you know a digest fires.
I've not tested this though.
Below code will let you change url without redirection such as: http://localhost/#/691?foo?bar?blabla
for(var i=0;i<=1000;i++) $routeProvider.when('/'+i, {templateUrl: "tabPages/"+i+".html",reloadOnSearch: false});
But when you change to http://localhost/#/692, you will be redirected.

Resources