Scenario:
I have a list of items rendered within an ng-repeat.
Each item has an item template.
When one of the items is clicked, that item becomes "active". That item then has a different template from the other items in the list.
By default, the first item in the list is "active".
Is it possible to use ui-router to accomplish this? I know I can use a templateURL function to get the state parameters, but then it would be applied to all items.
If possible, I'd like to avoid using ng-if/ng-switch since the active item would also have several possible nested states with different templates.
angular.module("app", ["ui.router"]);
angular.module("app")
.config(function($stateProvider) {
$stateProvider
.state("list", {
url: "/list",
abstract: true,
templateUrl: "list.html"
controller: "ListCtrl",
controllerAs: "listC",
})
// how to configure this to change template for "active" item in the list?
.state("list.item", {
url: "/list/item/:itemId"
});
});
angular.module("app")
.controller("ListCtrl", function($state) {
// this would be retrieved asynchronously
this.items = [
{id: 1, name: "One"},
{id: 2, name: "Two"},
{id: 3, name: "Three"},
];
$state.go("list.item", {itemId: this.items[0].id})
});
<div ng-app="app" ui-view></div>
<script type="text/ng-template" id="list.html">
<ul>
<li ng-repeat="item in listC.items" ui-view></li>
</ul>
</script>
<script type="text/ng-template" id="list-item.html">
<a ui-sref="list.item({itemId: item.id})">{{ item.name }}</a>
</script>
<script type="text/ng-template" id="list-item-active.html">
<h3>{{ item.name }}<h3>
</script>
take a look at http://angular-ui.github.io/ui-router/site/#/api/ui.router.state.directive:ui-sref-active
you should do something like:
<a ui-sref="list.item({itemId: item.id})" ui-serf-active="active">{{ item.name }}</a>
Related
Would this result in presenting the page with header, footer and content block filled with content.list view?
$stateProvider
.state('contacts', {
abstract: true,
url: '/contacts',
views: {
header: { templateUrl: 'admin/header.html'},
content: {
templateUrl: 'contacts.html',
controller: function($scope){
$scope.contacts = [{ id:0, name: "Alice" }, { id:1, name: "Bob" }];
}
},
footer: { templateUrl: 'admin/footer.html'}
}
})
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html'
})
.
<!-- index.html -->
...
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
...
.
<!-- contacts.html -->
<h1>Contacts Page</h1>
<div ui-view></div>
.
<!-- contacts.list.html -->
<ul>
<li ng-repeat="person in contacts">
<a ng-href="#/contacts/{{person.id}}">{{person.name}}</a>
</li>
</ul>
Yes, this will work. There is a working plunker.
The parent view's $scope (the view, defined in state 'contacts' views as a 'content') and its scope, will be a source for prototypical inheritance.
And that means, that its properties will be available in the child state 'contacts.list', because it is injected into that 'content' view
There is in detail more about it:
How do I share $scope data between states in angularjs ui-router?
To prove, it, we can extend the code snippet above with a list controller and inject some more contacts
...
.state('contacts.list', {
url: '/list',
templateUrl: 'contacts.list.html',
controller: 'listCtrl', // new controller
})
}
])
// we get already initiated contacts... coming from parent view
.controller('listCtrl', ['$scope', function($scope) {
$scope.contacts
.push({ id: 2, name: "from a child" });
}])
Check it here
I'd like to pass a function with to BootstrapUI's popover directive. The attribute is normally a string, but I need to do an AJAX call to supply the attribute to the directive. Currently, the popover displays the function as a string e.g. "showItem(one)" rather than the result of calling the function, e.g. "Item is one". Thanks!
The HTML
<li ng-repeat="item in items"
popover-placement="top"
popover-trigger="mouseenter"
uib-popover="showItem({{item.id}})">
{{item.id}}
</li>
The JS
app.controller("uibController", ["$scope", function ($scope) {
$scope.items = [
{id: "one"},
{id: "two"},
{id: "three"}
];
$scope.showItem = function(item){
$http.get('url').success(function(response){
//data for popover directive
return "Item is " + item.id;
})
};
}]);
Codepen
http://codepen.io/anon/pen/PZQOdY
<div ng-repeat="item in items"
popover-placement="bottom"
popover-trigger="mouseenter"
uib-popover="{{showItem(item)}}">
{{item.id}}
</div>
I've similarly asked this before, but neither solution proved useful.
I currently have a store app built with AngularJS, my problem is, I need to be able to click on one item and open into the "item" view and display only the information with a matching ID from the items array stored in a services.
For example. Item one displayed on store view has an id of 1, and on the store displays the product name etc. When clicked, the item is opened with the item view to display just that product and it's information.
Here's my store Controller:
'use strict';
angular.module('angularStoreApp')
.controller('storeCtrl', function($scope, StoreService){
$scope.items = StoreService.items();
});
Services (Shortened to save spare, there's 12 other items).
'use strict';
angular.module('angularStoreApp')
.service("StoreService", function() {
var items = [ {
itemId: 1,
qty: 0,
stock: 5,
price: 99.00,
name: 'Almond Toe Court Shoes, Patent Black',
category: 'Womens Footerwear'
},
{
qty: 0,
stock: 4,
price: 42.00,
name: 'Suede Shoes, Blue',
category: 'Womens Footerwear'
}];
this.items = function() {
return items;
};
});
Store View
<!-- Start of item iteration -->
<div ng-repeat="item in items">
<!-- Start of item -->
<div class="item">
<div class="item_info">
<img src="http://i.imgur.com/Bn1iB6X.jpg"/>
<div class="item_footer">
<div class="info_text">
<h2>{{item.name| limitTo: 8}}...</h2>
<span>{{item.price | currency}}</span>
</div>
<div class="icon">
<a ng-href="#"><button role="button">More</button></a>
</div>
</div>
</div>
</div>
<!-- End of item -->
</div>
<!-- End of item iteration-->
Item Ctrl
'use strict';
angular.module('angularStoreApp')
.controller('itemCtrl', function ($scope, StoreService) {
$scope.items = StoreService.items();
});
Item View
<div ng-include="'components/navbar/navbar.html'"></div>
<div ng-repreat="item in items">
<div class="item-page-container">
<div class="item-p-img">
{{item.name}}
</div>
<div class="item-p-tab-Container">
<ul>
<li>{{item.name}}</li>
<li></li>
<li></li>
</ul>
</div>
</div>
</div>
Please ask for any more code or for easier view, see my Github Repo
Please be clear on the function, feel free to put into a plunker, my Javascript skills come from Angular, but searching an array like this I haven't done before. I'd need a good solution where I can transfer/learn the logic.
Store View
(not sure about your routing) but would try smt like this:
<a ng-href="/items/{{item.itemID}}"><button role="button">More</button></a>
this should send you to your item view.
In your Item ctrl, if you inject $routeParams you could fetch the id like so:
item_id = $routeParams.id
and then search for your item like so:
items = StoreService.items();
$scope.item = items.indexOf(item_id)
so in your item view remove the ng-repeat and work with the item object.
As I said before, not enterily sure of your project's structures,. but this should get you on your way.
Make your routes like this :
$routeProvider
.when('/items', {
templateUrl: 'yourtemplate-path/items.html',
controller: 'yourItemController'
})
.when('/items/:itemId', {
templateUrl: 'yourtemplate-path/items-detail.html',
controller: 'yourItemDetailsController'
})
Your controller:
For items,
'use strict';
angular.module('angularStoreApp')
.controller('storeCtrl', function($scope, StoreService){
StoreService.items(function(){
$scope.items = data;
});
});
For item detail page,
'use strict';
angular.module('angularStoreApp')
.controller('storeDetailsCtrl', function($scope, $routeParams, StoreService){
StoreService.items({itemId: $routeParams.itemId}, function(){
$scope.item = data;
});
});
and create your new service, which fetches the data based on this id(itemId)
NOTE: I didn't tested the code.
I have a set of tabs in Ionic framework which show a list of movies:
<script id="tabs.html" type="text/ng-template">
<ion-tabs tabs-style="tabs-icon-top" tabs-type="tabs-positive">
<ion-tab title="Movies" icon="ion-film-marker" href="#/tab/movies">
<ion-nav-view name="movies-tab"></ion-nav-view>
</ion-tab>
</ion-tabs>
</script>
<script id="movies.html" type="text/ng-template">
<ion-view title="'Movies'">
<ion-content has-header="true" padding="false">
<div class="list">
<a ng-repeat="item in movies" href="#/tab/movies/{{item.id}}" class="item item-thumbnail-left item-text-wrap">
<img ng-src="{{ item.image }}">
<h2>{{ item.title }}</h2>
<h4>{{ item.desc }}</h4>
</a>
</div>
</ion-content>
</ion-view>
</script>
Each of the items in the list is linked to #/tab/movies/{{item.id}}, for example, #/tab/movies/27. My movies are defined in the controler
.controller('MyCtrl', function($scope) {
$scope.movies = [
{ id: 1, title: '12 Rounds', desc: 'Detective Danny Fisher discovers his girlfriend has been kidnapped by a ex-con tied to Fisher\'s past, and he\'ll have to successfully complete 12 challenges in order to secure her safe release.', image: ''}
];
My pages are routed as below:
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tabs', {
url: "/tab",
abstract: true,
templateUrl: "tabs.html"
})
.state('tabs.movies', {
url: "/movies",
views: {
'movies-tab': {
templateUrl: "movies.html",
controller: 'MyCtrl'
}
}
})
$urlRouterProvider.otherwise("/tab/movies");
})
What I need to do now is when each item on the above list is clicked, it takes it to it's own page, #/tab/movies/{{item.id}}, where I can display things like item.title or item.image, along with a back button to go to the list.
In order to do this, from what I can tell, I need to create a blank ng-template with a placeholder for the information, and then some how pass this information to it when clicked, but I'm not sure how to do this.
In your stateProvider config, you need to add a placeholderFor for the movie id, something like:
.state('tabs.movies', {
url: "/movies/:id",
views: {
'movies-tab': {
templateUrl: "movies.html",
controller: 'MyCtrl'
}
}
})
see https://github.com/angular-ui/ui-router/wiki/URL-Routing#stateparams-service
What are you looking for is probably service/factory where you can store list of movies and then retrieve full list for MyCtrl or just a single movie object for movie page.
angular.module('myAppName')
.factory('MovieService', function () {
return {
MoviesList: [
{movie object}
],
GetAllMovies: function () {
return this.MoviesList;
},
GetMovieById: function (id) {
//iterate MoviesList and return proper movie
}
}
}
that service can be then injected into your controllers
.controller('MyCtrl', function($scope, MoviesService) {
$scope.movies = MoviesService.GetAllMovies();
}
and same goes for a movie view controller:
.controller('ShowMyMovie', function($scope, MoviesService) {
$scope.movie = MoviesService.GetMovieById(//retrieve_id_from_routing_service);
}
then in template for this view you can simply use {{movie.title}} to display informations
Here is the demo I have created for asking this question and the inline code as requested: http://jsfiddle.net/Gncja/1/
<script type='text/ng-template' id='root.html'>
<list template-id='sidebar.templateId' selected-item-id='sidebar.selectedItemId'></list>
</script>
<script type='text/ng-template' id='sidebar.html'>
<ul style='width:100%;' class='nav nav-list bs-docs-sidenav'>
<li ng-repeat='item in data' ng-class="{active:item.id==selectedItemId}">
<a ng-href='#/{{item.id}}'>
<i class=icon-chevron-right></i>
<span ng-bind='item.text'></span>
</a>
</li>
</ul>
</script>
<body ng-app='main'>
<div ng-view></div>
</body>
function RootCtrl($scope, $routeParams){
$scope.sidebar = {
templateId: 'sidebar',
selectedItemId: $routeParams.navItemId
};
}
RootCtrl.$inject = ['$scope','$routeParams'];
angular.module('main', []).
config(['$routeProvider', function($routeProvider) {
$routeProvider.
when('/:navItemId', {
templateUrl: 'root.html',
controller: RootCtrl
}).
otherwise({redirectTo: '/1'});
}]).
directive('list', function(){
return {
restrict: 'E',
replace: true,
scope:{
'templateId': '=',
'selectedItemId':'='
},
template:'<ng-include src="templateUrl"></ng-include>',
controller: function($scope, $element, $attrs){
$scope.templateUrl = $scope.templateId + '.html';
$scope.data = [
{'id':'1', text:'lorem ipsum'},
{'id':'2', text:'dolor sit amet'},
];
}
};
});
This is a small piece of the application I have been working on, but it clearly shows what it is doing. There is the navigation menu in the page, the menu item is a link to the hash that is handled by angular.js routing that initializes the root controller, etc., it's quite tricky to describe, but the code sample clearly shows it.
The problem the entire page content is re-rendered each time when I click the navigation menu item - the routing is stateless and it does know nothing about the previous state. I would like to avoid this by re-using the navigation menu data/template rendering result when a user just navigates between the menu items(or browser history). Is it possible? I am sure that it is, just want to check out whether someone has good ideas. Thanks!
UPDATE:
I have found something that might help me:
http://www.bennadel.com/blog/2420-Mapping-AngularJS-Routes-Onto-URL-Parameters-And-Client-Side-Events.htm
I would put the navigation menu outside ng-view (so it doesn't re-render), but use ng-class in conjunction with location.path() to differentiate the currently selected item. E.g.,
<div ng-controller="navCtrl">
<ul ...>
<li ng-repeat='item in navData' ng-class="{active:isActiveRoute(item.id)}">
...
</ul>
</div>
<div ng-view></div>
Then in navCtrl:
$scope.navData = [
{'id':'1', text:'lorem ipsum'},
{'id':'2', text:'dolor sit amet'},
];
$scope.isActiveRoute = function(route) {
return '/' + route === $location.path();
};
Fiddle