0

I have a simple app with two states. First displays list of items, second is a detailed view of the chosen item. I use a $stateParams as a filter to select specific item in the detailed view. I wonder if there is a better way?

http://plnkr.co/edit/wJeyApEWquqskQot5dS7?p=preview

.state('list', { url: "/list", templateUrl: "list.html", controller: "ListController as list" }) .state('details', { url: "/details:itemId", templateUrl: "details.html", controller: "ListController as list" }) .controller("ListController", function($stateParams){ this.items = [ { itemId: 1, name: "item1", color: "red", size: "big" }, ... ]; this.id = $stateParams; }) <h1>list</h1> <ul> <li ng-repeat="item in list.items"> <a ui-sref="details({itemId:item.itemId})">{{item.name}}</a> </li> </ul> <h1>details</h1> <ul ng-repeat="detail in list.items | filter: list.id"> <li >{{detail.name}}</li> <li >{{detail.color}}</li> <li >{{detail.size}}</li> </ul> 

2 Answers 2

3

A good way, in my opinion, can be reached using nested states and an url structure more user-friendly (or rest-like) than yours...

For example, if you have a state called posts, the single view of the posts collection should be posts/:id or posts/slug, slugs are more readable than id...

Nesting states, also, can help you to keep trace of the current hierarchy.

So, this could be a little refactor of your app:

  1. First of all, wipe away from controllers all the business logic that isn't view-needed

//instead of this.items directly in controller, we can create a factory function ItemsServiceFactory($http, $q) { var items = []; return function getItem(id) { if(!id) { return $q.when(items); } for(var i = 0, len = items.length; i < len; i++) { if(id === items[i].id) { return $q.when(items[i]); } } return $q.reject(null); }; } angular .module('test', []) .factory('ItemsService', ['$http', '$q', ItemsServiceFactory]) ;

doing this you can make your controller light and keep it focused only in what the view needs.

  1. We can reorganize states around their scopes:

function StatesConfig($stateProvider) { var parent = { name: 'items', url: 'items/', abstract: true }; var list = { name: 'items.list', url: '', resolve: { items: ['ItemsService', function(ItemsService) { return ItemsService(); }] }, views: { "main": { templateUrl: 'items/list.html', controller: 'ItemsListCtrl as items' } } }; var single = { name: 'items.single', url: ':id/', resolve: { item: ['ItemsService', '$stateParams', '$state', function(ItemsService, $stateParams, $state) { return ItemsService($stateParams.id).catch(function() { //Item not found, redirect: return $state.go('errors.404', {}, { inherit: false }); }); }] }, views: { "main": { templateUrl: 'items/single.html', controller: 'ItemsSingleCtrl as item' } } }; $stateProvider .state(parent) .state(list) .state(single) ; } angular .module('test') .config(['$stateProvider'], StatesConfig) .controller('ItemsListCtrl', function(items) { angular.extend(this, items); }) .controller('ItemsSingleCtrl', function(item) { angular.extend(this, item); }) ;

Sign up to request clarification or add additional context in comments.

Comments

0

Yes, there is a better way. You shouldn't use a loop to display a single item. The detail state should have separate controller from the list state. It should get the item based on the ID passed as state parameter, and expose this item (and this item only) to the view.

In a real app, this would consist in making a GET request to the backend to get that item. In your simple app, where the list of items is hard-coded in the code, the list could be stored in a service, shared y the two controllers. The list controller would get the whole list from the service. The detail controller would ask the service to return the item with the given ID.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.