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 :)
});
Related
I'm new to angularJS and i'm trying to have different backgrounds for different pages. But i'm having difficulty since i have my view tied to some elements in my index.html.
index.html
<div class="top-container">
<div class="navigation" >
<nav>....</nav>
</div>
<div ui-view="topContent"></div>
</div>
The ui-view injects new content for each page which is tied to the nav element. I currently have one background for the entire site using
.top-container{
background-image: url('bg.jpeg');
}
Any ideas how i can change the background for different pages? Any way i can pass some variable or something to change the bg image based on the page i'm on.
Thanks
This is how I would do it. Firstly, set the background on every view.
$stateProvider.state("home",
{
templateUrl: "/scripts/app/home/home.html",
url: "/home",
controller: 'HomeCtrl',
data: {
background: 'home'
}
})
.state("login",
{
templateUrl: "/scripts/app/login/login.html",
url: "/login",
controller: 'LoginCtrl',
data: {
background: 'login'
}
});
Now grab that setting on every state change and put it in the root scope.
app.run([
'$rootScope', function($rootScope)
{
$rootScope.$on('$stateChangeStart', function(event, toState)
{
$rootScope.background = toState.data.background;
});
}
]);
Now in the HTML set your background from the root scope on whatever element you want. Mind you, this has to be inside your ng-app declaration.
<body ng-class="background">
And that is the beauty of Angular for you.
Also, this is my go-to solution for anything that changes for every state. Like page title for starters..
You can use ng-class directive
ng-class="{'class1': yourvalue == 'something' || yourvalue == 'somethingelse'}"
you can use ngStyle for your CSS class. here
is the demo plunker for changing color.
Maybe you can add new class name on .top-container div when view changed.
$scope.$on('$routeChangeStart', function(next, current) {
... you could trigger something here ...
});
You can use the $routeChangeStart listener, which will be fired every time you try to switch to a different route/view.
$rootScope.$on('$routeChangeStart', function (event, next, current) {
if(current.loadedTemplateUrl.indexOf("/call") > -1){
// set the required background image
}else if(current.loadedTemplateUrl.indexOf("/login") > -1){
// set the required background image
}
});
You can use javascript to set your background image or ng-class. For example
ng-class="{'one': url == '/login'}"
Note: Don't forget to add background-image property to your class.
I have a simple modal that appears when the user enters a page, but it is creating two of them. I have looked through the app and there are not two calls to the modal.
Here is the controller for it:
angular.module('rokoApp')
.controller('FinanceCtrl', function($scope, $modal) {
$modal.open({
templateUrl: 'includes/modal.html',
controller: function ModalInstanceCtrl($scope, $modal,$modalInstance){
console.log('opened')
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
});
will be much better if you give us an example of functional code in plunker or jsfiddle, because the problem could be in another place.
For example:
When we started creating an app with AngularJS usually we put ng-controller in a upper tag and write the first lines of our app. The problem is that some times when we added routing we forget remove the ng-controller and then the controller is executing twice. The first time with the configuration of the module and again with the processing of the html.
So the solution here is to remove the tag in the html if we use routing or delete the controller property of routing and keep the ng-controller in the html.
I will let you the two codes:
With the error: http://plnkr.co/edit/TmQ5QhjD55WTurQNaEIv
Without the error: http://plnkr.co/edit/1xGUGEvAZ6jUBs8CbhTT
My Question is can we reload the view in ui-router if you are on same state.
check my code at`http://plnkr.co/edit/MA7CuyH2RFrlaoAgBYog?p=preview
My app.js file is
var app = angular.module('plunker', ['ui.router']);
app.config(function($stateProvider) {
$stateProvider
.state("view1", {
url: "/view1",
templateUrl: "x.html"
})
.state("view2", {
url: "/view2",
templateUrl: "y.html"
})
})
app.controller("MainCtrl",function(){});
And index page is
<body ng-controller="MainCtrl">
Accounts
Dashboard
<div ui-view></div>
Now click on Dashboard link here you will see a text box. fill any value in that. Now again click on Dashboard link. now the state should reload and all data of page should be reloaded including its controller. Please make sure to ui-router only.
Thanks
add attribute to the element with
ui-sref=""ui-sref-opts="{reload:true}"
example:
<a ui-sref-opts="{reload:true}" ui-sref="app.post.applicants">Applicants</a>
Use ng-click and write a controller function with $state.go
You could catch the click in a "ng-click" and use the $state service to transition/reload pragmatically
I had this similar issue and I solved this in two simple way ...
First I wrote a function into related controller (my state was 'inbox' you can use your own ui-state):
$scope.reload = function() {
$state.go('inbox', null, { reload: true });
}
Second call this function to anchor tag:
<a ng-click="reload()">Inbox</a>
Hope it will help. :)
I am trying to apply animations to ng-view (routing) depending of the views involved.
For example, from View1 to View2 I need the View1 leaving through the left side and View1 entering from the right side. Otherwise, from View2 to View1 I need View2 leaving through the right side and View1 entering from the left side.
But I have also situations where I need apply different animations to both views, for example, View1 leaving fading out and View2 entering scaling up.
What I am doing is using a scope associated variable as class in the ng-view:
<div ng-view class="{{transition}}"></div>
This variable is set in each route change with something like this in each controller:
$scope.transition=Global.transition;
$rootScope.$on("$routeChangeStart",function (event, current, previous) {
// Here I get the leaving view and the entering view and the kind of transition is selected
...
$scope.transition=selectedLeavingTransition; // Set the transition for the leaving view
Global.transition=selectedEnteringTransition; // Set the transition for the entering view
});
Global is a service to set the transition variable for the entering scope from the leaving scope.
This way, when a route change is detected, the current ng-view is set with the class associated to selectedLeavingTransition, and the entering ng-view is set with the class associated to selectedEnteringTransition.
For example, if the route change was from View1 to View2 the ng-views during the animation could be:
<div ng-view class="fadeOut ng-animate ng-leave ng-leave-active"></div>
<div ng-view class="scaleUp ng-animate ng-enter ng-enter-active"></div>
The CSS in this case could be:
fadeOut.ng-leave {animation:1s fadeOut;}
scaleUp.ng-enter {animation:1s scaleUp;}
Though it works, I am wondering if there is a simpler way to do it as it seems a little mess.
An alternative solution that doesn't require much code is to define your animations on your routes:
$routeProvider.when('/view1', {
templateUrl: 'view1.html',
controller: 'View1Controller',
animations: {
enter: 'enter-left',
leave: 'leave-left'
}
});
Then use a directive to retrieve the current route's animations and add them to the element:
app.directive('viewAnimations', function ($route) {
return {
restrict: 'A',
link: function (scope, element) {
var animations = $route.current.animations;
if (!animations) return;
if (animations.enter) element.addClass(animations.enter);
if (animations.leave) element.addClass(animations.leave);
}
};
});
And put the directive on the element that contains the ngView directive:
<body ng-view view-animations></body>
Demo: http://plnkr.co/edit/Y3ExDyiPIJwvVKO4njBT?p=preview
Edit: New solution.
To set animations during run-time I would use a service just like you are doing, but a directive to apply them.
Very basic example of service:
app.factory('viewAnimationsService', function ($rootScope) {
var enterAnimation;
var getEnterAnimation = function () {
return enterAnimation;
};
var setEnterAnimation = function (animation) {
enterAnimation = animation;
};
var setLeaveAnimation = function (animation) {
$rootScope.$emit('event:newLeaveAnimation', animation);
};
return {
getEnterAnimation: getEnterAnimation,
setEnterAnimation: setEnterAnimation,
setLeaveAnimation: setLeaveAnimation
};
});
And the directive:
app.directive('viewAnimations', function (viewAnimationsService, $rootScope) {
return {
restrict: 'A',
link: function (scope, element) {
var previousEnter, previousLeave;
var enterAnimation = viewAnimationsService.getEnterAnimation();
if (enterAnimation) {
if (previousEnter) element.removeClass(previousEnter);
previousEnter = enterAnimation;
element.addClass(enterAnimation);
}
$rootScope.$on('event:newLeaveAnimation', function (event, leaveAnimation) {
if (previousLeave) element.removeClass(previousLeave);
previousLeave = leaveAnimation;
element.addClass(leaveAnimation);
});
}
};
});
Demo: http://plnkr.co/edit/DuQXaN2eYgtZ725Zqzeu?p=preview
I have been working on it and I have a neater solution, what I was doing had some problems. Now I am just using the $routeChangeStart at root scope and selecting there the leaving and enter transitions.
The only problem I have is that on the routeChangeStart event I can't modify the leaving view so I can't establish the leaving transition to the ngView element class attribute. I had to set it directly through the DOM (I know that is bad practice).
I tried to modify the leaving view through a shared service, the root scope and $apply() but none of them worked. Once the routeChangeStart event is launched the view seems static.
Here is a working example: jsfiddle.net/isidrogarcia/Fs5NZ
Short version of my question is: How do I change the URL without need to trigger route change or without need to run all the controllers on the currently displayed page?
Details:
I have a template which is displayed inside the <ng-view> that has regions governed by 3 controllers. On the very top of the page I have an interactive map. When you click on the regions it broadcasts a click and other component picks up on it and displays data about this region. Really simple setup.
What I would like to do is allow my users to deep link to the content. So every time someone clicks on a link I'd like to change the URL that can be copied and pasted to another browser. Some other user could just click the link and see the same state the first one saw.
Currently I change the location with code similar to this one:
$scope.$on('mapRegionClick', function($scope, regionCode) {
var url = generateURL(regionCode);
$scope.currentScope.$apply(function(){
$location.path(url);
});});
The URL is then picked up in my routing and the map plus data displays correctly. The downside of this is that every time I click on the map and URL changes the whole template / view is regenerated. Because generating the map is kind of heavy I'd like to trigger only a change to the data presenting controller.
Is it possible? How?
I could do some communication between controllers and achieve the my goal but then I would not be able to do deep linking.
PS: I do not want to use $location.search() and reloadOnSearch=false. my links have to be pretty :)
Sounds like you don't want to use $route service.
The $route service is designed to reload the controllers so that there is no difference between navigating to a URL and refreshing the URL. We do this by doing a full reload on every URL change. This is intentional.
Sounds like your use case, should not be using $route, just $location and ng-include.
You can use $locationChangeStart event to store the previous value in $rootScope or in a service. When you come back, just initialize all the previously stored values from the $rootScope. Check this quick demo using $rootScope.
var app = angular.module("myApp", ["ngRoute"]);
app.controller("tab1Ctrl", function($scope, $rootScope) {
if ($rootScope.savedScopes) {
for (key in $rootScope.savedScopes) {
$scope[key] = $rootScope.savedScopes[key];
}
}
$scope.$on('$locationChangeStart', function(event, next, current) {
$rootScope.savedScopes = {
name: $scope.name,
age: $scope.age
};
});
});
app.controller("tab2Ctrl", function($scope) {
$scope.language = "English";
});
app.config(function($routeProvider) {
$routeProvider
.when("/", {
template: "<h2>Tab1 content</h2>Name: <input ng-model='name'/><br/><br/>Age: <input type='number' ng-model='age' /><h4 style='color: red'>Fill the details and click on Tab2</h4>",
controller: "tab1Ctrl"
})
.when("/tab2", {
template: "<h2>Tab2 content</h2> My language: {{language}}<h4 style='color: red'>Now go back to Tab1</h4>",
controller: "tab2Ctrl"
});
});
<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-route.js"></script>
<body ng-app="myApp">
Tab1
Tab2
<div ng-view></div>
</body>
</html>