Angular UI Router - Load View With Controller Embedded - angularjs

Instead of doing two $http requests, one for the controller data and one for the view data, how come I can't just load a view and have a controller embedded in the view? It doesn't work.
My router code:
app.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/admin");
$stateProvider.state('home', {
url: "",
templateProvider: function($http, $stateParams) {
return $http({
method: 'GET',
url: '/admin/home'
}).then(function successCallback(html) {
return html.data;
});
},
controller: function($scope) {
// $scope.activeSection.activeSection = "notNone";
}
})
//all other states go here
});
My view returned from the templateProvider $http promise:
<div class="container" ng-controller="home">
{{orders[0]}}
</div>
<script>
app.controller('home', ['$scope', '$http', '$state', function($scope, $http, $state) {
$scope.orders = <?php echo json_encode($data[0]); ?>;
}]);
</script>
But I get an error saying "home" is undefined. I understand I can just set the controller on the route and do a $http request from there but it seems silly if I can just get what I need from a controller standpoint already in the view. Right? Or am I missing something. It would make it easier on the server to not have multiple routes (view and controller) for what is essentially one route.

Related

angular function call before html view is load

I am working on angularjs (1.6) and want to made a functionality in angular service, its call when a controller call and its service have an ajax code like
app.service('myServ', function($http, $window){
this.backdoor=function(){
$http({
method : 'get',
url : 'web_services/backdoor.php'
}).then(function(res){
// console.log(res.data);
// console.log(res.data.length);
if(res.data.length==0)
{
$window.location.href="index.html";
}
});
}});
and my controller code is :
app.controller('myCtrl', function($scope, $http, $window, myServ, $routeParams){
myServ.backdoor();
});
so the above code (service) is check a user session is created or not, but the problem is when session is not created on server side then my html page load for a second then server will call $window.location.href so please help me about the right way to do this....
I believe you need a resolve in angular.config. Its job is to run some code before you are being redirected to your route/state (for ngRoute or ui.router).
To do that you would need to have:
app.service('myServ', function($http, $window){
this.backdoor=function(){
return $http({ // return the promise if you need to use the values in the controller
method : 'get',
url : 'web_services/backdoor.php'
}).then(function(res){
if(res.data.length==0){ $window.location.href="index.html"; }
else{ return res.data; } // return the values
});
}});
and main part:
app.config(['$routeProvider', function($routeProvider) {
$routeProvider
.when('/your_page/:route_params', {
templateUrl: 'your_page_partial.html',
controller: 'myCtrl',
resolve: {
resolvedVal: function resMyService(myServ, $routeParams){ // setting an injectable instance `resolvedVal`
/* use $routeParams values as parameters? */
return myServ.backdoor(); // calling your service
}
}}
);
}]);
Then it's enough to just inject your resolve into the controller:
app.controller('myCtrl', function($scope, $http, $window, resolvedVal){ // note: resolvedVal injection
$scope.my_data = resolvedVal;
});

Angular Ui-router can't access $stateParams inside my controller

