I'm stuck with nested states in the ui-router.
I'm rendering a page with regions, countries and people per country
The index.html has three regions as link, EMEA APAC and AMER
the part of the index page has:
<a ui-sref="territory({territory: 'amer'})">AMER</a> <a ui-sref="territory({territory: 'emea'})">EMEA</a> <a ui-sref="territory({territory: 'apac'})">APAC</a>
<ui-view></ui-view>
when the link is clicked, the countries are returned by a $http.post action and displayed by /views/countries.html as:
<h1>This is countries</h1>
<br>
Teritory: {{region}}
<div ng-repeat='country in countries'>
<a ui-sref=".support({country: '{{country}}'})">{{country}}</a ui-sref>
</div>
This is working so far! so I have a .support link on each country.
The problem is now, when I click the link, the state is accepted...cause it is loading the templateURL, but it seems that the controller specified in that state is not loaded.
the angularjs code:
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'views/home.html',
controller: 'supportCtrl'
})
.state('territory', {
url: '/territory/:territory',
templateUrl: 'views/countries.html',
controller: 'countryController'
})
.state('territory.support',{
url: '/:country',
templateUrl: 'views/supporter.html',
controller: 'supportController'
})
});
the support controller is writing a console.log entry...but nothing happens.
the controllers:
supportApp.controller('countryController',['$scope', 'supportService', '$location', '$stateParams', function($scope, supportService, $location, $stateParams){
$scope.fromTerritory = $stateParams.territory;
$scope.getCountries = function(){
var countries = supportService.getCountries($scope.fromTerritory);
countries.then(function(response){
if(response.data.error){
$scope.error = true;
$scope.message = response.data.message;
}
else{
console.log('response OK');
$scope.region = $scope.fromTerritory;
$scope.countries = response.data;
}
});
}
$scope.getCountries();
}]);
supportApp.controller('supportController',['$scope', 'supportService', '$location', '$stateParams', function($scope, supportService, $location, $stateParams){
console.log('in the supportController');
var test = function(){
console.log('supportController country: '+$stateParams.country);
}
test();
}]);
I'm probably doing something wrong here.
The goal is clicking the country and then displaying names of the people belonging to that specific country.
If it's not clear, I can provide more info where needed. But for now it seems that the controller (supportController) for the nested state (.territory.support) is not running / loaded
thank you!
Ok,
I have my answer for now...
because I did not use the <ui-view></ui-view> on the supporter.html the controller was not 'needed' and did not load.
after adding the the consol.log is showing the log entries.
Related
I am trying to make a nested views, here is the plunker https://embed.plnkr.co/oRMnMW4QoWwhSkm9maHf/. The state changes but the template not changes.
Can anyone correct me what have I done wrong
Goto Link > Second Nested .
On Clicking the button , state changes successfully but the content is not injected. I want the link page content to be replaced by the second-nested content
Try to put abstract:true on the 'father' root like:
var routerApp = angular.module('routerApp', ['ui.router','ncy-angular-breadcrumb']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home/list');
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
abstract: true,
templateUrl: './partial-home.html'
})
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: './partial-home-list.html',
controller: function($scope) {
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('home.second', {
url: '/second',
templateUrl: './second.html',
});
});
routerApp.run(['$rootScope', '$state', function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log("state Change")
});
}]);
but Remember .. if you put abstract: true .. the url is not a real one .. it's a prefix .. or as i call it .. a father of other routing ..so you can not call it in the .otherwise()
And in your link (and routing) of the second view .. just remove the .list ... so like this:
.state('home.second', { //<-- HERE .. REMOVE THE .list
url: '/second',
templateUrl: './second.html',
});
and in the link:
// AND HERE ..
<a ui-sref="home.second" class="btn btn-danger">Second Nested</a>
Answer is pretty simple - you are initializing 3rd level of nesting states but in ./partial-home-list.html you've didn't add ui-view directive.
Add <ui-view></ui-view> in ./partial-home-list.html and you will see that it works as you defined.
If you want to display home.list.second as separate page then define second state like this
.state('home.second', {
url: '/home/list/second',
templateUrl: 'second.html',
});
Remember to update ui-sref to home.second on button
--
Just to get nested breadcrumb I have not "nice" solution but will work.
-- Partial home list html
<div ng-if="state.current.name != 'home.list.second'">
<ul>
<li ng-repeat="dog in dogs">{{ dog }}</li>
</ul>
<div ncy-breadcrumb></div>
<a ui-sref="home.list.second" class="btn btn-danger">Second Nested</a>
</div>
<ui-view></ui-view>
App js
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: 'partial-home-list.html',
controller: function($scope, $state) {
$scope.state = $state;
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('home.list.second', {
url: '/second',
templateUrl: 'second.html',
});
I'm using ui-router with angularjs. I want to write a template for a view that will show a image depending on what the view is. here's my example state.
$stateProvider
.state('index', {
url: "",
views: {
"topStormtrooper": {
templateUrl: '/components/stormtroopers/stormtroopers.html',
controller: "stormtroopersCtrl"
},
"bottomStormtrooper": {
templateUrl: '/components/stormtroopers/stormtroopers.html',
controller: "stormtroopersCtrl"
}
}
})
my controller looks like this
.controller('stormtroopersCtrl', ['$scope', '$http', '$stateParams', function ($scope, $http, $stateParams) {
//
$scope.stormtrooper = $stateView; //it should be something like this hopefully
}]);
The template is all the same just the image will be different depending which view it is loaded into. Currently I just added a new controller for each view and load the image based on that. But I feel like I should be able to do this with just one controller and the controller should be able to detect what the view is. I know you can detect the state but I want go deeper and get the view.
Any Ideas?
You can access the current state configuratin object like this:
$state.current
For further information take a look at the $state
You can listen to the $viewContentLoaded function in your controller as per the ui-router documentation
For example:
.controller('stormtroopersCtrl', ['$scope', '$http', '$stateParams', function ($scope, $http, $stateParams) {
$scope.$on("$viewContentLoaded",function(event,viewName){ //listen for when the content is loaded into the view
$scope.currentView = viewName; //assign the view name in a model
console.log($scope.currentView);
//do something because you got the view name now....
});
}]);
You do not need to change your state just for an image in template. You can make it with scope variables.
angular.module('app',[])
.controller('stormtrooperCtrl', ['$scope', function ($scope) {
//
$scope.selected = 0;
$scope.images = [
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSz6dBOOsFVAeSilVEIO9dqwrY4R5gCzEMcrRVZguhYhr9PVJsThQ",
"http://www.wallpapereast.com/static/images/Wallpaper-Nature-8B71.jpg",
"http://www.intrawallpaper.com/static/images/1250654-for-laptop-nature.jpg"
];
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="stormtrooperCtrl">
<button ng-click="selected=0">image1</button>
<button ng-click="selected=1">image2</button>
<button ng-click="selected=2">image3</button>
<div>
<img width="100" ng-src="{{images[selected]}}"/>
</div>
</div>
</div>
I'm new to AngularJS and stuck on below code.
app.config(function($routeProvider) {
$routeProvider
.when('/', {
templateUrl: "partials/home.html",
controller: "mainController",
})
.when('/products', {
templateUrl: "partials/productlist.html",
//controller: "ProductController",
})
.when('/product/:prodID', {
templateUrl: "partials/product.html",
controller: "viewController",
})
.when('/contact', {
templateUrl: "partials/contact.html",
controller: "contactController",
})
.otherwise({
redirectTo: "/"
});
});
app.controller('ProductController', function($scope, $http){
$http.get('partials/productTable.json').success(function(response){
$scope.datap = response.lists;
});
}).
controller('viewController',function($scope,$routeParams){
$scope.eachproduct = $scope.datap[$routeParams.prodID];
});
And my product.html page code will look like this.
<div ng-controller="viewController">
<ol class="breadcrumb">
<li>Home</li>
<li>Products</li>
<li class="active">{{eachproduct.link}}</li>
</ol>
<div class="col-md-4">
<figure><img ng-src="{{ }}"></figure>
<p>
Read More
</p>
</div>
</div>
Problem is when I navigate to any product page value of {{eachproduct.link}} is not showing.
Any solution will be appriciated.
Use $rootScope instead of $scope
$rootScope
The $rootScope is the top-most scope. An app can have only one $rootScope which will be shared among all the components of an app. Hence it acts like a global variable. All other $scopes are children of the $rootScope.
Sample :
controller('viewController',['$scope','$routeParams', '$http','$rootScope',function($scope,$routeParams, $http,$rootScope){
$http.get('partials/productTable.json').success(function(response){
$scope.datap = response.lists;
$rootScope.eachproduct = $scope.datap[$routeParams.prodID];
});
}]);
app.controller('ProductController', function($scope, $http){
$http.get('partials/productTable.json').success(function(response){
$scope.datap = response.lists;
});
}).
controller('viewController',function($scope,$routeParams, $http){
$http.get('partials/productTable.json').success(function(response){
$scope.datap = response.lists;
$scope.eachproduct = $scope.datap[$routeParams.prodID];
});
});
It seems like what you are looking for is an angular provider such as a factory to store the values in, this will allow the values to be pass values around the controllers while using the routes.
Have a look at this example, while it isn't using routes, the principal is the same:
https://jsbin.com/wiwejapiku/edit?html,js,output
For more information on providers have a look here:
https://docs.angularjs.org/guide/providers
Your example would work something like this:
app
.factory('productFactory',function(){
return {
data: {}
};
})
.controller('ProductController', function($scope, $http, productFactory){
$scope.productFactory = productFactory;
$http.get('partials/productTable.json').success(function(response){
$scope.productFactory.data = response.lists;
});
}).
controller('viewController',function($scope,$routeParams, productFactory){
$scope.productFactory = productFactory;
$scope.eachproduct = $scope.productFactory.data[$routeParams.prodID];
});
Note you would also have to change your view to reference 'productFactory.data' respectively.
I am using UI-Router for an Angular app. I can't seem to find what I am doing wrong. I am also not getting any errors which is making it really difficult for me to debug. Followed the docs as well and I am following their steps. My controller function is working when I don't nest it in a child view. Can someone please direct me to what I am doing wrong? Thanks in advance!
APP.JS
'use strict';
var app = angular.module('americasTopStatesApp', ['ui.router', 'ngAutocomplete']);
app.run(function($state, $rootScope) {
$rootScope.$state = $state;
});
app.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider
.otherwise('/home');
$stateProvider
//HOME
.state('home', {
url: '/home',
templateUrl: './app/views/homeTmpl.html',
controller: 'homeCtrl'
})
//RANKINGS
.state("rankings", {
url: "/rankings",
templateUrl: './app/views/rankingsTmpl.html',
controller: 'rankingsCtrl'
})
// RANKINGS CHILDREN
.state('rankings.data', {
url: '/data',
templateUrl: './app/views/rankingsDataTmpl.html',
controller: 'rankingsCtrl',
parent: 'rankings'
})
});
CONTROLLER rankingsCtrl
'use strict';
app.controller('rankingsCtrl', function($scope, rankingsService) { //Start Controller
// ***********************************************
// *************** GET LATEST DATA ***************
// ***********************************************
$scope.getAllStateRankings = function() {
rankingsService.getStateRankingsData().then(function(data) {
$scope.showRankings = true;
// console.log("Contoller Data", data);
$scope.states = data;
});
};
$scope.showRankings = false;
$scope.getAllStateRankings();
}); //End Controller
PARENT VIEW rankingsTmpl.html
<div class="rankings-heading">
<h1>America's Top States</h1>
<button ng-click="getAllStateRankings()">
<a ui-sref="rankings.data" id="data" class="btn">Data</a>
</button>
</div>
</div ui-view></div>
Child View (Nested ui-view) rankingsDataTmpl.html
<div class="rankings-container" ng-show="showRankings">
<div class="panel panel-primary" ng-repeat='state in states'>
<div class="panel-heading">
<h3 class="panel-title">{{state.state}}</h3>
</div>
<div class="panel-body">
Economy: {{state.economy}}<br>
Capital Access: {{state.accessToCapital}}<br>
Business: {{state.business}}<br>
Cost of living: {{state.costOfLiving}}<br>
</div>
</div>
</div>
Screen Shot
There is a working plunker
In this case, when we have parent child and angular's UI-Router, we should not use solution based on
parent and child has same controller. // WRONG approach
Because they in fact do have JUST same type. The instance of that type 'rankingsCtrl' in runtime is different.
What we need is:
How do I share $scope data between states in angularjs ui-router?
scope inheritance, driven by reference object, e.g. $scope.Model = {}
There is adjusted controller:
.controller('rankingsCtrl', ['$scope', function($scope) {
$scope.Model = {};
$scope.getAllStateRankings = function() {
//rankingsService.getStateRankingsData().then(function(data) {
$scope.Model.showRankings = true;
// console.log("Contoller Data", data);
$scope.Model.states = data;
//});
};
$scope.Model.showRankings = false;
$scope.getAllStateRankings();
}])
At the end, child can have different controller with its own logic for the child view:
.state("rankings", {
url: "/rankings",
templateUrl: 'app/views/rankingsTmpl.html',
controller: 'rankingsCtrl'
})
// RANKINGS CHILDREN
.state('rankings.data', {
url: '/data',
templateUrl: 'app/views/rankingsDataTmpl.html',
controller: 'rankingsChildCtrl',
parent: 'rankings'
})
Also, the parent view should have fixed div:
// wrong
</div ui-view></div>
// starting tag
<div ui-view></div>
Check it here in action
Have an app where admins create ITEMs for users to view. Each ITEM is a doc stored in Mongo.
The item.html view and ItemController.js are consistent for all the ITEMs..
The user is first presented an ITEM_list view..
..where the user can click on an ITEM divBox,
which would reveal the item.html view populated with the specific db content found for the selected ITEM
Is there a way to have angular do something like this in appRoutes.js
angular.module('appRoutes', []).config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$routeProvider
// start page listing all the ITEMs
.when('/', {
templateUrl: 'views/itemsList.html',
controller: 'ItemsListController'
})
// dynamic pages for each ITEM, once selected ?!
.when('/{{ITEM}}', {
templateUrl: 'views/item.html',
controller: 'ItemController'
});
$locationProvider.html5Mode(true);
}]);
You can use parameters in the route by using a colon before whatever variable name you want.
For example:
.when('/:itemID', {
templateUrl: 'views/item.html',
controller: 'ItemController'
}
Then in your ItemController, you can call that using $routeParams.
.controller('ItemController', ['$scope', '$routeParams',
function($scope, $routeParams) {
$scope.itemID = $routeParams.itemID;
}]);
Here is the link to the Angular docs for some more guidance. http://docs.angularjs.org/tutorial/step_07
You can pass the item id, for example, like so:
.when('/item/:item_id', {
templateUrl: 'views/item.html',
controller: 'ItemController'
})
Then, in your controller, you can inject $routeParams:
.controller('ItemController', function($scope, $routeParams) {
var item_id = $routeParams.item_id;
});
Then, when they select, you set the location to /item/2 or whatever, and you know it is item 2 in your controller, so you can then either fetch that item from the server, or if you have a service with them already loaded you can figure out which one it is.