I really new with AngularJs and I'm trying to create multiple routes with different $http requests. My problem start when the route change and the page content show later.
I figure it out in some way and i think its not the right way.
Hope someone can tell me if there is a better way.
Note: AngularJs version: 1.6 | Using ui router
main.js
var asTwl = angular.module('asTwl', ['ui.router']);
asTwl.controller('generals', function($scope, $http, $timeout){
$scope.pageLoader = false;
$scope.getPageData = function(path, postData, obj){
$scope.pageLoader = true;
$http({
method: 'post',
url: path,
data: postData,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
.then(function(response) {
if (response.data) {
$scope.data[obj] = JSON.parse(response.data);
$timeout(function(){
$scope.pageLoader = false;
}, 100)
}
})
.catch(function(e) {
new Error('Error: ', e);
throw e;
})
}
});
asTwl.controller('homePage', function($scope, $http){
var postData = {
//data...
}
$scope.getPageData('path', postData, 'home')
})
asTwl.controller('singlePage', function($scope, $http, $stateParams){
var postData = $stateParams;
$scope.getPageData('path', postData, 'page')
})
asTwl.controller('categoryPage', function($scope, $http, $stateParams){
var postData = $stateParams;
$scope.getPageData('path', postData, 'category')
})
asTwl.config(function($stateProvider, $urlRouterProvider, $locationProvider){
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl : 'templates/pages/home.html',
controller : 'homePage'
})
.state('info', {
url: '/info/:id',
templateUrl : 'templates/pages/info.html',
controller : 'singlePage'
})
.state('category', {
url: '/category/:type/:id',
templateUrl : 'templates/pages/category.html',
controller : 'categoryPage'
})
});
Thank you!
First, wrap your $http calls to services. Next,try to use resolve https://github.com/angular-ui/ui-router/wiki#resolve
Edit
Ok, example is here (without wrapping to service):
$stateProvider
.state('home', {
url: '/',
templateUrl : 'templates/pages/home.html',
controller : 'homePage',
resolve: {
routeData: function($http){
return $http({
method: 'post',
url: 'path',
data: postData /* your POST data - i don't know what is it for your code*/,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
})
}
}
})
.state('info', {
url: '/info/:id',
templateUrl : 'templates/pages/info.html',
controller : 'singlePage'
})
.state('category', {
url: '/category/:type/:id',
templateUrl : 'templates/pages/category.html',
controller : 'categoryPage'
})
And in controller:
asTwl.controller('homePage', function($scope, routeData){
$scope.someData = routeData;
})
You should first create a service which will be responsible for communicating with Server/API for playing around with data. You could include that method getPageData in that, it returns a promise object.
Service
app.service('myService', function($http){
var self = this;
self.getPageData = function(path, postData){
return $http.post(path,postData, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' });
.catch(function(e) {
new Error('Error: ', e);
throw e;
});
});
Then you could easily utilize the resolve option of state of ui-router will wait till your ajax promise gets resolved.
.state('info', {
url: '/info/:id',
templateUrl : 'templates/pages/info.html',
controller : 'singlePage',
resolve: {
getData: function(myService) {
return myService.getPageData('path', {}, 'info')
}
}
})
In a nutshell your routes have to be changed like this:
.state('category', {
resolve: {
data : ($stateParams, dataService) => dataService.getData('path', $stateParams, 'category')
},
url: '/category/:type/:id',
templateUrl : 'templates/pages/category.html',
controller : 'categoryPage'
})
And getData method should be refactored to the service (dataService)
Related
I need to call php file using service/Factory method using Angular.js. Here instead of calling $http repeatedly in each file to call diferent php file for different purpose, I need to make it common. I am explaining one example below.
logincontroller.js:
var loginAdmin=angular.module('Takeme');
loginAdmin.controller('loginController',function($scope,$http,$location,$window,inputField){
$http({
method: 'POST',
url: "php/Login/verify.php",
data: userData,
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function successCallback(response){
},function errorCallback(response) {
});
}
I have one common route.js file which is common for all controller and given below.
route.js:
var Admin=angular.module('Takeme',['ui.router', '720kb.datepicker','ngMessages','ngCapsLock','ui.bootstrap','ngFileUpload','angularUtils.directives.dirPagination']);
Admin.run(function($rootScope, $state) {
$rootScope.$state = $state;
});
Admin.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('/',{
url: '/',
templateUrl: 'view/login.html',
controller: 'loginController'
})
})
Admin.factory('inputField',function($timeout,$window){
return{
borderColor:function(id){
$timeout(function() {
var element = $window.document.getElementById(id);
if(element){
element.focus();
element.style.borderColor = "red";
}
});
},
clearBorderColor:function(id){
$timeout(function() {
var element = $window.document.getElementById(id);
if(element){
element.style.borderColor = "#cccccc";
}
});
}
};
});
Here I need to that $http service to call the php file common for which in every controller I will call that $http repeatedly. I need to pass only the parameters for $http service and return the response.
create a factory/service
angular.module('myApp').factory('DataService', DataService);
DataService.$inject = ['$http', '$q'];
function DataService($http, $q) {
return {
getData: getData,
}
function getData(userData) {
var deferred = $q.defer();
$http({
method: 'POST',
url: "php/Login/verify.php",
data: userData,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}).then(function(response) {
deferred.resolve(response.data);
},
function(error) {
deferred.reject(error.data);
});
return deferred.promise;
};
}
then use this factory whenever you need in a controller
angular.module('myApp')
.controller('MyController', ['$scope', 'DataService',
function($scope, DataService ) {
$scope.getMyData = function() {
var data = {};
DataService.getData(data)
.then(function(response) {
}, function(error) {
});
};
}
]);
addressbookController :
$http({
method: 'GET',
url: '/api/getnewgroup'
})
.then(function (response) {
$scope.draft.groups = response.data;
$scope.groups = response.data; // updated
}, function (response) {
console.log(response);
});
In this above controller, i am getting json response in $scope.draft.groups, I have this draft object in another controller called profsmsController.
profsmsController :
$scope.draft = {
draftType: '',
scheduledTime: '',
senderdata: '',
draftData: {
contacts: ''
},
groups: {
select: false
},
senderName: '',
message: '',
draftName: '',
createdOn: '',
updatedOn: ''
};
How to access $scope object ?
My Controller:
angular
.module('sampleApp.controllers', [])
//addressbook page controller
.controller('addressbookCtrl', function ($http, $scope, $rootScope, $location,
$state, toastr, $timeout, $window, sharedService) {
// Groups
// get group
$http({
method: 'GET',
url: '/api/getnewgroup'
})
sharedService.getDraftPromise().then(function (response) {
$scope.groups = response.data;
$scope.draft.groups = response.data;
}, function (response) {
console.log('error');
});
})
.controller('profsmsCtrl', function ($http, $scope, $rootScope, $location,
$state, toastr, $timeout, $window) {
/* for drafts */
$scope.draft = {
draftType: '',
scheduledTime: '',
senderdata: '',
draftData: {
contacts: ''
},
groups: {
select: false
},
senderName: '',
message: '',
draftName: '',
createdOn: '',
updatedOn: ''
};
//add draft
$scope.addmanualInputDraft = function () {
$http.post('/api/addmanualinputdraft', $scope.draft).then(function (response) {
toastr.success("Added successfully!");
$('.bd-example-modal-lg-manual').modal('hide');
$state.reload();
});
}
})
My services.js:
angular
.module('sampleApp.services', [])
.factory('sharedService', function ($http) {
var draftPromise = $http({
method: 'GET',
url: '/api/getnewgroup'
});
return {
getDraftPromise: function () {
return draftPromise;
}
};
});
my app.js:
'use strict';
angular
.module('sampleApp', ['sampleApp.controllers', 'sampleApp.directives','sampleApp.services','sampleApp.filters','ui.router','toastr','ngSanitize', 'ui.select'])
.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.hashPrefix('');
$urlRouterProvider.otherwise('/dash');
$stateProvider
.state('dash', {
url: '/dash',
templateUrl: 'partials/dash',
})
.state('quicksms', {
url: '/quicksms',
templateUrl: 'partials/quicksms',
controller: 'quicksmsCtrl'
})
.state('professionalsms', {
url: '/professionalsms',
templateUrl: 'partials/professionalsms',
controller: 'profsmsCtrl'
})
.state('file2sms', {
url: '/file2sms',
templateUrl: 'partials/file2sms',
controller: 'file2smsCtrl'
})
.state('addressbook', {
url: '/addressbook',
templateUrl: 'partials/addressbook',
controller: 'addressbookCtrl'
})
});
This is updated full code. I want to access $scope.draft.groups object from addressbook Controller.
In general, you'd want to create a service that holds your shared data:
myApp.factory('sharedService', function($http) {
var draftPromise = $http({
method: 'GET',
url: '/api/getnewgroup'
});
return {
getDraftPromise: function() {
return draftPromise;
}
};
});
In your controllers, you can then use the service by declaring it as a dependency:
myApp.controller("myController", function($scope, sharedService) {
sharedService.getDraftPromise().then(function(response) {
$scope.draft.groups = response.data;
});
});
Both controllers will refer to the same instance of draftPromise.
Note: if you are minifying your code, you'll want to use the alternate syntax for dependency injection that uses arrays. Take a look at the official documentation for dependency injection.
I am new to Angular and I can't get my dropdown selectes to map to the browser URL (dynamic based on dropdown selections).
For example, the browser URL is "/home?color=Red&&size=Large". When I paste this URL in a new tab, I want the color and size dropdowns to already be selected as Red and Large. How do I do this?
My AngularJS controller code:
$scope.getResults = function() {
server.getResults($scope.myColor, $scope.mySize)
.success(function(data) {
results = data;
$scope.myColor = $location.search('color', $scope.myColor);
$scope.mySize = $location.search('size', $scope.mySize);
});
};
AngularJS service for the above function:
app.factory('server', ['$http', function($http){
return {
getResults : function(color, size) {
var req = {};
req.color = color;
req.size = size;
return $http({
method: 'GET',
url: 'results',
params : req
});
}
}
}]);
ui-router in Angular:
$stateProvider.state('home', {
url: '/home',
templateUrl: '/home.html',
controller: 'MainCtrl',
reloadOnSearch: false
})
Check this Solution
ui-router in Angular:
$stateProvider.state('home', {
url: '/home/:color/:size',
templateUrl: '/home.html',
controller: 'MainCtrl',
reloadOnSearch: false
})
myApp.controller('MainCtrl', function($scope, $stateParams)
{
$scope.color=$stateParams.color;
$scope.size=$stateParams.size;
});
View
var params= { size:'24', color:red };
$state.go('home', params);
Here are my code -
var app = angular.module('chartApp', []);
app.controller('artistCtrl', ['$scope', '$http', function($scope, $http) {
$scope.artists =
$http({
method: 'GET',
url: 'http://ws.audioscrobbler.com/2.0/?method=chart.gettopartists',
params: {api_key: 'e8452c5962aafbb3e87c66e4aaaf5cbf', format: 'json'}
}).success(function(result) {
return result.data;
});
console.log($scope.artists);
}]);
How to view the original data from json file?
Move the assignment statement inside the success block:
.success(function(result) {
$scope.artists = result.data;
})
Also, one more thing to note is the console.log($scope.artists) statement right after the $http block will still print undefined because it is executed before the promise is resolved.
Try this it will work :
var app = angular.module('chartApp', []);
app.controller('artistCtrl', ['$scope', '$http', function($scope, $http) {
$scope.artists =
$http({
method: 'GET',
url: 'http://ws.audioscrobbler.com/2.0/?method=chart.gettopartists',
params: {api_key: 'e8452c5962aafbb3e87c66e4aaaf5cbf', format: 'json'}
}).success(function(result) {
$scope.artists = result.data;
}).error(function(msg) {
$scope.err = msg;
});
}]);
I've often had a problem where I had a scope variable set up in a parent controller, and the child controller calls this scope variable. However, it calls it before the function has been able to set the scope element, causing it to return undefined. Example:
Parent Controller:
module.controller('parent', '$scope', '$http', function ($scope, $http) {
$scope.init = function(profileID, profileViewStatus) {
//Initiiaze user properities
$http.get(requestUserInformationGetURL + profileID)
.success(function(profile) {
$scope.profile = profile;
$scope.userID = profile.user_id;
$scope.username = profile.username;
console.log($scope.userID);
})
.error(function() {
exit();
});
}
Child Controller:
module.controller('child', function($scope, $http, fetchInfo) {
console.log($scope.userID);
//Fetch the HTTP POST data for the user profile
var promise = $http({
method: "post",
url: fetchInfo,
data: {
user_id: $scope.userID //From the parent controller
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
promise.then(function(successResponse) {
//Populate the scope, log the data
console.log(successResponse);
$scope.data = successResponse.data;
}, function(error) {
alert(error);
});
HTML:
<div ng-controller="parent" init="init('<?php $user_id;?>')">
<div ng-controller="child">
</div>
</div>
What often happens is that the userID will be reported back as undefined in the child controller, but then right after, it will be reported back as defined in the parent controller. Obviously, the child controller using the $scope.userID is being called before the init function in the parent controller is complete. How do I force AngularJS to wait in the child controller until the init function is complete? I've tried something like:
if (!$scope.userID) {
$scope.$digest();
}
But it didn't work and I don't think it's the correct syntax. I guess, I don't fully understand the Asycn nature of AngularJS and this occurs multiple times. How do you control the DOM loading elements to solve something like this problem?
Proper way in this case would be to use dedicated service to handle async operations, requests, data caching, etc. But since you don't have service layer yet, I will propose simple Promise-based solution using controller scope promise object.
Check you modified code:
module.controller('parent', ['$scope', '$http', function ($scope, $http) {
$scope.init = function (profileID, profileViewStatus) {
$scope.profilePromise = $http.get(requestUserInformationGetURL + profileID).success(function (profile) {
$scope.profile = profile;
$scope.userID = profile.user_id;
$scope.username = profile.username;
})
.error(exit);
}
}]);
module.controller('child', function($scope, $http, fetchInfo) {
// Fetch the HTTP POST data for the user profile
$scope.profilePromise.then(function() {
return $http({
method: "post",
url: fetchInfo,
data: { user_id: $scope.userID },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
})
.then(function(successResponse) {
console.log(successResponse);
$scope.data = successResponse.data;
}, function(error) {
alert(error);
});
});
As you can see, parent controller init method is still called, but now it immediately sets scope property profilePromise, which is accessible in child controller.
Child controller uses then method of the parent controller profilePromise object, which guaranties that $http request using $scope.userID will fire after profile is already available.
Generally you would use a route resolve with the UI Router to ensure the work is done before either controller is constructed. Child states automatically have access to the resolves of their parent.
//Router configuration
.state('app.inspections.list', {
url: '',
templateUrl: 'Template/parent',
controller: "Parent as vm",
resolve: {
profile: ['$http', function ($http) {
return $http.get(requestUserInformationGetURL + profileID)
.success(function(profile) {
console.log(profile.userID);
return profile;
})
.error(function() {
exit();
});
}]
}
}).state('parent.child', {
url: 'child',
templateUrl: 'Template/child',
controller: "Child as vm"
})
//parent controller
module.controller('parent', '$scope', 'profile', function ($scope, profile){
$scope.profile = profile;
$scope.userID = profile.user_id;
$scope.username = profile.username;
}
//child controller
module.controller('child', 'profile', function($scope, $http, fetchInfo, profile){
console.log(profile.userID);
//Fetch the HTTP POST data for the user profile
var promise = $http({
method: "post",
url: fetchInfo,
data: {
user_id: profile.userID //From the parent controller
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
promise.then(function(successResponse) {
//Populate the scope, log the data
console.log(successResponse);
$scope.data = successResponse.data;
}, function(error) {
alert(error);
});
you can use promise ($q service) :try using this code:
parent controller :
$scope.init = function(profileID, profileViewStatus) {
var deferred = $q.defer();
$http.get(requestUserInformationGetURL + profileID)
.success(function(profile) {
$scope.profile = profile;
$scope.userID = profile.user_id;
$scope.username = profile.username;
deferred.resolve($scope.userID);
console.log($scope.userID);
})
.error(function() {
deferred.reject('error');
exit();
});
return deferred.promise;
}
Don't call init method in parent contrller.
in child controller:
$scope.init().then(function(userID){
var promise = $http({
method: "post",
url: fetchInfo,
data: {
user_id: userID //From the parent controller
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
});
promise.then(function(successResponse) {
//Populate the scope, log the data
console.log(successResponse);
$scope.data = successResponse.data;
}, function(error) {
alert(error);
});
})
.catch(function(error){
console.log('error');
})
Your problem might be that $.get is being called asynchronously, which is the default behavior. Your init method might actually be called in the order you're expecting but what's happening is:
Parent init is called
$.get is called, but the server's response is non-instantaneous
Child init is called
GET data bounces back from the server
$.get(..).success(function(data){...}); is called to deal with the data
I'd suggest what other people are, using promises to defer execution.