I am very beginner in Angular.js, I am using the Ui-router framework for routing.
I can make it work upto where I have no parameters in the url. But now I am trying to build a detailed view of a product for which I need to pass the product id into the url.
I did it by reading the tutorials and followed all the methods. In the tutorial they used resolve to fetch the data and then load the controller but I just need to send in the parameters into the controllers directly and then fetch the data from there. My code looks like below. when I try to access the $stateParams inside the controller it is empty. I am not even sure about whether the controller is called or not.
The code looks like below.
(function(){
"use strict";
var app = angular.module("productManagement",
["common.services","ui.router"]);
app.config(["$stateProvider","$urlRouterProvider",function($stateProvider,$urlRouterProvider)
{
//default
$urlRouterProvider.otherwise("/");
$stateProvider
//home
.state("home",{
url:"/",
templateUrl:"app/welcome.html"
})
//products
.state("productList",{
url:"/products",
templateUrl:"app/products/productListView.html",
controller:"ProductController as vm"
})
//Edit product
.state('ProductEdit',{
url:"/products/edit/:productId",
templateUrl:"app/products/productEdit.html",
controller:"ProductEditController as vm"
})
//product details
.state('ProductDetails',{
url:"/products/:productId",
templateUrl:"app/products/productDetailView.html",
Controller:"ProductDetailController as vm"
})
}]
);
}());
this is how my app.js looks like. I am having trouble on the last state, ProdcutDetails.
here is my ProductDetailController.
(function(){
"use strict";
angular
.module("ProductManagement")
.controller("ProductDetailController",
["ProductResource",$stateParams,ProductDetailsController]);
function ProductDetailsController(ProductResource,$stateParams)
{
var productId = $stateParams.productId;
var ref = $this;
ProductResource.get({productId: productId},function(data)
{
console.log(data);
});
}
}());
NOTE : I found lot of people have the same issue here https://github.com/angular-ui/ui-router/issues/136, I can't understand the solutions posted their because I am in a very beginning stage. Any explanation would be very helpful.
I created working plunker here
There is state configuration
$urlRouterProvider.otherwise('/home');
// States
$stateProvider
//home
.state("home",{
url:"/",
templateUrl:"app/welcome.html"
})
//products
.state("productList",{
url:"/products",
templateUrl:"app/products/productListView.html",
controller:"ProductController as vm"
})
//Edit product
.state('ProductEdit',{
url:"/products/edit/:productId",
templateUrl:"app/products/productEdit.html",
controller:"ProductEditController as vm"
})
//product details
.state('ProductDetails',{
url:"/products/:productId",
templateUrl:"app/products/productDetailView.html",
controller:"ProductDetailController as vm"
})
There is a definition of above used features
.factory('ProductResource', function() {return {} ;})
.controller('ProductController', ['$scope', function($scope){
$scope.Title = "Hello from list";
}])
.controller('ProductEditController', ['$scope', function($scope){
$scope.Title = "Hello from edit";
}])
.run(['$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}])
.controller('ProductDetailController', ProductDetailsController)
function ProductDetailsController ($scope, ProductResource, $stateParams)
{
$scope.Title = "Hello from detail";
var productId = $stateParams.productId;
//var ref = $this;
console.log(productId);
//ProductResource.get({productId: productId},function(data) { });
return this;
}
ProductDetailsController.$inject = ['$scope', 'ProductResource', '$stateParams'];
Check it here
But do you know what is the real issue? Just one line in fact, was the trouble maker. Check the original state def:
.state('ProductDetails',{
...
Controller:"ProductDetailController as vm"
})
And in fact, the only important change was
.state('ProductDetails',{
...
controller:"ProductDetailController as vm"
})
I.e. controller instead of Controller (capital C at the begining)
The params in controller definition array should be strings
["ProductResource", "$stateParams"...
This should properly help IoC to inject the $stateParams
And even better:
// the info for IoC
// the style which you will use with TypeScript === angular 2.0
ProductDetailsController.$inject = ["ProductResource", "$stateParams"];
// this will just map controller to its name, parmas are defined above
.controller("ProductDetailController", ProductDetailsController);

Angular - Wait $http finish until go to the next line

It's a simple webshop. So I'd like to load several things before show the home page and the controller.
I decided that the index.html will be also the Master Page containing a header, the ui-view for routing templates , a sidebar containing the categories and a footer.
<body ng-app="app">
<header ng-include="'./templates/masterPage/header.html'"></header>
<div ui-view></div>
<sidebar ng-include="'./templates/masterPage/sideBar.html'"></sidebar>
<footer ng-include="'./templates/masterPage/footer.html'"></footer>
</body>
The things on header, sidebar and footer will came from a json file containgin everything I need such as categories, currency, name of the webshop and others things that's will never changes after load.
So, I created the following app.js...
angular
.module('app', [
'ui.router'
])
.config(['$urlRouterProvider', '$stateProvider',
function ($urlRouterProvider, $stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: 'templates/common/home.html',
controller: 'homeCtrl',
})
}])
.run(function ($rootScope, $http) {
$rootScope.api = "http://127.0.0.1:5000/demoShop";
var call = $rootScope.api + "/frontEnd/loadStructure";
$rootScope.webshop = $http.get(call).then(function (response) {
console.log('1');
return response.data;
});
console.log("2");
})
The problem is: check the console.logs in the end....2 executes before 1 because I can't make the $http to wait until go to the next line. How can I do it ?
I need to populate the webshop variable on rootscope before do anything.
Simple answer:
You can't.
Longer answer:
You can wait for the AJAX request to finish. That's what you're already doing - it's the function in .then. You can put your code there. Question is - why do you need it to be finished first? Or maybe better question is why do you need to do the request in the controller? Isn't it a bit too late for that? See related answer from Misko Hevery.
I would suggest resolving the data when the homeCtrl for the root-route loads. Then you can access you webshop data apon instaciation of the controller. Consider the example below:
angular
.module('app', [
'ui.router'
])
.config(['$urlRouterProvider', '$stateProvider',
function ($urlRouterProvider, $stateProvider) {
var resolveLoadStructure = function() {
$rootScope.api = "http://127.0.0.1:5000/demoShop";
var call = $rootScope.api + "/frontEnd/loadStructure";
return $http.get(call);
};
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home', {
url: '/',
templateUrl: 'templates/common/home.html',
controller: 'homeCtrl',
resolve: {
loadStructure: resolveLoadStructure
}
});
}])
.controller('homeCtrl', function($scope, loadStructure) {
$scope.webshop = loadStructure;
})
I found the problem. My mistaken is to set the $rootScope directly - Instead I set this inner the callback. I just made a small surgery and everything works fine. Take a look
.run(function ($rootScope, $http) {
$rootScope.nodeshop = "http://127.0.0.1:5000/rawDemo";
var call = $rootScope.nodeshop + "/frontEnd/loadStructure";
var head = {};
$http.get(call).then(function (response) {
$rootScope.webshop = response.data; //See ? this variable is no longer set as result of the $http return !
return response.data;
});
})

