angularjs ui-router persistant page elements - javascript

I am trying to get my head around angularjs and ui-router at the moment, my application is split into 3 layouts basically (Marketing Layout, Authentication Layout & Application Layout). I am currently struggling to get my ui-router working to allow me to do this, I understand that I can have parent states that are abstracted (correct?) but I am struggline to get child states to inherit parent views etc.
Basically what I wanting to do is the following - I'll use the application section of the site for the example.
A user is logged in logged, and the screen is split into a header, sidebar, and main content area, where the ever the user goes within applicaiton section of the site, the header and sidebar will remain but the main contnet will update to the relevant content. Here is what I have so far
app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider', '$httpProvider', function($stateProvider, $urlRouterProvider, $locationProvider, $httpProvider){
$urlRouterProvider.otherwise('/application/dashboard');
$stateProvider
.state('application', {
abstract : true,
views : {
'#' : {
templateUrl : 'templates/layout.html',
controller : 'applicationController'
},
'top#index' : {
templateUrl : 'templates/app/header.html'
},
'left#index' : {
templateUrl : 'templates/app/sidebar.html'
},
'main#index' : {
templateUrl : 'templates/app/main.html'
}
}
})
.state('application.dashboard', {
url : '/application/dashboard',
views : {
'#' : {
templateUrl : 'templates/schools/manage.html',
controller : ''
},
'main#app' : {
templateUrl : ''
}
}
})
}]);
https://plnkr.co/edit/6fJNjTFhoKqyN82P7NvL?p=preview
As you will able to see I can see the manage.html template, but on the preview I should also see the page header from the header.html template, and the sidebar.html also...
Any ideas as to why I am not? I assume it is something I am doing wrong.

If I understand what you're saying, you basically want an app skeleton of Header, Sidebar and MainContent. The only thing that should change is MainContent. The contents of the header or sidebar may change, but they'll be persistent.
I personally think router should have nothing to do with this; a router should route. Having it be a data bus and application state manager seems to tightly couple things that shouldn't be.
Anyway, with that said, here's how I go about it. In my index.html, here's my markup:
<body ng-app="app">
<div header-directive></div>
<div ng-view></div>
<div footer-directive></div>
</body>
That's it. If you have other elements and need them to be responsive, just use a bootstrap grid (like to lay in sidebars or whatever). If you don't have to worry about any of that, a good ol' simple html table does the job perfectly. You can use CSS to do fixed positioning, height, etc.
With this setup, your header and footer never go away, and the router will only change the content in the ng-view. You use the router for the one and only thing that the term "router" makes sense for: routing from here, to there. Here's a sample router config:
.config(['$routeProvider', ...
function ( $routeProvider, ... ) {
$routeProvider.
when('/home', {
templateUrl: 'views/home.html',
controller: 'HomeController',
controllerAs: 'homeCtl'
})
.when('/info', {
templateUrl: 'views/info.html',
controller: 'InfoController',
controllerAs: 'infoCtl'
})
....
For communication between the three components, you can just create a service that they all inject. This way you can send any one of the elements (or all of them) instructions to change their content, hide/show themselves, whatever you need.
Angular does sometimes drastically overcomplicate something simple. This is definitely one of them. The Router in Angular 2 is a complicated behemoth.

Related

Why wont ngRoute now work and breaks all other Angular functionality?

I am trying to us ngRoute to inject HTML into my web page but it doesn't work.
I have the below in my main html document.
<main ng-view></main>
next i created a folder call views with two HTML files (news.html & sectors.html)
I have also included angular-route.min.js into my project correctly. All paths for all files are correct.
Next I set up my routes as below, but it renders a blank page and even breaks the rendering of any ng-includes I have implemented which previously worked. In addition, it also breaks all other Angular functionality like filtering. There are no errors in the console, it just seems that ngRoute wont work.
var app = angular.module('app', ['ngRoute']);
app.config(['$routeProvider', function($routeProvider){
$routeProvider
.when('/sectors', {
templateUrl: 'views/sectors.html'
})
.when('/news', {
templateUrl: 'views/news.html',
controller: 'primaryController'
}).otherwise({
redirectTo: '/sectors'
});
}]);
Any help welcome.
You can check templateUrl for the path to your html templates. Everything else seems correct.

Waypoints.js not working as expected with angular views

I am trying to apply some css animation to some of the elements in my views when they reach the top of the window (offset 70%) with the help of waypoints.js.
I've created a directive as shown below.
angular.module("myApp")
.directive("clWaypoints",function(){
return{
restrict:"A",
link: function(scope,element,attrs)
{
var wayp=new Waypoint({
element:element,
handler:function(direction){
if(direction=="down")
{
var animation = scope.$eval(attrs.clWaypoints).animation;
element.css('opacity', '1');
element.addClass("animated " + animation);
}
},
offset:'70%',
})
}
}
})
Below is my app.js file
angular
.module('myApp', [
'ngRoute',
])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/home.html',
controller: "mainCtrl"
})
.when('/about', {
templateUrl: 'views/about.html'
})
.otherwise({
redirectTo: '/'
});
});
Both the views contain elements which use the cl-waypoints="{animation:'fadeInUp'}" directive.
When the app is loaded on a browser, the animations work as expected, but when I navigate to another view and begin to scroll down the elements in that view are not animated. Anyhow if I refresh the page, it works just fine.
Looks like the page needs to be refreshed for waypoint.js to do its magic. Please let me know if there is a solution to this problem.
Would appreciate any help.
Thanks.
Was able to find a solution to this problem. Waypoint requires us to call a function (Waypoint.refreshAll()) every time we make changes to the DOM, in our case changing the view content.
Added an $viewContentLoaded event listener (in the parent controller) to call the method whenever the view changes.
$scope.$on('$viewContentLoaded', function(event){
$timeout(function() {
Waypoint.refreshAll()
},0);
});
Hope this helps others.

