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
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;
});
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);
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;
});
})
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.
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