Pass resolve to body controller

Hello ive got something like this
<body ng-app="app" id="ng-app" ng-controller="MasterCtrl">
and some views which need master controller to resolve some data
<div ui-view></div>
im using ui-router
how to pass resolve to masterctrl?
if i try something like doing the master state at '/' in state configuration i dont get master resolved when urling to children
So, you're resolving something in your controller(s).. [let's use 'user']
.state('myCoolState', {
url: baseURL + 'coolState/:id',
templateUrl: baseURL + 'Scripts/Templates/CoolStateView.html',
controller: 'coolStateController',
title: 'MyCoolState.com',
resolve: {
user : function (userService) {
return userService.getUser();
}
}
})
and it's working great.. but now you want to resolve user in your master controller..
What works for me is, with a factory like:
.factory("userService", function ($q, $http, baseURL) {
return {
getUser: function () {
var deferred = $q.defer();
$http({
url: baseURL + 'api/User/',
method: "GET"
})
.then(function (result) {
deferred.resolve(result.data.data);
});
return deferred.promise;
}
}
});
I just inject my userService into the master controller
MasterCtrl.$inject = ['$scope', '$location', '$rootScope', '$state', '$http', 'baseURL', 'userService'];
with my master controller defined as
var MasterCtrl = function ($scope, $location, $rootScope, $state, $http, baseURL, user) {
user.getUser().then(function() {
//stuff where you need access to user
//this won't happen until 'user' is resolved
});
};
It'd be nice to resolve user outside of the masterCtrl definition.. so it looked cleaner.. as far as I know you can't do that.

Can't access value of scope in AngularJS. Console log returns undefined