Angular-ngRoute: force ng-view contents, allow navigation afterwards

While using ngRoute, I want to have Angular configured so that the current contents of ng-view are left as the contents for the current route, and allow the user to navigate away to different routes, rendering their respective templates afterwards:
Plunker
HTML
<ul class="menu">
<li>view1</li>
<li>view2</li>
</ul>
<div ng-view>
<ul>
<li>Some</li>
<li>Soon obliterated</li>
<li>Content</li>
</ul>
</div>
JavaScript
angular.module('myApp', ['ngRoute'])
.config(function($routeProvider) {
$routeProvider
.when('/view1', {
templateUrl: 'view1.html',
controller: 'View1Ctrl'
})
.when('/view2', {
templateUrl: 'view2.html',
controller: 'View2Ctrl'
})
.otherwise({
redirectTo: '/view1'
})
})
.controller('View1Ctrl', function() {
})
.controller('View2Ctrl', function() {
});
When the user first sees the page, I want him to see the following:
Note: Angular needs to be bootstrapped at this point, with directives functioning in this area.
Note 2: This content should be in the actual HTML of the page, not in a template or script tag.
Then, when the 'view2' link is clicked:
And then, when the 'view1' link is clicked:
My first thought was using $route.updateParams(newParams) but I think that's not really its purpose.
EDIT
I ended up using
//Server-side rendered code
myModule
.config(['$routeProvider', function($routeProvider){
$routeProvider.when('<# my current route #>',
{
templateUrl: '/server-static',
});
angular.bootstrap(myModule);
In app.js:
myModule
.config('$routeProvider', function($routeProvider){
$routeProvider.when('/my-client-routes',
{
templateUrl: '/my-template.html',
}); // etc.
How can I trick Angular into thinking that the contents of ng-view are the appropiate contents for the current entry route? Can I just cancel route resolution/ngView directive (or make it transclude) rendering for first load? Or if not possible, what's the preferred method to do this?
Thanks
EDIT: See this answer that proposes adding the contents of ng-view to $templateCache through a custom directive.
It's possible for templateUrl to be a function. In which case you can actually change the view based on some kind of state:
var initialized = false;
$routeProvider
.when('/view1', {
templateUrl: function(){
if(initialized){
return 'view1.html';
}
initialized = true;
return 'view-initial.html';
},
controller: 'View1Ctrl'
})
Here is a working Plunker based on yours.
The task you're asking help for can be achieved in a number of ways. #Josh offers a good one. Using $templateCache is another option. But, as you correctly said, these are tricks.
Correct and only recommended approach is to use dedicated template for a default route. You can specify it via external file, via template cache or even script tag, but it's much better, clear and easy to support.
If it's your own code - just choose any preferred way you like. If you want it to be shared with community, used as open-source or enterprise solution - I'd suggest to use the only recommended approach.
Or... look into ui-router )) . May be it's nested views support is the option you need.

Link to #ID element from another page in AngularJS

I've tried all the solutions presented here:
How to handle anchor hash linking in AngularJS
But none worked for me.
In my header.html I have a link: <a id="button" href="#/views/home#page"> Contact</a></li>
To an ID in home.html
When I am in /home it works, but when I am in another page it doesn't work.
I tried using ##page with no success.
Or putting this in app.js:
app.run(function($rootScope, $location, $anchorScroll, $routeParams) {
$rootScope.$on('$routeChangeSuccess', function(newRoute, oldRoute) {
$location.hash($routeParams.scrollTo);
$anchorScroll();
});
});
and customizing my link:
href="#/views/home/?scrollTo=page"
Can someone explain which files should I edit and how?
Edit:
I started from Angular-Seed
My app.js is:
'use strict';
// Declare app level module which depends on views, and components
angular.module('myApp', [
'ngRoute',
'myApp.view1',
'myApp.view2',
'myApp.training',
'myApp.faq',
'myApp.media',
'myApp.contact',
'myApp.home',
'myApp.apply',
'myApp.classes',
'myApp.version'
]).
config(['$routeProvider', function($routeProvider) {
$routeProvider.otherwise({redirectTo: '/views/home'});
}]);
And in every view I have another js file like training.js which looks like:
'use strict';
angular.module('myApp.training', ['ngRoute'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/views/training', {
templateUrl: 'views/training/training.html',
controller: 'TrainingCtrl'
});
}])
.controller('TrainingCtrl', [function() {
}]);
It is configured based on angular-seed model.
So, when, I am in view /faq that has the partial header with the menu and all the links, how can I link to a specific ID in view /home?
I think that the problem is that you have the logic for scrolling to that hash in the $routeChangeSuccess and as the documentation says, this event is:
Broadcasted after a route dependencies are resolved. ngView listens
for the directive to instantiate the controller and render the view.
So the view is not rendered yet, therefore the DOM element with that id doesn't exist yet.
Try putting that logic in the onload event of the ngView directive instead.
I've created this plunker: http://plnkr.co/edit/S7bUT8iYY7UEti71X5Z8?p=preview that shows that if you add that logic into the onload event of the ngView directive everything works fine.

