AngularJS redirect to view on button click - angularjs

I'm trying to load a new view when a button is clicked. For some reason it isn't working with one of my paths. It will work with the path /lookup and will go to the lookup page, but when I change the path to /search it does nothing? I'm confused.
Here is my controller:
(function () {
'use strict'
angular
.module('crm.ma')
.controller('navbarCtrl', function ($location) {
var vm = this;
vm.redirect = function () {
$location.url('/search');
}
});
})();
Here is my button
<button class="btn default-btn advancedbtn" ng-click="redirect()">Advanced</button>
And here's part of my route file if that will help at all.
.state('index.topnavbar', {
url: '/topnav',
templateUrl: 'app/components/common/topnavbar.html',
controller: 'navbarCtrl as vm'
})
.state('index.search', {
url: '/search',
templateUrl: 'app/components/common/topnav_advancedmodal.html',
controller: 'AdvancedSearchCtrl as vm',
data: {
pageTitle: 'Advanced Search'
}
})
If any other code is needed please let me know. Thanks.

Do you get any errors in the console? Does your template exist? If Angular can't find your template it won't transition to the route. You can check the network requests and console for errors.
You could try using ui-sref:
<a ui-sref="index.search">Advanced</a>
If you prefer to keep the redirect method, can you add a console.log and do you see that log? If not then your scoping is off.
If so, and you prefer to keep your redirect method rather than use ui-sref (in case you want to check something before redirecting), you can inject $state into your controller and call the method:
$state.go('index.search');
Update to add to follow-up question
To help with your follow-up question of reloading if clicking once you're already there, according to the UI-Router documentation, you can specify the option to reload like this:
<a ui-sref="index.search" ui-sref-opts="{ reload: true }">Advanced</a>

Your call for redirect method are not in the scope. You have to change the redirect() to vm.redirect() in the html.
before:
<button class="btn default-btn advancedbtn" ng-click="redirect()">Advanced</button>
after:
<button class="btn default-btn advancedbtn" ng-click="vm.redirect()">Advanced</button>

This might help you
When you alias controller with some name you need to use aliasName in your view
change you code
<button class="btn default-btn advancedbtn" ng-click="vm.redirect()">Advanced</button>
Refer: https://docs.angularjs.org/api/ng/directive/ngController

Related

UI Router Refreshing Page Instead on State Change

I've had no luck with this particular issue. Everything I've looked up has to do with people having issues 'trying' to refresh the page instead of my issue where it's refreshing every time I go to a certain state. This ONLY happens in IE not chrome.
Code:
app.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('box', {
templateUrl: templatePath + 'box.html',
controller: 'BoxController',
controllerAs: 'box',
url: '/Box/{folder:string}'
})
.state('compose', {
templateUrl: templatePath + 'compose.html',
controller: 'ComposeController',
controllerAs: 'compose',
url: '/Compose/{messageSentId:int}'
})
.state('view', {
templateUrl: templatePath + 'view.html',
controller: 'ViewController',
controllerAs: 'view',
url: '/Box/{folder:string}/{messageId:string}/{messageSentId:string}'
});
$urlRouterProvider.otherwise('/Box/');
});
The problem is with my compose state. If I go directly to that state like so:
vm.compose = function () {
$state.go('compose', { messageSentId: 0 }, { notify: true });
};
It works just fine. However, if I add a different number besides 0 and call from a different controller it does not work:
vm.reply = function() {
var id = Math.floor($stateParams.messageSentId);
$state.go('compose', { messageSentId: id }, { notify: true });
};
I thought maybe it was having issues because it thought id was a string and therefore didn't match to /Compose/{messageSentId:int} so I added the Math.floor() but that didn't help.
Something to note is that if I fire my compose function first and go to that state the reply function will work. However, if I attempt to navigate with my reply function first the page reloads.
Another thing that I can confirm is that my controller for that page and the page itself loads just fine. You can actually see the form pop up briefly. The problem is once the controller has loaded a refresh is triggered. No errors. No nothing. Simply fails in IE.
After many hours of research and having found nothing similar to my issue on the web I made a simple change. I moved my state change out of my controller and used ui-sref instead:
Code:
<div class="btn-group pull-right" ng-if="view.canReply()">
<a class="btn btn-primary" ui-sref="compose({messageSentId: view.replyMessageId})">
<i class="fa fa-reply"></i> Reply
</a>
</div>
Where 'view' is my controllerAs since I don't use $scope and I set my $stateparameter on the variable replyMessageId. Maybe this will help someone.

