ionic : Master Detail returns only id - javascript

I'm running out of idea right now. Everything seems fine, but when Im trying to inherit master data into the details view nothing really shows when I consoled except for the id.
Console Output : Object {id: "78"}
Here's my code :
Config
.state('app.home', {
url: "/home",
views: {
'menuContent': {
templateUrl: "templates/home.html",
controller: 'PostHomeCtrl'
}
}})
.state('app.posthome', {
url: "/home/:id",
views: {
'menuContent': {
templateUrl: 'templates/post.html',
controller: 'PostDetailCtrl'
}
}})
Factory
.factory('Posts', function($http){
var blogs = []; //Private Variable
return {
GetBlog: function(){
return $http.get('path/to/resources').then(function(response){
blogs = response;
return response;
});
},
GetPost: function(postId){
for(i=0;i<blogs.length;i++){
if(blogs[i].id == postId){
return blogs[i];
}
}
return null;
}
}})
Controller
.controller('PostHomeCtrl', function(Posts, $scope){
Posts.GetBlog().then(function(blogs){
$scope.blogs = blogs.data;
console.log(blogs.data);
});
})
.controller('PostDetailCtrl', function(Posts, $stateParams, $scope){
var postId = $stateParams;
$scope.blog = Posts.GetPost(postId);
console.log(postId);
});

nvm just figured it out, turns out my I forgot to add .data at my blog = response . So it becomes blog=response.data instead of blog=response. Thanks

Related

Data for listing page and detail page without 2 API calls

I have set up a service to return a listing of clients from my API. Using UI-router, I can successfully pass a client's id to the details state - however, it seems unnecessary here to make another API call to retrieve a single client when I have all the necessary data in my controller.
What is the best way to use the ID in my detail state URL to show data for that client? Also - if a user browses directly to a client detail URL - I'll need to then make a call to the API to get just that client data - or is there a better way?
EDIT: I am not looking to load the two views on the same 'page', but completely switch views here, from a listing page to a detail page.
Routes in App.js
$stateProvider
.state('root', {
abstract: true,
url: '',
views: {
'#': {
templateUrl: '../partials/icp_index.html',
controller: 'AppController as AppCtrl'
},
'left-nav#root': {
templateUrl: '../partials/left-nav.html'
},
'right-nav#root': {
templateUrl: '../partials/right-nav.html'
},
'top-toolbar#root': {
templateUrl: '../partials/toolbar.html'
}
/*'footer': {
templateUrl: '../partials/agency-dashboard.html',
controller: 'AppController as AppCtrl'
}*/
}
})
.state('root.clients', {
url: '/clients',
views: {
'content#root': {
templateUrl: '../partials/clients-index.html',
controller: 'ClientsController as ClientsCtrl'
}
}
})
.state('root.clients.detail', {
url: '/:clientId',
views: {
'content#root': {
templateUrl: '../partials/client-dashboard.html',
//controller: 'ClientsController as ClientsCtrl'
}
}
})
// ...other routes
Service, also in app.js
.service('ClientsService', function($http, $q) {
this.index = function() {
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients')
.then(function successCallback(response) {
console.log(response.data);
deferred.resolve(response.data);
},
function errorCallback(response) {
// will handle error here
});
return deferred.promise;
}
})
And my controller code in ClientsController.js
.controller('ClientsController', function(ClientsService) {
var vm = this;
ClientsService.index().then(function(clients) {
vm.clients = clients.data;
});
});
And finally, my listing page clients-index.html
<md-list-item ng-repeat="client in ClientsCtrl.clients" ui-sref="clients-detail({clientId : client.id })">
<div class="list-item-with-md-menu" layout-gt-xs="row">
<div flex="100" flex-gt-xs="66">
<p ng-bind="client.name"></p>
</div>
<div hide-xs flex="100" flex-gt-xs="33">
<p ng-bind="client.account_manager"></p>
</div>
</div>
</md-list-item>
You can use inherited states like suggested here.
$stateProvider
// States
.state("main", {
controller:'mainController',
url:"/main",
templateUrl: "main_init.html"
})
.state("main.details", {
controller:'detailController',
parent: 'main',
url:"/:id",
templateUrl: 'form_details.html'
})
Your service does not change.
Your controllers check if the Model has been retrieved:
app.controller('mainController', function ($scope, ClientsService) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
});
})
app.controller('detailController', function ($q, $scope, ClientsService, $stateParams) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
$scope.Item = data[$stateParams.id];
});
})
See
http://plnkr.co/edit/I4YMopuTat3ggiqCoWbN?p=preview
[UPDATE]
You can also, if you must, combine both controllers:
app.controller('mainController', function ($q, $scope, ClientsService, $stateParams) {
var promise = $scope.Model ? $q.when($scope.Model) : ClientsService.index();
promise.then(function(data){
$scope.Model = data;
$scope.Item = data[$stateParams.id];
});
})
I would change the service to cache the data. With $q.when() you can return a promise from a variable. So you save your response in a variable, and before doing the API call you check if the cache has been set. If there is any cache, you return the data itself. Otherwise, you do the usual promise call.
.service('ClientsService', function($http, $q) {
var clients = null;
this.getClient = function(id) {
if (clients !== null) {
return $q.when(id ? clients[id] : clients);
}
var deferred = $q.defer();
$http.get('http://api.icp.sic.com/clients').then(function(response) {
clients = response.data;
deferred.resolve(id ? clients[id] : clients);
}, function (response) {
// will handle error here
});
return deferred.promise;
}
})

