My code:
/**
* Created by maki1234 on 14.03.16.
*/
angular.module('admin', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/all.html',
controller: 'mainController'
})
.when('/details/:id', {
templateUrl: 'views/details.html',
controller: 'detailsController',
resolve: {
id: function ($route, $q) {
var q = $q.defer();
var id = parseInt($route.current.params.id, 10);
if (!isNaN(id)) {
q.resolve(id);
} else {
q.reject('Param Id must be a number!');
}
return q.promise;
}
}
})
.otherwise({
redirectTo: '/'
});
})
.controller('mainController', function () {
})
.controller('detailsController', function ($scope, $location, id, $log) {
$scope.$on('$routeChangeError', function (event, current, previous, rejection) {
$scope.error = rejection;
});
$log.info($scope.error);
});
My problem is: This code works but when ID is numeric, but when it's a string controller doesn't log anything in console. Any suggestion? (when id is numeric in console i see undefined - it's good i think)
Updated:
app.js
angular.module('admin', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'views/all.html',
})
.when('/details/:id', {
templateUrl: 'views/details.html',
controller: 'detailsController',
resolve: {
'id': function ($route, $q) {
var q = $q.defer();
var id = parseInt($route.current.params.id, 10);
if (!isNaN(id)) {
q.resolve(id);
} else {
q.reject('Param id must be a number!');
}
return q.promise;
}
}
})
.otherwise({
redirectTo: '/'
});
})
.controller('mainController', function ($rootScope, $location) {
$rootScope.$on('$routeChangeError', function (event, current, previous, rejection) {
$rootScope.error = rejection;
});
})
.controller('detailsController', function ($scope, id) {
$scope.id = id;
});
index.html
<div class="container-fluid">
<div class="row">
<div class="col xs-10 col-xs-offset-1" ng-controller="mainController" ng-view>
</div>
</div>
</div>
details.html
<h3>{{id}}</h3>
<h3>{{error}}</h3>
The problem is when I have valid url id is printed on the screen, but when I change id to bad value on the screen I see printed id and the error (it seems that $scope.id is saved and it's all time in memory). Second problem is when I have for example bad url I see error on the screen but when I press F5 and the page "reload" route doesn't change so i see white screen without any error (because route is the same and routeChangeError event isn't fired...).
Related
I've looked at similar questions but I can't seem to understand what I am missing. Basically, I have a service that gets data from the server, and I am trying to get that data into a controller through UI-Router's resolve property. However, after following numerous tutorials and documentations, I can't get the controller to find the data, so to speak. Everything comes up as undefined. I am hoping someone can help me understand what is happening. My code is below.
services.js
myServices.factory('SoundCloudService', ['$http', '$log', '$sce', function($http, $log, $sce) {
function getPlayerHtml() {
return $http.get('/get-site-data').then(function(oEmbed) {
return $sce.trustAsHtml(oEmbed.data.player);
});
};
function getSiteAbout() {
return $http.get('/get-site-data').then(function(oEmbed) {
return $sce.trustAsHtml(oEmbed.data.about);
});
}
function getAllTracks() {
return $http.get('/get-all-tracks').then(function(tracks) {
return JSON.parse(tracks.data);
});
};
function getAllPlaylists() {
return $http.get('/get-playlists').then(function(playlists) {
return JSON.parse(playlists.data);
})
};
function getPlaylist(pid) {
return $http.post('/get-playlist', pid, $http.defaults.headers.post).then(function(playlist) {
return playlist.data;
});
};
function getXMostTrendingFrom(x, playlist) {
var i, trending = [];
playlist.sort(function(a, b) { return b.playback_count - a.playback_count} );
for(i=0;i<x;i++) {
trending.push(all_tracks[i]);
}
return trending;
};
return {
getAllTracks: getAllTracks,
getAllPlaylists: getAllPlaylists,
getPlayerHtml: getPlayerHtml,
getSiteAbout: getSiteAbout,
getXMostTrendingFrom: getXMostTrendingFrom,
getPlaylist: getPlaylist,
};
}]);
app.js
myApp.config(['$stateProvider', '$urlRouterProvider', 'ngMetaProvider',
function($stateProvider, $urlRouterProvider, ngMetaProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('main', {
url: '',
template: '<ui-view/>',
abstract:true,
controller: 'MainController',
resolve: {
player: function(SoundCloudService) { return SoundCloudService.getPlayerHtml(); },
about: function(SoundCloudService) { return SoundCloudService.getSiteAbout(); },
}
})
.state('main.home', {
url: '/',
templateUrl: '../static/partials/home.html',
controller: 'IndexController',
})
.state('main.team', {
url: '/team',
templateUrl: '../static/partials/team.html',
controller: 'TeamController',
})
.state('main.contact', {
url: '/contact',
templateUrl: '../static/partials/contact.html',
controller: 'ContactController',
})
.state('main.resources', {
url: '/resources',
templateUrl: '../static/partials/resources.html',
controller: 'ResourcesController',
})
.state('main.listen-to', {
url: '/listen-to',
templateUrl: '../static/partials/listen-to.html',
controller: 'ListenController',
})
.state('main.listen-to.season', {
url: '/listen-to/:season',
templateUrl: '../static/partials/listen-to.season.html',
controller: 'ListenController',
})
.state('main.listen-to.season.episode', {
url: '/listen-to/:season/:episode',
templateUrl: '../static/partials/listen-to.season.episode.html',
controller: 'ListenController',
})
.state('main.read', {
url: '/read',
templateUrl: '../static/partials/read.html',
controller: 'ReadController',
})
.state('main.read.post', {
url: '/read/:post',
templateUrl: '../static/partials/read.post.html',
controller: 'ReadController',
})
}
]);
controller.js
myControllers.controller('MainController', ['$scope', '$log', 'PageTitleService',
function($scope, $log, PageTitleService, player) {
$log.log(player); /* This is always undefined */
}
]);
[UPDATE]
As pointed out by Hadi in the answer below, I placed player in the array, and the controller now looks like this:
skodenControllers.controller('MainController', ['$scope', '$log', '$sce', 'PageTitleService', 'player',
function($scope, $log, $sce, PageTitleService, player) {
$log.log(player);
}
]);
The console DOES show the data, but only after an error as such:
Error: [$injector:unpr]
http://errors.angularjs.org/1.3.2/$injector/unpr?p0=playerProvider%20%3C-%20player
at angular.js:38
at angular.js:3930
at Object.d [as get] (angular.js:4077)
at angular.js:3935
at d (angular.js:4077)
at Object.e [as invoke] (angular.js:4109)
at F.instance (angular.js:8356)
at angular.js:7608
at r (angular.js:347)
at I (angular.js:7607)
Hopefully someone can lead me in the right direction.
You forgot pass player into array. change to this
myControllers.controller('MainController', ['$scope', '$log',
'PageTitleService','player',
function($scope, $log, PageTitleService, player) {
$log.log(player); /* This is always undefined */
}
]);
As myServices and myControllers are both modules, ensure you add them as dependencies of myApp module.
// init myApp module
angular.module('myApp', ['myServices', 'myControllers']);
Edit
Some leads :
According to the documentation, when using ui-router nested views, child views (state name = main.xxx) must declare the parent state, so you must add parent: "main" or child views won't inherit resolved properties of main state controller
As siteDate is loaded asynchronously in SoundCloudService (services.js:23), you cannot be sure it will be available in your controllers which are loaded at the same time.
Instead, add a getSiteDate() method to SoundCloudService which returns a promise. siteData is then cached and immediately return by the promise.
For example :
/**
* #name getSiteData
* #description Scrap site data
* #returns {promise} a promise
*/
function getSiteData() {
var deferred = $q.defer();
if(siteData) {
deferred.resolve(siteData);
}
else {
$http.get('/get-site-data').then(function(response) {
siteData = response.data;
deferred.resolve(siteData);
}, function(err) {
deferred.reject(err.message);
});
}
return deferred.promise;
}
Why trying to map SoundCloudService to siteData ? You should simply inject SoundCloudService in controllers that use it :
For example :
skodenControllers.controller('MainController', ['$scope', '$log', '$sce', 'PageTitleService', 'SoundCloudService',
function($scope, $log, $sce, PageTitleService, SoundCloudService) {
// Note: getSiteData() could use a cache inside the service
SoundCloudService.getSiteData().then(function(siteData) {
...
});
}
Right now i am making an AngularJS+UI router install application. But i have a problem, the problem is, that i want to disable access to the views, associated with the install application. I want to do it in resolve in the state config.
But the problem is i need to get the data from a RESTful API, whether the application is installed or not. I tried making the function, but it loaded the state before the $http.get request was finished.
Here was my code for the resolve function:
(function() {
var app = angular.module('states', []);
app.run(['$rootScope', '$http', function($rootScope, $http) {
$rootScope.$on('$stateChangeStart', function() {
$http.get('/api/v1/getSetupStatus').success(function(res) {
$rootScope.setupdb = res.db_setup;
$rootScope.setupuser = res.user_setup;
});
});
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/404");
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($q, $state, $timeout, $rootScope) {
var setupStatus = $rootScope.setupdb;
var deferred = $q.defer();
$timeout(function() {
if (setupStatus === true) {
$state.go('setup-done');
deferred.reject();
} else {
deferred.resolve();
}
});
return deferred.promise;
}
}
})
.state('user-registration', {
url: "/install/user-registration",
templateUrl: "admin/js/partials/user-registration.html",
controller: "RegisterController"
})
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html"
})
.state('404', {
url: "/404",
templateUrl: "admin/js/partials/404.html"
});
}]);
})();
EDIT:
Here is what my ajax call returns:
Try this way:
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
setupStatus: function($q, $state, $http) {
return $http.get('/api/v1/getSetupStatus').then(function(res) {
if (res.db_setup === true) {
$state.go('setup-done');
return $q.reject();
}
return res;
});
}
}
})
Then inject setupStatus in controller:
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html",
controller: ['$scope', 'setupStatus', function ($scope, setupStatus) {
$scope.setupdb = setupStatus.db_setup;
$scope.setupuser = setupStatus.user_setup;
}]
})
I'm brand new to Angularjs and am trying to set up a new site but I'm confused as to the set up. I have a module and am using $route to successfully navigate but I'm lost as to what to do with my nav. When I load the module I want to read my database for a list of links that the user is allowed to access then spit them out in the nav. I don't want to repeat this in every view because I don't need to. So I'm trying to figure out how to run the ajax call once and then keep changing the view (I'd also like to add a class .selected to whatever view they're on). How would I go about doing that, with a directive?
(function () {
var app = angular.module('manage', ['ngRoute', 'manageControllers']);
/*
I've tried this but obviously $http isn't injected. Can I even do that?
var thisApp = this;
$http.get('/test/angular/php/angular.php', {params: {'function': 'nav'}}).then(function successCallback(response) {
});
*/
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'templates/dash.html',
controller: 'DashCtrl'
}).
when('/inventory/', {
templateUrl: 'templates/inventory.html',
controller: 'InventoryCtrl'
}).
when('/inventory/:mvKey', {
templateUrl: 'templates/inventory.html',
controller: 'InventoryCtrl'
}).
when('/inventory/:mvKey/:tab', {
templateUrl: 'templates/inventory.html',
controller: 'InventoryCtrl'
}).
/* etc...*/
}
]);
})();
EDIT:
My attempt at getting the nav to run once
controllers.js
var manageControllers = angular.module('manageControllers', []);
var thisApp = this;
nav = null;
navSelected = '/';
manageControllers.controller('NavCtrl', ['$scope', '$http', function($scope, $http) {
if (thisApp.nav === null) {
$http.get('php/angular.php', {params: {'function': 'nav'}}).then(function successCallback(response) {
console.log(response.data);
thisApp.nav = response.data;
$scope.nav = thisApp.nav;
$scope.select = thisApp.navSelected;
});
} else {
$scope.nav = thisApp.nav;
$scope.select = thisApp.navSelected;
}
}]);
manageControllers.controller('DashCtrl', ['$scope', function($scope) {
thisApp.navSelected = '/';
}]);
I would swith to UI Router (https://github.com/angular-ui/ui-router) instead of $route. It allows you being much more flexible with your routing.
A Small example:
app.config(['$stateProvider',
function($stateProvider) {
$stateProvider.
state('/', {
url: '/',
views: {
'': {
templateUrl: 'templates/dash.html',
controller: 'DashCtrl'
},
'nav#': {
templateUrl: 'path/to/nav.html',
controller: 'NavCtrl'
},
}
}).
state('/inventory/', {
url: '/',
views: {
'': {
templateUrl: 'templates/dash.html',
controller: 'DashCtrl'
},
'nav#': {
templateUrl: 'path/to/nav.html',
controller: 'NavCtrl'
},
}
}).
// ...
and in your index.html
<div ui-view="nav"></div>
<div ui-view ></div>
Take a closer look at UI Router's doc, there's much more you can do with it!
I want to make my views show only after the initial data is fetched and i am trying to accomplish this with a route resolve, but i can't get it to work. What am i doing wrong? Also my angular skills are a bit shabby so i aplogize in advance if my question is dumb.
Application.js :
var Application = angular.module('ReporterApplication', ['ngRoute']);
Application.config(['$routeProvider', '$interpolateProvider',
function($routeProvider, $interpolateProvider) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
$routeProvider
.when('/packing/scan.html', {
templateUrl: 'packing/scan.html',
controller: 'PackingScanController',
resolve: {
initData : Application.PackingScanInit()
}
})
.when('/packing/stats.html', {
templateUrl: 'packing/stats.html',
controller: 'PackingStatisticsController'
})
etc
and here is my Scan.js file :
Application.PackingScanInit = function ($q,$timeout,$http) {
var serverData = "";
$http.get('/packing/scan.action')
.success(function(data){
serverData = data;
})
.error(function(data){
serverData = data;
});
return serverData;
}
Application.controller('PackingScanController', ['initData', '$scope', '$http', function(initData, $scope, $http) {
var packer = this;
// Message log model
packer.messageLog = [{
status : "",
message : null
}];
the files are included in this order.
service are singletons means there are initialized only one but time but if you simply return from service it will be called one time but if you return a function from service it will be called again and again .See Below Sample for working.
var app = angular.module('ajay.singhApp', [])
.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/view1', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
resolve: {
myVar: function (repoService) {
return repoService.getItems().then(function (response) {
return response.data;
});
}
}
})
.when('/view2', {
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.otherwise({
redirectTo: '/view1'
});
}]);
app.factory('repoService', function ($http) {
return {
getItems: function () {
return $http.get('TextFile.txt');
}
};
});
Try this:
Application.PackingScanInit = function ($q,$timeout,$http) {
return $http.get('/packing/scan.action')
.success(function(data){
return data;
})
.error(function(data){
return data;
});
}
Also you have to adjust your resolve to this:
resolve: {
initData : Application.PackingScanInit
}
Here is a specific working example:
(function() {
angular.module('app',['ngRoute']);
function TestCtrl($scope, initData) {
$scope.value = initData;
}
angular.module('app').config(function($routeProvider) {
$routeProvider.otherwise({
template: '`<p>Dude {{value}}</p>`',
controller: TestCtrl,
resolve: {
initData: function($http) {
return $http.get('test.json') //change to test-e.json to test error case
.then(function(resp) {
return resp.data.value; //success response
}, function() {
return 'Not Found'; // error response
});
}
}
});
});
})();
http://plnkr.co/edit/SPR3jLshcpafrALr4qZN?p=preview
I want to redirect after login to /dashboard
$scope.submit = function (user) {
if ($scope.loginForm.$valid) {
UserService.login(user).then(
function (result) {
$location.path('/dashboard');
}, function (reason) {
$scope.msg = "username or password is not correct";
});
$scope.reset();
}
};
in app.js I want to create my menu dynamically and show dashboard.html
$routeProvider
.when('/dashboard', {
templateUrl: 'views/dashboard.html',
abstract:true,
resolve: {
menu: function (MenuService) {
return MenuService.getMenu();
}
},
controller: function ($scope, menu) {
$scope.menu = menu;
$scope.oneAtATime = true;
}
})
and in dashboard I use ng-view to load my template
<div class="content">
<div class="container">
<ng-view></ng-view>
</div>
but it catch "Maximum call stack size exceeded" error.
THE MenuService servie return promise
factory('MenuService', function ($q, $http) {
var getMenu = function () {
var deferred = $q.defer();
$http.post('/menu', 1).
success(function (data) {
deferred.resolve(data);
}).
error(function (data, status) {
deferred.reject(status);
});
return deferred.promise;
};
return{
getMenu: getMenu
}
})
I changed the MenuService.getMenu() to this:
$routeProvider
.when('/dashboard', {
templateUrl: 'views/dashboard.html',
abstract:true,
resolve: {
menu: function (MenuService) {
MenuService.getMenu().then(function(result){
return result;
});
}
},
controller: function ($scope, menu) {
$scope.menu = menu;
$scope.oneAtATime = true;
}
})
and now dashboard is loaded but without menu. then method is called after dashboard.html loading!!!!
when I remove ng-view every thing work perfectly.
What is my problem?
thank you
Basically you need to do change in your custom resolve service code. Currently your controller promise has receiving chain promise, so you should return a promise instead of return chain promise.
Code
$routeProvider
.when('/dashboard', {
templateUrl: 'views/dashboard.html',
abstract:true,
resolve: {
menu: function (MenuService) {
return MenuService.getMenu();
}
},
controller: function ($scope, menu) {
menu.then(function(data){
$scope.menu = data;
});
$scope.oneAtATime = true;
}
})