Angular $location doesn't work

I created a plunkr for this code and it can be viewed here:
The problem is very simple. I am trying to create a master/details scenario. So there are two templates: listings and details. In the listing controller there is a methods redirects to the detials route. This method works well as i verified it with the debugger (via breaking point).
$scope.goToDetails = function(propItem) {
//$rootScope.currentProperty = propItem;
$location.path('/details/');
}
The 'details' path (see blow) calls the 'detailsController', which is currently (for testing purposes) defined as:
var detailsController = function($scope, $http, $routeParams, $rootScope) {
var dosomething = "do";
};
I verified with the debugger that the execution indeed reaches the "dosomething" command and that the route changes in the browser to "details". However, and HERE is the problem, when I continue with the debugger, angular changes the route back to the default route. I went over the definitions but nothing that i did seems wrong.Any ideas?
Here is how I defined the routes:
app.config(function($routeProvider) {
$routeProvider
.when('/main/', routes.main)
.when('/details/', routes.details)
.otherwise({
redirectTo: '/main'
});
});
var routes = {
main: {
templateUrl: 'PropertiesResults.html',
controller: 'listingController'
},
details: {
templateUrl: 'property-detail.html',
controller: 'detailsController'
},
}
Replace:
<a href="#" ng-click="goToDetails(property)" ...
With:
<a href="" ng-click="goToDetails(property)" ...
Or it will go to your otherwise route.
Change your link to be
Read More
A few things to consider:
Links should work just like in a regular html page
If you want to execute code on a new page, look into putting that code on the route, or in a controller in the new view.
If you want to conditionally enable or disable the link, think about disabling the link with something like <a ng-disabled="expression"... this might not work out of the box but you could add a custom directive.
If you still need to run that code in a controller method, consider using a <button type="button" class="link"... and style it to look like a link, e.g. display: inline; border: 0; background: transparent;"
Happy coding

AngularJs: Reload page