angular 10 $digest() iterations reached

I following thinkster MEAN stack tutorial and I have a problem in angular factory service
angular.js:11598 Error: [$rootScope:infdig] 10 $digest() iterations
reached. Aborting! Watchers fired in the last 5 iterations: []
app.js
app.factory('posts', ['$http', function($http){
var o = {
posts: []
};
o.getAll = function() {
return $http.get('/posts').success(function(data){
console.log(data)
angular.copy(data, o.posts);
});
};
return o;
}]);
my config file has route provider
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl',
resolve: {
post: ['$stateParams', 'posts', function($stateParams, posts) {
return posts.get($stateParams.id);
}]
}
})
Im not sure what is wrong..
Any help is much appreciated. Thanks in advance...
.successis deprecated so i will use then
I think this is what you wanted to write.
app.factory('posts', ['$http', function($http){
var o = {};
o.get = function(id){
return $http.get('/posts/'+id).then(function(response){
return response.data;
});
}
o.getAll = function() {
return $http.get('/posts').then(function(response){
return response.data;
});
};
return o;
}]);
resolve: {
post: ['$stateParams', 'posts', function($stateParams, posts) {
return posts.get($stateParams.id);
}]
}
// usage of the factory in controller :
posts.getAll().then(function(posts){
$scope.allPosts = posts;
})
posts.get(id).then(function(post){
$scope.post = post;
})
Some points :
then/ successare chainable; however you must use the return statement in order than the next chain will have the data. I will have what you have returned.
I totally don't know from where you got your return posts.get($stateParams.id); so i added something relevant.

How to resolve data into nested view multiple times, with different data

I am using the AngularJS framework (as well as its ui-router), and I am having a hard time getting my data to resolve the way I want. I will give an example below of what I am trying to do:
$stateProvider
.state('home',{
views: {
'test': {
templateUrl: 'test.html',
resolve: {
config: function() {
var result = /*service call that returns json*/
return result;
}
}
controller: function($scope, config){
console.log(config);
}
},
'test': {
templateUrl: 'test.html',
resolve: {
config: function() {
var result = /*service call that returns DIFFERENT json*/
return result;
}
}
controller: function($scope, config){
console.log(config);
}
}
}
})
<div ui-view="test">
<div ui-view="test">
Is there any way in which to uniqely assign and inject 'config' into the controller so that it contains the json that its respective service call returned? The idea here is I want the same template but with different configs coming into them (scoped to that particular view). Right now, config just contains the last resolve that was executed.
I hope I am not confusing. Let me know if you need any clarifications...Much appreciated!
The views need to be named differently and so do the resolves. Using the same template for both of them is not an issue. Lastly, the resolve has to be relative to the state, not the views.
$stateProvider
.state('home',{
views: {
'test': {
templateUrl: 'test.html',
controller: function($scope, config){
console.log(config);
}
},
'test2': {
templateUrl: 'test.html',
controller: function($scope, config2){
console.log(config2);
}
}
},
resolve: {
config: function() {
var result = /*service call that returns json*/
return result;
},
config2: function() {
var result = /*service call that returns DIFFERENT json*/
return result;
}
}
})
<div ui-view="test">
<div ui-view="test2">

