Displaying the projects list
Now that we can load our projects, we'd like to show them to the user somewhere. For the moment, let's just list them on the homepage.
About States
Xing applications use UI-router to describe the user interface as a tree of states. States are a convenient way to organize both functionality and appearance and styling. The default state tree of a new Xing project looks like this:
You're not tied to this structure at all: you can write your application's states however you like. This is just a set of defaults Xing gives you to get you started. States have a full name created from their position in the tree, separated by dots, for example root.homepage
, or root.inner.sessions
.
We'll talk more about states in a bit, but for now you just need to know that the first "page" you see when viewing Crowdfundr in a browser is the root.homepage.show
state.
We're going to add our list of projects to the user interface for that state.
The homepage state
Open the file frontend/src/app/homepage/homepageStates.js
. This file contains a description of all the states under the 'homepage' part of the tree. We recommend that you group all the code related to a state in a directory with that name. When the number of states gets too big and the file gets too long, you can break some of them out into a subdirectory. For now, we just have the homepage state and the homepage show state, each of which has a specification for its controller (where interactive code for this state lives), its template (HTML for the state), and the user-visible URL for the state.
frontend/src/app/homepage/homepageStates.js
import {State, Resolve} from 'stateInjector';
import {AdminOnlyState, TrackAdminState} from 'stateClasses';
@State('root.homepage')
export class HomepageState extends TrackAdminState {
constructor() {
super();
this.controller = 'HomepageCtrl';
this.controllerAs = 'homepage';
this.templateUrl = 'homepage/homepage.tpl.html';
this.abstract = true;
this.url = 'home';
}
}
@State('root.homepage.show')
export class HomepageShowState {
constructor() {
this.url = '';
this.controller = 'HomepageShowCtrl';
this.controllerAs = 'homepageShow';
this.templateUrl = 'homepage/homepage-show.tpl.html';
}
}
These state descriptors set up our states in a fairly straightforward syntax. The URL entries get concatenated together: the root state has a URL of "/" (you can see this in rootState.js
), root.homepage is 'home', and root.homepage.show is '', so a user visiting root.homepage.show will see '/home' in their browser bar.
The abstract = true
setting in root.homepage
means a user cannot actually visit this state: root.homepage
only exists to be an ancestor of other states. (You might wonder why the state is even there. It's because Xing also has a prebuilt content management module. We didn't use the CMS for this tutorial, but when it is included, there's a root.homepage.edit
state that allows side administrators to modify the content of the homepage.)
Finally, both states have associated controllers and templates.
Loading the projects
We want our frontend to fetch the list of projects from the backend whenever a user visits the root.homepage.show
state. There are a number of ways we could do that, for now we'll create a Resolve
function: a function that must execute successfully for the application to enter that state, and which makes its return value available to controllers for this state and any sub-states.
Change the homepage show state entry to look like this:
@State('root.homepage.show')
export class HomepageShowState {
constructor() {
this.url = '';
this.controller = 'HomepageShowCtrl';
this.controllerAs = 'homepageShow';
this.templateUrl = 'homepage/homepage-show.tpl.html';
}
@Resolve("resources")
projects(resources){
return resources.projects().load();
}
}
The @Resolve
function uses the resources
object - the same one we were working on with resources.js
. It tells Angular to fetch a resource named 'projects' based on the URL provided by the back-end. Because the function itself is named 'projects', it will create an object also called 'projects' and make it available to the controller for this state. (Again, it's a convenient coincidence that it's called projects
in both cases. So let's review which is which: the @Resolve
function called projects
creates a projects
object we can refer to in the controller. The other function of the same name in resources.**projects()**.load()
refers to the HTTP JSON resource of that name we set up in resources.js
. Clear? great! Still confusing? Don't worry about it for now, it'll make more sense with experience.)
Loading the projects over the network
That's all we need to load the projects list whenever a user visits the homepage.
We haven't displayed them yet, so if you fire up rake develop
you'll see the same homepage as before. But if you're feeling intrepid, open the network tab of your browser's developer tools, and reload the page. You should see a whole bunch of requests to the backend, including GET /resources
and GET /projects
, the JSON requests that happen because of that resources.projects().load()
command.
Getting our projects into the controller
Now that we've loaded the projects, displaying them takes two steps. First, they must be stored in the controller so that they are accessible to our HTML template.
Open the controller file. You'll see the following:
frontend/src/app/homepage/homepageControllers.js
import {Controller} from 'a1atscript';
@Controller('HomepageShowCtrl', [])
export class HomepageShowController {
constructor() {}
}
@Controller('HomepageCtrl', [])
export class HomepageController {
constructor() {}
}
Change HomepageShowController
to the following:
@Controller('HomepageShowCtrl', ['projects'])
export class HomepageShowController {
constructor(projects) {
this.projects = projects;
}
}
Let's look at the changees we made here.
First, we added the ['projects']
argument to the annotation. This specifies that the projects object is a dependency to be injected into this controller. This is the same projects
object created by our @Resolve
function back in the state descriptor. (We could inject other objects here as well, like various angular tools, or other resolves from other ancestor states, etc.) If you're not familiar with dependency injection in AngularJS, we strongly recommend you spend some time learning about it: you'll use it everywhere in Xing applications.
All our dependencies get passed to the controller's constructor function. Here we only do one thing with it: we store it as a property of the controller. That's what will let us refer to it in the template.
Displaying the projects on the page
The file frontend/src/app/homepage/homepage-show.tpl.html
contains the HTML template for the root.homepage.show state. We're just going to make a table with one row for each project in our projects list. While we're at it, let's update the headline of our homepage to reflect the name of our application, Crowdfundr. Open that file and replace the contents with this:
frontend/src/app/homepage/homepage-show.tpl.html
<h1>Crowdfundr</h1>
<p>Below you can see the list of projects available to be funded!</p>
<table id='projects'>
<tr>
<th>Project Name</th>
</tr>
<tr ng-repeat="project in homepageShow.projects">
<td>{{project.name}}</td>
</tr>
</table>
That's it!
If we haven't led you astray, you should be able to run the application now and see the list of projects on your homepage. They'll have random names as generated by your sample data script in the backend
part of the app, so yours should be similar but not exactly like this screenshot.
They're a little ugly and unstyled at the moment, so let's take care of that next.