<a ng-href="#" class="navbar-brand" title="home" data-translate>PORTAL_NAME</a>
I want to reload the page. How can I do this?
You can use the reload method of the $route service. Inject $route in your controller and then create a method reloadRoute on your $scope.
$scope.reloadRoute = function() {
$route.reload();
}
Then you can use it on the link like this:
<a ng-click="reloadRoute()" class="navbar-brand" title="home" data-translate>PORTAL_NAME</a>
This method will cause the current route to reload. If you however want to perform a full refresh, you could inject $window and use that:
$scope.reloadRoute = function() {
$window.location.reload();
}
**Later edit (ui-router):**
As mentioned by JamesEddyEdwards and Dunc in their answers, if you are using angular-ui/ui-router you can use the following method to reload the current state / route. Just inject $state instead of $route and then you have:
$scope.reloadRoute = function() {
$state.reload();
};
window object is made available through $window service for easier testing and mocking, you can go with something like:
$scope.reloadPage = function(){$window.location.reload();}
And :
<a ng-click="reloadPage" class="navbar-brand" title="home" data-translate>PORTAL_NAME</a>
As a side note, i don't think $route.reload() actually reloads the page, but only the route.
location.reload();
Does the trick.
<a ng-click="reload()">
$scope.reload = function()
{
location.reload();
}
No need for routes or anything just plain old js
Similar to Alexandrin's answer, but using $state rather than $route:
(From JimTheDev's SO answer here.)
$scope.reloadState = function() {
$state.go($state.current, {}, {reload: true});
}
<a ng-click="reloadState()" ...
If using Angulars more advanced ui-router which I'd definitely recommend then you can now simply use:
$state.reload();
Which is essentially doing the same as Dunc's answer.
My solution to avoid the infinite loop was to create another state which have made the redirection:
$stateProvider.state('app.admin.main', {
url: '/admin/main',
authenticate: 'admin',
controller: ($state, $window) => {
$state.go('app.admin.overview').then(() => {
$window.location.reload();
});
}
});
Angular 2+
I found this while searching for Angular 2+, so here is the way:
$window.location.reload();
This can be done by calling the reload() method in JavaScript.
location.reload();
It's easy enough to just use $route.reload() (don't forget to inject $route into your controller), but from your example you could just use "href" instead of "ng-href":
<a href="" class="navbar-brand" title="home" data-translate>PORTAL_NAME</a>
You only need to use ng-href to protect the user from invalid links caused by them clicking before Angular has replaced the contents of the {{ }} tags.
On Angular 1.5 - after trying some of the above solutions wanting to reload only the data with no full page refresh, I had problems with loading the data properly. I noticed though, that when I go to another route and then I return back to the current, everything works fine, but when I want to only reload the current route using $route.reload(), then some of the code is not executed properly. Then I tried to redirect to the current route in the following way:
$scope.someFuncName = function () {
//go to another route
$location.path('/another-route');
};
and in the module config, add another when:
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/first-page', {
templateUrl: '/first-template',
controller: 'SomeCtrl'
}).when('/another-route', {//this is the new "when"
redirectTo: '/first-page'
});
}])
and it works just fine for me. It does not refresh the whole page, but only causes the current controller and template to reload. I know it's a bit hacky, but that was the only solution I found.
<a title="Pending Employee Approvals" href="" ng-click="viewPendingApprovals(1)">
<i class="fa fa-user" aria-hidden="true"></i>
<span class="button_badge">{{pendingEmployeeApprovalCount}}</span>
</a>
and in the controller
$scope.viewPendingApprovals = function(type) {
if (window.location.hash.substring(window.location.hash.lastIndexOf('/') + 1, window.location.hash.length) == type) {
location.reload();
} else {
$state.go("home.pendingApproval", { id: sessionStorage.typeToLoad });
}
};
and in the route file
.state('home.pendingApproval', {
url: '/pendingApproval/:id',
templateUrl: 'app/components/approvals/pendingApprovalList.html',
controller: 'pendingApprovalListController'
})
So, If the id passed in the url is same as what is coming from the function called by clicking the anchor, then simply reload, else folow the requested route.
Please help me improve this answer, if this is helps. Any, suggestions are welcome.
This can be done by calling the reload() method of the window object in plain JavaScript
window.location.reload();
I would suggest to refer the page. Official suggestion
https://docs.angularjs.org/guide/$location

use angular-ui-router with bootstrap $modal to create a multi-step wizard