AngularJs: Hide navbar element based on route?

I'm trying to figure out how to show or hide an element in my navbar based on the route, or the current view being displayed. For example, I have an basic/advanced toggle button that I put in the navbar (Bootstrap 3) when the user is on the search form. But when they are anywhere else in the app that toggle button should be hidden.
In terms of the DOM, it's just a list item that builds out the nav. I'm not sure if I should show or hide based on a global value that gets set on each view, or if I can just use the route or view name. If so how that would work?
Thanks!
One solution is to build a function in the controller that is responsible for the navbar that could be queried to determine if the element should be displayed:
$scope.isActive = function(viewLocation) {
return viewLocation === $location.path();
};
(The above code uses Angular's $location service)
Then within the template, you can show/hide based on the result of the call (passing in the route that should toggle displaying the element):
ng-show="isActive('/search-form')"
Here's the approach I took with ui-router:
I only want to hide the navbar for a small number of pages so I went with an opt out property on the state(s) that I want to hide the navbar.
.state('photos.show', {
url: '/{photoId}',
views: {
"#" : {
templateUrl: 'app/photos/show/index.html',
controller: 'PhotoController'
}
},
hideNavbar: true
})
Inject $state in your navbar's controller and expose it to the template:
$scope.state = $state;
Then add ng-hide to your navbar template:
<nav ng-hide="state.$current.hideNavbar" ...
Above works perfectly using ui-router don't forget to pass $scope and $state within your function
Example:
app.controller('LoginCtrl', function($scope, $state){
$scope.state = $state;
console.log($state); // this will return the current state object just in case you need to see whats going on for newbies like me :)
});

Categories

Resources