Why am I unable to see this variable in my view?

So here's the relevant segment from my router:
app.js
.state('app.browse', {
url: "/browse/:question",
controller: 'QCtrl',
resolve: {
question: function($stateParams, qService){
console.log(qService.getQuestion($stateParams.question).q);
return qService.getQuestion($stateParams.question).q;
}
},
views: {
'menuContent': {
templateUrl: "templates/browse.html"
}
}
})
/* QSERVICE HERE */
.factory('qService', function() {
var questions = [ {'q': "text text"} ];
return {
questions: questions,
getQuestion: function(index) {
return questions[index]
}
}
})
controllers.js
.controller('QCtrl', function($scope, question){
$scope.questions = qService.questions;
$scope.question = question;
})
Which finds exactly what I'm looking for as demonstrated by the console log.
However in my browser view, I am unable to grab the question variable!
browser.html
<ion-view view-title="Browse">
{{question}}
</ion-content>
Which always displays as empty! Why is this happening, and how do I resolve it?
Resolve will not bind question unto your controller.
In your controller do this
.controller('QCtrl', function ($scope, question) {
$scope.question = question;
})
In addition - in your state object, the question is being passed incorrectly. Correction:
.state('app.browse', {
url: "/browse/:question",
resolve: {
question: function($stateParams, qService){
return qService.getQuestion($stateParams.question);
}
},
views: {
'menuContent': {
templateUrl: "templates/browse.html",
controller: 'QCtrl',
}
}
})
You're also missing templateUrl in your state object. Update this to reflect where the the template is and it should be good to go :)

AngularJS : angular-ui-router always redirects to $urlRouterProvider.otherwise location