The FAQ for ui-router has a section about integration with bootstrap $modals, but it doesn't mention anything about abstract views. I have 3 views under a single abstract view, so something like the following.
$stateProvider
.state('setup', {
url: '/setup',
templateUrl: 'initialSetup.html',
controller: 'InitialSetupCtrl',
'abstract': true
})
// markup for the static view is
<div class="wizard">
<div ui-view></div>
</div>
.state('setup.stepOne', {
url: '/stepOne',
controller: 'SetupStepOneCtrl',
onEnter: function($stateParams, $state, $modal) {
$modal.open{
backdrop: 'static',
templateUrl: 'setup.stepOne.html',
controller: 'SetupStepOneCtrl'
})
}
})
.state('setup.stepTwo', {
url: '/stepTwo',
controller: 'SetupStepTwoCtrl',
onEnter: function($stateParams, $state, $modal) {
$modal.open({
backdrop: 'static',
templateUrl: 'setup.stepTwo.html',
controller: 'SetupStepTwoCtrl'
})
}
})
.state('setup.stepThree', {
url: '/stepThree',
templateUrl: 'setup.stepThree.html',
controller: 'SetupStepThreeCtrl'
...
});
}]);
I've also tried to only add the onEnter block to the abstract state, and removed onEnter from each of the 3 child states. This actually seems to me like the right approach. The abstract state initializes and opens the $modal and the subsequent states should interpolate into , but when I tried this the ui-view container was empty.
I can think of some other hacky ways to workaround this but thought I'd ask to see if there's a canonical way of handling this.
Alternative way is to use ng-switch with ng-include combination inside $modal controller to dynamically load wizard step templates, that is if you don't mind sharing the same controller for all wizard steps:
<div ng-switch="currentStep.number">
<div ng-switch-when="1">
<ng-include src="'wizardModalStep1.html'"></ng-include>
</div>
<div ng-switch-when="2">
<ng-include src="'wizardModalStep2.html'"></ng-include>
</div>
<div ng-switch-when="3">
<ng-include src="'wizardModalStep3.html'"></ng-include>
</div>
</div>
Here is Plunker with working example: http://plnkr.co/edit/Og2U2fZSc3VECtPdnhS1?p=preview
Hope that helps someone !!
I used following approach to develop a wizard. this might be help for you.
I used states like below sample with parent property.
var home = {
name: 'home',
url: '/home',
controller: 'MainController',
templateUrl: '/html/main.html'
},
sampleWizard = {
name: 'sampleWizard',
url: '/sampleWizard',
controller: 'sampleWizardController',
templateUrl: '/html/sd/sample/sampleWizard.html'
},
sampleSectionOne = {
name: 'sampleSectionOne',
url: '/sampleSectionOne',
parent: sampleWizard,
controller: 'sampleSectionOneController',
templateUrl: '/html/sd/sample/sampleSectionOne.html'
},
sampleSectionTwo = {
name: 'sampleSectionTwo',
url: '/sampleSectionTwo',
parent: sampleWizard,
controller: 'sampleSectionTwoController',
templateUrl: '/html/sd/sample/sampleSectionTwo.html'
};
$stateProvider.state(home);
$stateProvider.state(sampleWizard);
$stateProvider.state(sampleSectionOne);
$stateProvider.state(sampleSectionTwo);
I'm not sure you want to fire the modal every single time you go to the next step.
I think all you have to do is create a modal view () then each step has a modal a templateUrl assigned to it.
each template should look like:
<div class="modal fade in" id="whatever" style="display:block">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Modal title</h4>
</div>
<div class="modal-body">
<p>One fine body…</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<a ui-sref="next_page_route_id" type="button" class="btn btn-primary">Next</a>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="modal-backdrop fade in"></div>
On the last screen you can add a data-dismiss="modal" to the submit and you are done
I have dealt with similar scenario, where I had to create a wizard (which allows you to go through steps and finally hit on summary and save).
For this, I had created different views but with one controller.
Since controller scope dies after each re-routing, I had to save the scope of controller(Basically the model object associated with the wizard) in a service object for each routing in the wizard (Captured through location.path()) and load this object back from service object on load of controller.Something like below:-
// Saving data on routing
$scope.nextPage = function () {
service.ModelForWizard = $scope.ModelForWizard;
switch ($location.path()) {
case RouteOfPage1:
//Do some stuff
break;
case RouteOfPage2:
//Do some stuff
break;
default:
}
Service or factory is persisted throughout life time of user session and is ideal to hold user data.
One more thing which was helpful , was use of 'Resolve' in the routes.
It ensures that next page in not rendered until the required data(generally lookup data) is not loaded. Code of resolve is something like this:
.when('/RouteForWizardPage1', {
templateUrl: '/templates/ViewPage1Wizard.html',
caseInsensitiveMatch: true,
controller: 'wizardController',
resolve: {
wizardLookupDataPage1: function (Service) {
return service.getwizardModelLookupDataPage1().$promise;
}
},
})
I was running into the same thing.. What worked for me was emptying the url property of the first sub state. Hence for the first sub state, your code should look as follows:
.state('setup.stepOne', {
url: '',
controller: 'SetupStepOneCtrl',
onEnter: function($stateParams, $state, $modal) {
$modal.open{
backdrop: 'static',
templateUrl: 'setup.stepOne.html',
controller: 'SetupStepOneCtrl'
})
}
})
Also, incase you're not using the url property to call the other 3 sub states, and are calling them using the state name only, you don't necessarily need to mention a url property for them.
If you want to show a wizard in a modal dialog and have a separate state for each of the wizard's steps, you need to keep in mind that the modal dialog is rendered completely outside your view hierarchy. Therefore, you cannot expect any interaction between ui-router's view rendering mechanisms and the dialog contents.
A robust solution is to put the wizard contents manipulation logic onto the parent state scope
$scope.wizard = {
scope: $scope.$new(),
show: function (template) {
// open the $modal if not open yet
// purge scope using angular.copy()
// return $scope.wizard.scope
},
// private
open: function () {
$modal.open({
scope: $scope.wizard.scope,
// ...
});
}
};
and then manually show the appropriate content from each of the sub-states and manipulate the wizard's scope as needed
$scope.wizard.show('someTemplate');
$scope.wizard.scope.user = ...;
When we faced this problem in our project, we decided after some discussion, that we didn't actually need separate ui-router states for the wizard steps. This allowed us to create a wizard directive used inside the dialog template to read wizard configuration from scope (using a format similar to ui-router state definition), provide methods to advance the wizard, and render appropriate view/controller inside the dialog.
To create multi-step wizards, you can use this module (I am the author): https://github.com/troch/angular-multi-step-form.
It allows you to create steps like views, and you can enable navigation (back / forward button, url with an URL search parameter). Examples are available here.

