In my controller, I have $state.go('purchase');, but am having the following error :
Error: Could not resolve'purchase'from state ''.
But initially, I defined url '' to '/home' in my file states.js, which goes to state 'home'. Why is my state transition not happening? Please let me know where I am wrong.
states.js
/**
* Dashboard routes.
*/
define(['./templates'], function(templates) {
var mod = angular.module("item.states", ["ui.router"]);
console.log("Inside item home states");
var StateProvider = function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('', '/home');
$stateProvider
.state("item", {
url: '/home',
template: templates.home,
controller : 'controller.item.home.main'
})
.state("purchase", {
url: '/purchase',
template: templates.purchase,
controller : 'controller.item.home.main'
});
}
StateProvider.$inject = ["$stateProvider", "$urlRouterProvider"];
mod.config(StateProvider);
return mod;
});
controller.js
function _purchase(_event){
console.log("Clicked purchase button");
$state.go('purchase');
}
main.js
define(['./controllers','./states'], function(controllers,states) {
var mod = angular.module("item.home", ['common.services.ItemService',
'common.filters.format']);
mod.controller('controller.item.home.main', controllers.main);
console.log("Inside item home main");
return mod;
});
my server side jade
extends ../layouts/default
block content
div(ng-controller="controller.item.home.main as main" ng-init="item= #{JSON.stringify(item)}")
.masterhead.segment
.ui.page.grid.stackable
.row.ui.basic.segment
.four.wide.column.ui.center.aligned
img.ui.image.rounded(src="/images/itemicon.png")
h2() !{item.name} - !{item.activeseason.name}
.twelve.wide.column
.ui.small.orange.button(id="purchaseBtn" ng-click="purchase($event)”)
| Purchase
div.ui.page.grid(ui-view)
Thanks.
use transition to
$state.transitionTo('purchase')
and change syntax error in yor code
url: '/purchase,
to
url: '/purchase',
I'm not sure the syntax you are using and why defining your routes inside a template, but I'd recommend another way to do it, create a file on the 'module folder' or your root folder and define your routes in a more 'general' way like the following:
'use strict';
//Setting up route
angular.module('mainModule').config(['$stateProvider',
function($stateProvider) {
// main module state routing
$stateProvider.
state('home', {
url: '/home',
templateUrl: 'controller.item.home.main', //I'd recommend to use plain html files, instead something rendered in the server
controller:'HomeController'
}).
state('purchase', {
url: '/purchase',
templateUrl: 'templates.purchase',
controller: 'purchaseController'
});
}
]);
I guess in your home controller:
angular.module('mainModule').controller('HomeController', ['$scope','$state',
function($scope,$state) {
$scope.onclick = function(){
$state.go('purchase');
};
}]);
on the other side your states are not related to your server side at all.
The error was because,
In my main.js - I missed to include the module 'item.states'
define(['./controllers','./states'], function(controllers,states) {
var mod = angular.module("item.home", ['common.services.ItemService',
'common.filters.format','item.states']);
mod.controller('controller.item.home.main', controllers.main);
console.log("Inside item home main");
return mod;
});
Related
I'm new to Angular 1 and have to implement a new feature on an existing webapp. The app uses jhipster to generate some parts of the backend and frontend (Angular 1 and uirouter).
So I tried to use my own route and state like this which is mostly copy and pasted from existing components of the webapp:
(function() {
'use strict';
angular
.module('artemisApp')
.config(stateConfig);
stateConfig.$inject = ['$stateProvider'];
function stateConfig($stateProvider) {
$stateProvider
.state('model-comparison-exercise-for-course', {
parent: 'entity',
url: '/course/{courseid}/model-comparison-exercise',
data: {
authorities: ['ROLE_ADMIN', 'ROLE_TA'],
pageTitle: 'artemisApp.modelComparisonExercise.home.title'
},
views: {
'content#': {
templateUrl: 'app/entities/model-comparison-exercise/model-comparison-exercise.html',
controller: 'ModelComparisonExerciseController',
controllerAs: 'vm'
}
},
resolve: {
translatePartialLoader: ['$translate', '$translatePartialLoader', function ($translate, $translatePartialLoader) {
$translatePartialLoader.addPart('modelComparisonExercise');
$translatePartialLoader.addPart('exercise');
$translatePartialLoader.addPart('global');
return $translate.refresh();
}],
courseEntity: ['$stateParams', 'Course', function ($stateParams, Course) {
return Course.get({id: $stateParams.courseid}).$promise;
}]
}
});
}
})();
Then I try to open this route with the following code:
<a ui-sref="model-comparison-exercise-for-course({courseid:course.id})"
data-translate="artemisApp.course.modelComparisonExercises"></a>
By clicking on that link a http get request is fired which returns a http status code 404: http://localhost:8080/app/entities/model-comparison-exercise/model-comparison-exercise.html
Actually, the url that should be opened is http://localhost:8080/#/course/1/model-comparison-exercise
Any idea what I could have configured wrong?
Please try changing 'content#' to 'content#artemisApp'.
As explained here:
The symbol before the # is the name of the view you want to match, and the symbol after the # is a reference to the state in which the template the ui-view directive should exist in.
And the <a> tag is not being closed:
<a ui-sref="model-comparison-exercise-for-course({courseid:course.id})"
data-translate="artemisApp.course.modelComparisonExercises"></a>
Searching through the code, I found that model-comparison-exercise.html does not exist in folder model-comparison-exercise. Besides model-comparison-exercises.html exist.
I've been trying to make ocLazyLoad work on my project, but I keep getting this Angular error
Error: $controller:ctrlreg
A controller with this name is not registered
The controller with the name 'eventoCtrl' is not registered.
NOTICE: I'm also using ui-router to define my app's states.
NOTICE #2: Any suggestion on other methods of using routes or lazy loads will also be apreciated
app.js
(function(){
angular
.module('kulchr', [
'ui.router',
'oc.lazyLoad'
]);})();
config.js
angular
.module('kulchr')
.config(function ($stateProvider, $urlRouterProvider, $ocLazyLoadProvider) {
$stateProvider
.state('eventos', {
url: '/eventos',
views: {
'top-menu-nav': {
templateUrl: 'templates/top-menu-nav.html',
},
'main': {
templateUrl: 'templates/eventos.html',
controller: 'eventoCtrl as listaEvento',
resolve: {
eventoCtrl: function ($ocLazyLoad) {
return $ocLazyLoad.load(
{
files: ['controller/listaEventoController.js'
,'service/eventoService.js']
});
}
}
}
}
});
$urlRouterProvider.otherwise('/');
});
controller
(function() {
'use strict';
angular
.module('kulchr')
.controller('eventoCtrl', ListaEventoController);
ListaEventoController.$inject = ['servicoEvento'];
function ListaEventoController(evento){
var vm = this;
var promise = evento.buscaDados();
promise.then (function(response){
vm.eventos = response.data;
})
}
})();
service
(function() {
'use strict';
angular
.module('kulchr')
.service('servicoEvento', Evento);
function Evento($http, $q) {
var d = $q.defer();
var self = this;
$http.get('/mockup-data/eventos.json')
.then(function (response){
d.resolve(response);
}, function(reason) {
console.log("Motivo: " + reason.data +
"Status: " + reason.status +
" - " + reason.statusText);
return $q.reject(reason);
});
self.buscaDados = function(){
return d.promise;
}
}
})();
What am I missing here? I've reached the ui-router documentation but it just made more confused
BTW, Everything is working fine when adding the files directly on the index.html file using .
Currently what happening is, your listaEventoController is haven't goad loaded when named view is rendering. The reason being is resolve object has been used in wrong place. It doesn't work on named view level. It should taken out and keep it after url(flatten property) inside state definition object.
By taking resolve out oc-lazyLoad module will take care of downloading listaEventoController & eventoService file from server and would make download service register inside angular context and available to use inside angular application.
Code
$stateProvider
.state('eventos', {
url: '/eventos',
views: {
'top-menu-nav': {
templateUrl: 'templates/top-menu-nav.html',
},
'main': {
templateUrl: 'templates/eventos.html',
controller: 'eventoCtrl as listaEvento'
}
},
//moved resolve out from "named view" object.
resolve: {
eventoCtrl: function ($ocLazyLoad) {
return $ocLazyLoad.load({
files: [
'controller/listaEventoController.js',
'service/eventoService.js'
]
}
);
}
}
})
I wan't to be able to change pages based on url. My url's look like this http://todolist.com/#/1 where last number is page number. So far my pagination is working (angular ui bootstrap). If i try to change page with numbers or buttons in pagination row the pages will change based on response. But url are not changing in url bar and if i change url manually the pages won't change.
This is my controller
controllers.todoCtrl = function ($scope, $timeout, todoFactory, $location, $routeParams) {
if($routeParams.pageNumber == undefined || $routeParams.pageNumber == null){
$scope.currentPage = 1;
} else {
$scope.currentPage = $routeParams.pageNumber;
}
getData();
//get another portions of data on page changed
$scope.pageChanged = function () {
getData();
};
/**
* Get list of todos with pagination
*/
function getData() {
todoFactory.index($scope.currentPage).then(function (data) {
$scope.totalItems = data.paging.count;
$scope.itemsPerPage = data.paging.limit;
$scope.todos = data.Todos;
});
}
My routes
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'app/templates/todolists.html',
controller: 'todoCtrl'
}).when('/:pageNumber', {
templateUrl: 'app/templates/todolists.html',
controller: 'todoCtrl'
}).otherwise({ redirectTo: '/'});
What do i have to do to make pagination based on url working. If you need any additional information, please let me know and i will provide. Thank you
you can use the updateParams function of $route to update the url.
So your code would look like this:
//get another portions of data on page changed
$scope.pageChanged = function () {
//getData();
$route.updateParams({pageNumber: $scope.currentPage});
};
This will cause the url to change. However keep in mind that this will destroy and recreate your controller.
Personally I avoid using the build in Angular router and prefer to use UI-Router instead. UI-Router uses a state base approach with a nice clean interface
So in order to use UI-Router you have to grab it from here or install it with your favorite package manager.
Your routes would be configured like this:
var app = angular.module('myApp', ['ui.router']);
app.config(function($stateProvider) {
$stateProvider.state('home', {
url: '/',
templateUrl: 'app/templates/todolists.html',
controller: 'todoCtrl'
})
.state("details", {
url: '/:pageNumber',
templateUrl: 'app/templates/todolists.html',
controller: 'todoCtrl'
});
});
As you can see in the above sample, states get names with UI-Router. You can use those names later in your controllers and templates to reference the states. In addition to that you can have nested states.
Example in your controller:
controllers.todoCtrl = function ($scope, $timeout, todoFactory, $location, $state, $stateParams) {
if(!$stateParams.pageNumber){
$scope.currentPage = 1;
} else {
$scope.currentPage = $stateParams.pageNumber;
}
getData();
//get another portions of data on page changed
$scope.pageChanged = function () {
$state.go("details", {pageNumber: $scope.currentPage });
};
/**
* Get list of todos with pagination
*/
function getData() {
todoFactory.index($scope.currentPage).then(function (data) {
$scope.totalItems = data.paging.count;
$scope.itemsPerPage = data.paging.limit;
$scope.todos = data.Todos;
});
}
The use case is to change login button to text "logged in as xxx" after authentication.
I have devided my page to 3 views: header, content, footer. The login button is in the header view. When I click login, it transits to "app.login" state, and the content view changes to allow user input username and password.
Here's the routing code:
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/',
views: {
'header': {
templateUrl: 'static/templates/header.html',
controller: 'AppController'
},
'content': {
templateUrl: 'static/templates/home.html',
controller: 'HomeController'
},
'footer': {
templateUrl: 'static/templates/footer.html',
}
}
})
.state('app.login', {
url: 'login',
views: {
'content#': {
templateUrl : 'static/templates/login.html',
controller : 'LoginController'
}
}
})
The html template has code like this:
<li><span ng-if='loggedIn' class="navbar-text">
Signed in as {{currentUser.username}}</span>
</li>
LoginController set a $scope.loggedIn flag to true once authentication succeeded, but how can I populate that flag to the header view?
As I understand it I can't just use $scope.loggedIn in the html template as above because the $scope is different in two controllers. I know if LoginController is a child of AppController, then I can call $scope.$emit in LoginController with an event and call $scope.$on in AppController to capture it. But in this case the two controllers are for different views, how can I make them parent-child?
I know I can use $rootScope but as I'm told polluting $rootScope is the last resort so I'm trying to find a best practise. This must be a very common use cases so I must be missing something obvious.
You can use a factory to handle authentication:
app.factory( 'AuthService', function() {
var currentUser;
return {
login: function() {
// logic
},
logout: function() {
// logic
},
isLoggedIn: function() {
// logic
},
currentUser: function() {
return currentUser;
}
};
});
Than can inject the AuthService in your controllers.
The following code watches for changes in a value from the service (by calling the function specified) and then syncs the changed values:
app.controller( 'AppController', function( $scope, AuthService ) {
$scope.$watch( AuthService.isLoggedIn, function ( isLoggedIn ) {
$scope.isLoggedIn = isLoggedIn;
$scope.currentUser = AuthService.currentUser();
});
});
In such cases I typically opt to use a service to coordinate things. Service's are instantiated using new and then cached, so you effectively get a singleton. You can then put in a simple sub/pub pattern and you're good to go. A basic skeleton is as follows
angular.module('some-module').service('myCoordinationService', function() {
var callbacks = [];
this.register = function(cb) {
callbacks.push(cb);
};
this.send(message) {
callbacks.forEach(function(cb) {
cb(message);
});
};
}).controller('controller1', ['myCoordinationService', function(myCoordinationService) {
myCoordinationService.register(function(message) {
console.log('I was called with ' + message);
});
}).controller('controller2', ['myCoordinationService', function(myCoordinationService) {
myCoordinationService.send(123);
});
Do you use any serivce to keep logged user data? Basically serivces are singletons so they are good for solving that kind of problem without polluting $rootScope.
app.controller('LoginController', ['authService', '$scope', function (authService, $scope) {
$scope.login = function(username, password) {
//Some validation
authService.login(username, password);
}
}]);
app.controller('HeaderController', ['authService', '$scope', function (authService, $scope) {
$scope.authService = authService;
}]);
In your header html file:
<span ng-if="authService.isAuthenticated()">
{{ authService.getCurrentUser().userName }}
</span>
I am trying to create a service to use throughout my Angular app that pulls in data from a .json file using $http. This is what the factory looks like:
var trooNewsServices = angular.module('trooNewsServices', []);
trooNewsServices.factory('Articles', ['$http',
function($http){
$http.get('resources/articles.json').success(function(data) {
return data;
});
}]);
I passed in the trooNewsServices dependency into my module declaration. Any controller that I try to pass in my new Articles service, I get a
"Could not instantiate controller HomeController"
error in the console. Not sure what I am missing/what is wrong with this code. Should I be using $resource instead of $http?
Here is how I am passing the 'trooNewsServices' into my main module:
var TrooNews = angular
.module('TrooNews', ['ngMaterial', 'ngNewRouter', 'trooNewsServices'])
.config(function($mdThemingProvider) {
$mdThemingProvider
.theme('default')
.primaryPalette('indigo')
.accentPalette('pink');
})
.config(function($locationProvider) {
$locationProvider.html5Mode({
enabled: false,
requireBase: false
});
});
Here is how I try to inject 'Articles' into one of my controllers:
TrooNews.controller('HomeController', ['Articles',
function(Articles) {
this.name = 'Troo News';
this.articles = Articles.query();
}]);
And here is how I set up routing in my 'AppController':
TrooNews.controller('AppController', function($router, $mdSidenav, $mdToast, $parse, $http) {
$router.config([{
path: '/',
component: 'home'
}, {
path: '/home',
component: 'home'
}, {
path: '/about',
component: 'about'
}, {
path: '/settings',
component: 'settings'
}, {
path: '/article/:id',
component: 'article'
}]);
this.toggleSidenav = function(menuId) {
$mdSidenav(menuId).toggle();
};
this.navigateTo = function(link) {
var parts = link.match(/^(.+?)(?:\((.*)\))?$/);
var url;
if (parts[2]) {
url = '.' + $router.generate(parts[1], $parse(parts[2])());
} else {
url = '.' + $router.generate(parts[1]);
}
$mdToast.show($mdToast.simple().content('Navigate To: ' + url).position('bottom right'));
$router.navigate(url);
this.toggleSidenav('left');
};
});
Inside your HomeController, you are executing this.articles = Articles.query();, but your Articles service doesn't define any query function.
Instead, your service is just immediately executing an HTTP GET request upon creation. Not sure why this would lead to your error, but it is a red flag.
Try changing your Articles service to the following:
trooNewsServices.factory('Articles', ['$http',
function Articles($http){
this.query = function() {
return $http.get('resources/articles.json')
.then(function(response) { return response.data; });
};
}]);
I was experiencing the same error message under different conditions. In my case, it was because I was referencing $scope in my dependencies (old habit that I'm trying to break). In my case, I wasn't using $scope and could easily remove the reference. That cleared up my error. Check your code for $scope references and see if that fixes it.
https://github.com/angular/router/issues/313
and
How can we watch expressions inside a controller in angular 1.4 using angular-new-router