I'm trying to create an SPA where you have to be logged in to access almost everything. So naturally, the default screen you see is the login screen. However, after a user has logged in, no matter what the ui-sref is, ui-router redirects to the login page (even when the user is authenticated). Here is my ui-router code:
(function () {
'use strict';
angular
.module('app', ['ui.router', 'satellizer'])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('auth');
}
});
return $q.reject(rejection);
},
response: function(response) {
var authorization = response.headers('authorization');
if(authorization !== null) {
authorization = authorization.substr(7).trim();
//console.log(authorization);
var $auth = $injector.get('$auth');
$auth.setToken(authorization);
}
return response;
}
}
}]);
$authProvider.loginUrl = 'mingdaograder/api/authenticate';
$stateProvider
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
//.state('otherwise', {
// url: '*path',
// templateUrl: 'views/authView.html',
// controller: 'AuthController as auth'
//});
//$urlRouterProvider.otherwise('/auth');
$urlRouterProvider.otherwise(function($injector, $location) {
console.log("Could not find " + $location);
$location.path('/auth');
});
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log(toState.name);
var user = JSON.parse(localStorage.getItem('user'));
if (user) {
$rootScope.authenticated = true;
$rootScope.currentUser = user;
}
}
);
}
);
})();
Anytime I try to use $state.go(any state name here) or even type the address into the address bar, I am always redirected to the auth state. On the console the message is "Could not find http://localhost/#/" for every single route. I can type in http://localhost/#/users/5/subjects and I get the same message.
Here is one of my controllers doing a redirect:
(function () {
'use strict';
angular
.module('app')
.controller('AuthController', AuthController);
function AuthController($auth, $state, $http, $rootScope, $log) {
var vm = this;
vm.loginError = false;
vm.loginErrorText;
vm.login = function () {
var credentials = {
username: vm.username,
password: vm.password
};
$auth.login(credentials).then(function () {
return $http.get('api/authenticate/user');
}, function (error) {
vm.loginError = true;
vm.loginErrorText = error.data.error;
}).then(function (response) {
var user = JSON.stringify(response.data.user);
localStorage.setItem('user', user);
$rootScope.authenticated = true;
$rootScope.currentUser = response.data.user;
//$log.info('From AuthCtrl: ' + $rootScope.currentUser.id);
$state.go('subjects', {user_id:$rootScope.currentUser.id});
});
}
}
})();
Any ideas what I'm doing wrong? Thanks a lot for your time.
Update: Ok, I haven't found a way to fix it but I think I may have found a possible cause. It seems to only happen for the routes with parameters. For example, if I go to the users state, whose path is /users, there is no redirect. However, if I go to the subjects state, whose path is /users/:user_id/subjects, it does redirect. It's like the Url matching service can't recognize that /users/5/subjects matches /users/:user_id/subjects, so redirects. Any ideas how to work around this?
I found I didn't have a '/' at the beginning of my initial state url. Every time I navigated to the state, the missing '/' seemed to push it into the stateProvider.otherwise.
state1: 'opportunity'
state1Url : '/opportunity/' <== added initial forward slash to make it work.
state2: 'opportunity.create'
state2Url : 'create/'
The first path to be recognised will be the selected as the current location. This means that the order of your route definitions is crucially important. In your case you only have a single catch-all otherwise route definition and since all routes match this then all routes are directed to your login page ignoring any other route definitions you may have, including all your stateProvider state definitions.
One way to fix this is to remove the urlRouterProvider route definition altogether and instead use the *path syntax provided by ui-router to create an alternative otherwise state (which must be defined last for the same reasons given above).
Therefore your code might look something like this:
$stateProvider
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
})
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state("otherwise", {
url: "*path",
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
From experience, this is either due to the / missing at either the beginning or the end of the url route property definition.
Make sure for parent routes to add the initial forward slash to your routes.
.state('checkers', {
url: '/checkers/',
templateUrl: 'checkers.html',
controller: 'CheckersController',
title: 'Checker',
})
(function () {
'use strict';
angular
.module('app', ['ui.router', 'satellizer'])
.config(function ($stateProvider, $urlRouterProvider, $authProvider, $httpProvider, $provide) {
$httpProvider.interceptors.push(['$q', '$injector', function($q, $injector){
return {
responseError: function (rejection) {
var $state = $injector.get('$state');
var rejectionReasons = ['token_not_provided', 'token_expired', 'token_absent', 'token_invalid'];
angular.forEach(rejectionReasons, function (value, key) {
if (rejection.data.error === value) {
localStorage.removeItem('user');
$state.go('auth');
}
});
return $q.reject(rejection);
},
response: function(response) {
var authorization = response.headers('authorization');
if(authorization !== null) {
authorization = authorization.substr(7).trim();
//console.log(authorization);
var $auth = $injector.get('$auth');
$auth.setToken(authorization);
}
return response;
}
}
}]);
$authProvider.loginUrl = 'mingdaograder/api/authenticate';
$stateProvider
.state('users', {
url: '/users',
templateUrl: 'views/userView.html',
controller: 'UserController as user'
})
.state('subjects', {
url: '/users/:user_id/subjects',
templateUrl: 'views/subjectsView.html',
controller: 'SubjectsCtrl as subjectsCtrl'
})
.state('subject', {
url: '/users/:user_id/subjects/:subject_id',
templateUrl: 'views/subjectView.html',
controller: 'SubjectCtrl as subjectCtrl'
})
.state('auth', {
url: '/auth',
templateUrl: 'views/authView.html',
controller: 'AuthController as auth'
});
//.state('otherwise', {
// url: '*path',
// templateUrl: 'views/authView.html',
// controller: 'AuthController as auth'
//});
//$urlRouterProvider.otherwise('/auth');
$urlRouterProvider.otherwise(function($injector, $location) {
console.log("Could not find " + $location);
$location.path('/auth');
});
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log(toState.name);
var user = JSON.parse(localStorage.getItem('user'));
if (user) {
$rootScope.authenticated = true;
$rootScope.currentUser = user;
}
}
);
}
);
})();

Categories

Resources