Conditional partials in Angular.js

Angular novice here. I'm trying to wrap my head around the right way to accomplish a basic template issue.
I have a header, which should read “click here to log in” when the user is not logged in, and “Welcome, Dudefellah” (and associated Settings links and whatnot) when a user is logged in.
I've written a Service that is able to return a JSON bundle including a login state and username, but I don't know what “The Angular Way” to express: “if(auth.loggedin), output partials/header.html; else output partials/header_login.html”.
I’m unclear as to whether this logic would belong in the controller, or some kind of “auth” model, or even in the view (that can't be right, right?). Any assistance would be greatly appreciated.
Within the controller once the login state is fetched create a scope variable headerTemplate and assign the name of the template depending on the login state
function MyCtrl($scope, loginService) {
$scope.auth = loginService.getLoginState();
$scope.headerTemplate = $scope.auth ? 'partials/header.html' : 'partials/header_login.html';
}
In in your markup
<div ng-include src="headerTemplate"></div>
There's a sample angular application called angular-app that does this really well. They have a security service, then a toolbar partial and directive that shows things depending on the state.
https://github.com/angular-app/angular-app/tree/master/client/src/common/security
from angular-app:
src/common/security/login/toolbar.tpl.html:
<ul class="nav pull-right">
<li class="divider-vertical"></li>
<li ng-show="isAuthenticated()">
{{currentUser.firstName}} {{currentUser.lastName}}
</li>
<li ng-show="isAuthenticated()" class="logout">
<form class="navbar-form">
<button class="btn logout" ng-click="logout()">Log out</button>
</form>
</li>
<li ng-hide="isAuthenticated()" class="login">
<form class="navbar-form">
<button class="btn login" ng-click="login()">Log in</button>
</form>
</li>
</ul>
src/common/security/login/toolbar.js:
angular.module('security.login.toolbar', [])
// The loginToolbar directive is a reusable widget that can show login or logout buttons
// and information the current authenticated user
.directive('loginToolbar', ['security', function(security) {
var directive = {
templateUrl: 'security/login/toolbar.tpl.html',
restrict: 'E',
replace: true,
scope: true,
link: function($scope, $element, $attrs, $controller) {
$scope.isAuthenticated = security.isAuthenticated;
$scope.login = security.showLogin;
$scope.logout = security.logout;
$scope.$watch(function() {
return security.currentUser;
}, function(currentUser) {
$scope.currentUser = currentUser;
});
}
};
return directive;
}]);
You could also use ui-router which does wonders for conditional routing and for good infrastructure in general. You'll need to define two states:
myapp.config(function($stateProvider, $urlRouterProvider){
...
// Now set up the states
$stateProvider
.state('login', {
parent: account,
url: "/login",
templateUrl: "partials/header_login.html"
})
.state('auth', {
parent: account,
url: "/authorized",
templateUrl: "partials/header.html"
})
})
when you are back from your query, change state by $state.transitionTo('login') or ('auth') and the router will load the right template for you (and also the URL). in general its much better to use a good router as the basis of your app and not give ad-hoc solutions per each case. you could also read a page (I wrote) about it here

Resources