I'm using angular $stateProvider to allow routes throughout my app. I have an index.html and I am changing the html content inside using the following...
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/app');
$stateProvider
.state('app', {
url: '/app',
templateUrl: 'templates/menu.html',
controller: 'AppCtrl'
})
.state('app.accounts', {
url: '/accounts',
templateUrl: 'templates/accounts.html'
})
});
The first route works, when you open the app template/menu.html is put inside index.html. The problem occur when I try to change the state.
At the moment, I am implementing an API in my app, what the API does is irrelevant, but on success I want the API to change the state to app.accounts, see below...
app.controller('AppCtrl', function($scope, $ionicModal, $timeout) {
$scope.create = function() {
var linkHandler = Plaid.create({
env: 'tartan',
clientName: 'Example Project',
key: 'test_key',
product: 'connect',
//On success I want to change the state
onSuccess: function(token) {
window.location = '/app/accounts';
},
});
linkHandler.open();
}
});
Above, on success, I attempt changing the state, but when window.location is called, I get the error...
net::ERR_FILE_NOT_FOUND (file:///app/accounts)
I am not sure why I get this error because the first route works fine. Any idea how I can fix this?
Your path is wrong. That's what the error is saying. It can't find that route.
Try $window.location.href = "/#/app/accounts";
Remember to inject $window in the controller.
However a better way would be to inject the $state service and use it to navigate:
$state.go('app.accounts')
Related
I have state with multiple params. Like below
$urlRouterProvider.otherwise(function ($injector, $location) {
var $state = $injector.get('$state');
return $state.go('404');
});
$stateProvider
// Home
.state('dashboard', {
url: '/:primaryId/:moduleType/:modelId',
templateUrl: "app/dashboard/dashboard.html"
}
})
// Extra
.state('404', {
url: '/404',
templateUrl: "app/page-extra/404.html"
});
When site opens default it redirects to 404.
And it also works fine with when passing all params in dashboard.
But when customer types http://localhost:3000/#!/// in browser, It gives below error instead of going in dashboard or 404 (if path is invalid).
Can anyone tell me what am i missing here?
We are developing an single page application using angular JS and I am using state provider for configuring routes. Basically there is a global navigation view and a dashboard view. I have to pass few params from navigation to make a service call and then display the dashboard accordingly.I have split the states as two, one for navigation and other for dashboard. THe thing which i am not able to figure out is that where should i make ajax call to fetch dashboard data. Should i make it in navigation itself and pass it through resolve. or should i just pass the data to dashboard controller and make ajax call from there. Below is my state
$stateProvider
.state('home', {
url: '/',
templateUrl: 'templates/home.htm',
controller: 'homeController',
})
.state('dashboard', {
url: 'contact',
templateUrl: 'templates/dashboard.htm',
controller: 'dashboardController'
})
.state('state3', {
url: '/articles',
templateUrl: 'templates/state3.htm',
controller: 'state3Controller'
});
$urlRouterProvider.otherwise('/home');
This entirely depends on how you want the user experience to play out.
If you want to do all the data fetching before transitioning to the dashboard state, use a resolve state configuration
.state('dashboard', {
url: '/contact',
templateUrl: 'templates/dashboard.htm',
controller: 'dashboardController',
resolve: {
someData: function($http) {
return $http.get('something').then(res => res.data);
}
}
}
then your controller can be injected with someData, eg
.controller('dashboardController', function($scope, someData) { ... })
This will cause the state transition to wait until the someName promise has been resolved meaning the data is available right away in the controller.
If however you want to immediately transition to the dashboard state (and maybe show a loading message, spinner, etc), you would move the data fetching to the controller
.controller('dashboardController', function($scope, $http) {
$scope.loading = true; // just an example
$http.get('something').then(res => {
$scope.loading = false;
$scope.data = res.data;
});
})
We have an ASP.NET MVC 5 application which uses ui-router AngularJS module.
When we go back to a state that has already been loaded, it always shows the old data until we do a full page refresh.
For example:
User clicks 'View Profile', we show the "ViewProfile" state, a page displaying the profile
User clicks "Edit Profile", we show the "EditProfile" state, a page with fields to edit the profile
User makes changes and clicks 'Save', they are then moved back to the "ViewProfile" state
When "ViewProfile" state loads, it still shows the old data before the edits
How can we force ui-route to pull fresh data any time it loads any state?
Angular config
var app = angular.module("MyApp", ['ngIdle', 'ui.router'])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider) {
// Configure client-side routing
$locationProvider.hashPrefix("!").html5Mode(true);
$urlRouterProvider.otherwise("/");
$stateProvider
.state('Home', {
url: '/',
views: {
"mainContainer": {
templateUrl: function (params) { return 'Home/Index'; }
}
}
})
.state('ProfileManagement', {
url: '/ProfileManagement-{action}',
views: {
"mainContainer": {
templateUrl: function (params) { return 'ProfileManagement/' + params.action; }
}
}
})
}]);
How we are doing the transition
$state.go(stateName, { action: actionName }, { reload: true, inherit: false, notify: true });
EDIT w/ Solution
Since all the functionality is written using jQuery, we cannot use Angular controllers to control the data. Our solution was to completely disable Angular template caching with the following code:
app.run(function ($rootScope, $templateCache) {
$rootScope.$on('$viewContentLoaded', function () {
$templateCache.removeAll();
});
});
By default ui-router state transition will not re-instantiate toState's controller.
To force instantiation of the controller, you can do this.
//assuming you are using $state.go to go back to previous state
$state.go('to.state', params, {
reload: true
});
You need to use resolves, like so:
var app = angular.module("MyApp", ['ngIdle', 'ui.router'])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider) {
// Configure client-side routing
$locationProvider.hashPrefix("!").html5Mode(true);
$urlRouterProvider.otherwise("/");
$stateProvider
.state('Home', {
url: '/',
views: {
"mainContainer": {
templateUrl: function (params) { return 'Home/Index'; }
}
},
resolve: {
loadedData: (injectable) => injectable.getData()
}
})
.state('ProfileManagement', {
url: '/ProfileManagement-{action}',
views: {
"mainContainer": {
templateUrl: function (params) { return 'ProfileManagement/' + params.action; }
}
},
resolve: {
loadedData: (injectable) => injectable.getData()
}
})
}]);
In each state, there is a resolve, calling the method 'getData()' in the injectable service (I'm saying a 'service' because that's where we would have the data loaded from).
Then for each state's controller, it will have access to the loadedData resolve; the data will already be loaded at that point and be usable by that controller. So for your case scenario, going back to the state you've come from, the newly updated data will be loaded again and available in that state again.
Keep your state transition the same, with the reload: true, as that is what you want as well.
Maybe this work for your problem.
In your angular main page you should have something like this:
app.run(['$rootScope', function ($rootScope){
$rootScope.$on('$stateChangeStart', function(event, toState){
$rootScope.UrlActive=toState.name;
});
}]);
In your angular controller you should have something like this:
app.controller('AnalisisCtrl', ['$scope','$rootScope', function ($scope,$rootScope) {
$scope.$watch('UrlActive', function(newValue) {
if(newValue="ProfileManagement"){
doSomething();
};
});
}]);
Working on my first angular app, and writing my first protractor test to check login functionality. Authentication is using ng-token-auth and I'm also using ui-router. I suspect the issue is that the test is not properly catching the redirect but I've tried various workarounds and cannot get anything to work.
Here's the basic routing and redirection code:
angular
.module('testAngularApp', [
'ngAnimate',
'ngCookies',
'ngResource',
'ui.router',
'ngSanitize',
'ng-token-auth'
])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $locationProvider) {
$locationProvider.html5Mode(true).hashPrefix('!');
$urlRouterProvider.otherwise('/');
$authProvider.configure({
apiUrl: 'http://backend.dev'
});
$stateProvider
.state('index', {
url: "/",
templateUrl: 'views/main.html',
controller: 'MainCtrl',
controllerAs: 'main'
})
.state('app', {
url: '/app',
abstract: true,
template: '<ui-view/>',
resolve: {
auth: function($auth, $state) {
return $auth.validateUser().catch(function() {
$state.go('index');
});
}
}
})
.state('app.dashboard', {
url: '/dashboard',
templateUrl: 'views/dashboard.html',
controller: 'DashboardCtrl',
controllerAs: 'dashboard'
});
})
.run(function($rootScope, $location, $state) {
$rootScope.$on('auth:login-success', function() {
$state.go('app.dashboard');
});
});
So on rendering the main page, you fill in login details, click the button, and use $state.go to head to the app dashboard. My protractor test is as follows:
describe('Budget', function() {
describe('Authentication', function() {
beforeEach(function() {
browser.get('http://localhost:9000');
});
it('should log in and redirect to the dashboard', function(done) {
element(by.model('loginForm.email')).sendKeys('someone#somewhere.com');
element(by.model('loginForm.password')).sendKeys('password');
element(by.id('login-button')).click();
expect($('h3.title').getText()).toEqual('Welcome');
});
});
});
Fairly straightforward. The error I get is:
Timed out waiting for Protractor to synchronize with the page after 11 seconds. Please see https://github.com/angular/protractor/blob/master/docs/faq.md. The following tasks were pending:
- $timeout: function () {
return _this.validateUser({
config: _this.getSavedConfig()
});
}
Notably as this test fails, it works just fine. In the chrome window that pops up, logging in works, the browser redirects and I see the Welcome text.
Things I've tried...
browser.ignoreSynchronization = true; in the test
browser.wait(...for element...) in the test
$location.path instead of $state.go
adding browser.waitForAngular() to the test
probably random other stack overflow suggestions I've found and forgotten
Any ideas?
Finally figured it out. For reference it seems to be an issue with ng-token-auth using a $timeout variable in some of it's code that confuses progractor. Fixed in this PR:
https://github.com/lynndylanhurley/ng-token-auth/pull/196
and verified fixed in the 0.29beta1 release.
I'm having the same issue using ui-router and ng-token-auth, but I finally got it resolved with a mix of those options; Try this:
it('should log in and redirect to the dashboard', function(done) {
element(by.model('loginForm.email')).sendKeys('someone#somewhere.com');
element(by.model('loginForm.password')).sendKeys('password');
element(by.id('login-button')).click();
browser.ignoreSynchronization = true;
browser.waitForAngular();
browser.sleep(5000);
$('h3.title').getText().then(function(text){
expect(text).toEqual('Welcome');
});
browser.ignoreSynchronization = false;
});
I think this could be because of a bug in ng-token-auth, but I'm not 100% sure.
edit: it was a bug in ng-token-auth, fixed in the latest version(v0.0.29-beta1)
I am learning angularJS and creating a web application which uses ui-router.
I have defined states as follows:
angular.module('test', [])
.config(function($stateProvider){
$stateProvider.
state('root',{
url: '/',
abstract:true,
templateUrl: '/root.html',
controller: 'MyController'
})
.state('root.route1',{
url: '/route1',
parent: 'root',
views:{
'':{
templateUrl: '/route1.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
.state('root.route2',{
url: '/route2',
parent: 'root',
views:{
'':{
templateUrl: '/route2.html'
}
'estimatedCost#':{
templateUrl: '/esitmatedCost.html'
}
}
})
});
While navigating back and forth between route1 and route2, I want to share scope variables from MyController. When I navigate to route2 from route1, it is loosing value of scope variable.
I am not sure what I am doing wrong.
Can anyone help me?
Thanks in advance.
I have yet to work with the ui-router, but I have worked with AngularJS for the last couple of years and this is how the language generally has worked in the past.
A controller's main purpose is to control the data on a single template. These controllers can communicate to each other through an AngularJS factory, often known as a service. In your case, you probably want to use a service as the controllers are getting destroyed on successful route change.
angular.module('test', [])
.factory('myFactory', function() {
var info = "Hello World";
return {
info: info
};
})
.controller('CtrlOne', function($scope, myFactory) {
$scope.info = myFactory.info;
})
.controller('CtrlTwo', function($scope, myFactory) {
$scope.info = myFacotry.info;
});
You can then use the two controllers on the two different views and they share the variables from the service that connects them.
Use $stateParams to pass parameters between two states.
Fallow the below steps :
Define your state with params object.
.state('route.route1', {
url: 'your url name',
params: {
nameOfParamObj: null
},
controller: 'your controller',
templateUrl: 'your template url',
})
From the controller where you want to send scope data use as fallows
$state.go(toState, params, options);
In toState controller catch state params using $stateParams
$stateParams.yourParamObjectName
Make sure $stateParams, $state services as dependency in your regarding controller
Have a look into the official ui-router documentation Here.