I'm using ui-router for my application and nesting controllers within ui-view. My parent controller looks like this:
'use strict';
angular.module("discussoramaApp").controller("mainController", ["RestFullResponse", "Restangular", "localStorageService", "$scope", function(RestFullResponse, Restangular, localStorageService, $scope){
var currentId = localStorageService.get("***");
var user = Restangular.one("users", currentId);
var Profile = user.get({}, {"Authorization" : localStorageService.get('***')}).then(function(profile) {
$scope.profile = profile;
});
}]);
And my child controller:
'use strict';
angular.module("discussoramaApp").controller("getTopicsController", ["RestFullResponse", "Restangular", "localStorageService", "$scope", function(RestFullResponse, Restangular, localStorageService, $scope){
var topics = Restangular.all('topics');
var allTopics = topics.getList({},{"Authorization" : localStorageService.get('***')}).then(function(topics){
$scope.topics = topics;
});
console.log($scope); // this works
console.log($scope.profile); // this returns undefined
}]);
The problem I'm having is getting the inherited $scope value for profile in the child controller. When I log $scope, profile is clearly visible in the console.
But when I try to log $scope.profile the console returns undefined. Any ideas?
Edit: Adding my ui-router config.
angular.module("discussoramaApp").config(
function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('/home');
$urlRouterProvider.when('', '/home');
$stateProvider
.state('main',{
url: '',
templateUrl: 'partials/main.html',
requireLogin: true
})
.state('main.home',{
url: '/home',
templateUrl: 'partials/main.home.html',
requireLogin: true,
title: 'Home'
});
}
);
And the corresponding html files:
// main.html
<div ng-controller="mainController">
<div class="container">
<div ui-view></div>
</div>
</div>
and the child html partial:
// main.home.html
<div ng-controller="getTopicsController">
<div ng-repeat="topic in topics | filter:search">
<a ui-sref="main.topic({id: topic.id})">{{ topic.topic_title }}</a>
</div>
</div>
UPDATE: Solved this with a watcher set up like this in the child controller. Thanks #jonathanpglick and #Nix for the help.
$scope.$watch('profile', function(profile) {
if(profile) {
$window.document.title = "Discussorama | " + profile.user.name;
}
});
$scope.profile is being set after an asynchronous request so I suspect that the second controller is being instantiated before user.get() returns and assigns a value to $scope.profile.
I think you'll want to set up a watcher (like $scope.$watch('profile', function(profile) {});) in the child controller so you can do things when the profile becomes available or changes.
Also, the reason you can see the profile key on $scope when you console log $scope can be explained here: https://stackoverflow.com/a/7389177/325018. You'll want to use console.dir() to get the current state of the object when it's called.
UPDATE:
I just realized you're using the ui-router and so there's an even easier way to do this. The ui-router has a resolve object that you can use to dependency inject things like this into your controller. Each resolve function just needs to return a value or a promise and it will be available for injection into the controller with resolve key name. It would look like this for you:
angular.module("discussoramaApp").config(
function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('/home');
$urlRouterProvider.when('', '/home');
$stateProvider
.state('main',{
url: '',
templateUrl: 'partials/main.html',
requireLogin: true,
resolve: {
profile: ['Restangular', 'localStorageService', function(Restangular , localStorageService) {
var currentId = localStorageService.get("***");
var user = Restangular.one("users", currentId);
return user.get({}, {"Authorization" : localStorageService.get('***')});
}
}
})
.state('main.home',{
url: '/home',
templateUrl: 'partials/main.home.html',
requireLogin: true,
title: 'Home'
});
}
);
angular.module("discussoramaApp").controller("mainController", ["profile", "$scope", function(profile, $scope){
$scope.profile = profile;
}]);
Just because you have nested scope, doesn't mean it will wait for user.get() to return before instantiating your nested getTopicsController.
Your issue is:
mainController controller initializes and calls user.get()
getTopicsController initializes and logs console.log($scope.profile)
The call to user.get() returns and then sets on scope.
This is a common issue, if you need to gaurantee that $scope.profile is set, use resolve or watch the variable.
I actually gave an example of how to do this earlier today: AngularJS $rootScope.$broadcast not working in app.run

Resources