End of tutorial

This commit is contained in:
Thomas Schwery 2016-11-17 08:18:48 +01:00
parent 42aeae609d
commit 6c5dd4f352
38 changed files with 489 additions and 1 deletions

View file

@ -0,0 +1,5 @@
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
namespace: 'api'
});

View file

@ -0,0 +1,4 @@
import Ember from 'ember';
export default Ember.Component.extend({
});

View file

@ -0,0 +1,20 @@
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['list-filter'],
value: '',
init() {
this._super(...arguments);
this.get('filter')('').then((results) => this.set('results', results));
},
actions: {
handleFilterEntry() {
let filterInputValue = this.get('value');
let filterAction = this.get('filter');
filterAction(filterInputValue).then((filterResults) => this.set('results', filterResults));
}
}
});

13
app/controllers/items.js Normal file
View file

@ -0,0 +1,13 @@
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
filterByName(param) {
if (param !== '') {
return this.get('store').query('item', { name: param });
} else {
return this.get('store').findAll('item');
}
}
}
});

View file

@ -0,0 +1,3 @@
import ItemsController from '../items';
export default ItemsController;

11
app/models/item.js Normal file
View file

@ -0,0 +1,11 @@
import DS from 'ember-data';
export default DS.Model.extend({
title: DS.attr(),
owner: DS.attr(),
location: DS.attr(),
type: DS.attr(),
image: DS.attr(),
bedrooms: DS.attr(),
description: DS.attr()
});

View file

@ -7,6 +7,10 @@ const Router = Ember.Router.extend({
});
Router.map(function() {
this.route('about');
this.route('items', function() {
this.route('show', {path: '/:item_id'});
});
});
export default Router;

4
app/routes/about.js Normal file
View file

@ -0,0 +1,4 @@
import Ember from 'ember';
export default Ember.Route.extend({
});

8
app/routes/index.js Normal file
View file

@ -0,0 +1,8 @@
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel() {
this._super(...arguments);
this.replaceWith('items');
}
});

4
app/routes/items.js Normal file
View file

@ -0,0 +1,4 @@
import Ember from 'ember';
export default Ember.Route.extend({
});

View file

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
model() {
return this.get('store').findAll('item');
}
});

7
app/routes/items/show.js Normal file
View file

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Route.extend({
model(params) {
return this.get('store').findRecord('item', params.item_id);
}
});

9
app/templates/about.hbs Normal file
View file

@ -0,0 +1,9 @@
<div class="jumbo">
<div class="right tomster"></div>
<h2>About Super Rentals</h2>
<p>
The Super Rentals website is a delightful project created to explore Ember.
By building a property rental site, we can simultaneously imagine traveling
AND building Ember applications.
</p>
</div>

View file

@ -0,0 +1,17 @@
<div class="container">
<div class="menu">
{{#link-to 'index'}}
<h1 class="left">
<em>StockPile</em>
</h1>
{{/link-to}}
<div class="left links">
{{#link-to 'about'}}
About
{{/link-to}}
</div>
</div>
<div class="body">
{{outlet}}
</div>
</div>

View file

@ -0,0 +1,15 @@
<article class="listing">
<h3 class="title name">{{#link-to "items.show" item}}{{item.title}}{{/link-to}}</h3>
<div class="detail owner">
<span>Owner:</span> {{item.owner}}
</div>
<div class="detail type">
<span>Type:</span> {{item.type}}
</div>
<div class="detail location">
<span>Location:</span> {{item.location}}
</div>
<div class="detail bedrooms">
<span>Number of bedrooms:</span> {{item.bedrooms}}
</div>
</article>

View file

@ -0,0 +1,2 @@
{{input value=value key-up=(action 'handleFilterEntry') class="light" placeholder="Filter By Name"}}
{{yield results}}

1
app/templates/index.hbs Normal file
View file

@ -0,0 +1 @@
{{outlet}}

1
app/templates/items.hbs Normal file
View file

@ -0,0 +1 @@
{{outlet}}

View file

@ -0,0 +1,10 @@
{{#list-filter
filter=(action 'filterByName') as |items| }}
<ul class="results">
{{#each items as |itemUnit|}}
<li>{{item-listing item=itemUnit}}</li>
{{/each}}
</ul>
{{/list-filter}}
{{outlet}}

View file

@ -0,0 +1,22 @@
<div class="jumbo show-listing">
<h2 class="title">{{model.title}}</h2>
<div class="right detail-section">
<div class="detail owner">
<strong>Owner:</strong> {{model.owner}}
</div>
<div class="detail">
<strong>Type:</strong> {{model.type}}
</div>
<div class="detail">
<strong>Location:</strong> {{model.city}}
</div>
<div class="detail">
<strong>Number of bedrooms:</strong> {{model.bedrooms}}
</div>
<p class="description">{{model.description}}</p>
</div>
<img src="{{model.image}}" class="rental-pic">
</div>
{{outlet}}

View file

@ -2,6 +2,7 @@
"name": "ember-quickstart",
"dependencies": {
"ember": "~2.9.0",
"ember-cli-shims": "0.1.3"
"ember-cli-shims": "0.1.3",
"bootstrap": "~3.3.5"
}
}

View file

@ -5,6 +5,9 @@ var EmberApp = require('ember-cli/lib/broccoli/ember-app');
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
// Add options here
'ember-bootstrap': {
'importBootstrapTheme': true
}
});
// Use `app.import` to add additional libraries to the generated

56
mirage/config.js Normal file
View file

@ -0,0 +1,56 @@
export default function() {
this.namespace = '/api';
let items = [{
type: 'items',
id: 'grand-old-mansion',
attributes: {
title: 'Grand Old Mansion',
owner: 'Veruca Salt',
city: 'San Francisco',
type: 'Estate',
bedrooms: 15,
image: 'https://upload.wikimedia.org/wikipedia/commons/c/cb/Crane_estate_(5).jpg',
description: "This grand old mansion sits on over 100 acres of rolling hills and dense redwood forests."
}
}, {
type: 'items',
id: 'urban-living',
attributes: {
title: 'Urban Living',
owner: 'Mike Teavee',
city: 'Seattle',
type: 'Condo',
bedrooms: 1,
image: 'https://upload.wikimedia.org/wikipedia/commons/0/0e/Alfonso_13_Highrise_Tegucigalpa.jpg',
description: "A commuters dream. This rental is within walking distance of 2 bus stops and the Metro."
}
}, {
type: 'items',
id: 'downtown-charm',
attributes: {
title: 'Downtown Charm',
owner: 'Violet Beauregarde',
city: 'Portland',
type: 'Apartment',
bedrooms: 3,
image: 'https://upload.wikimedia.org/wikipedia/commons/f/f7/Wheeldon_Apartment_Building_-_Portland_Oregon.jpg',
description: "Convenience is at your doorstep with this charming downtown rental. Great restaurants and active night life are within a few feet."
}
}];
this.get('/items', function(db, request) {
if(request.queryParams.name !== undefined) {
let filteredItems = items.filter(function(i) {
return i.attributes.title.toLowerCase().indexOf(request.queryParams.name.toLowerCase()) !== -1;
});
return { data: filteredItems };
} else {
return { data: items };
}
});
this.get('/items/:id', function (db, request) {
return { data: items.find((item) => request.params.id === item.id) };
});
}

View file

@ -0,0 +1,11 @@
export default function(/* server */) {
/*
Seed your development database using your factories.
This data will not be loaded in your tests.
Make sure to define a factory for each model you want to create.
*/
// server.createList('post', 10);
}

View file

@ -0,0 +1,4 @@
import { JSONAPISerializer } from 'ember-cli-mirage';
export default JSONAPISerializer.extend({
});

View file

@ -21,6 +21,7 @@
"devDependencies": {
"broccoli-asset-rev": "^2.4.5",
"ember-ajax": "^2.4.1",
"ember-bootstrap": "0.11.2",
"ember-cli": "2.9.1",
"ember-cli-app-version": "^2.0.0",
"ember-cli-babel": "^5.1.7",
@ -29,6 +30,7 @@
"ember-cli-htmlbars-inline-precompile": "^0.3.3",
"ember-cli-inject-live-reload": "^1.4.1",
"ember-cli-jshint": "^1.0.4",
"ember-cli-mirage": "0.2.4",
"ember-cli-qunit": "^3.0.1",
"ember-cli-release": "^0.2.9",
"ember-cli-sri": "^2.1.0",

View file

@ -0,0 +1,47 @@
import { test } from 'qunit';
import moduleForAcceptance from 'ember-quickstart/tests/helpers/module-for-acceptance';
moduleForAcceptance('Acceptance | list items');
test('visiting /items', function(assert) {
visit('/items');
andThen(function() {
assert.equal(currentURL(), '/items');
});
});
test('should link to information about the company.', function (assert) {
visit('/items');
click('a:contains("About")');
andThen(function () {
assert.equal(currentURL(), '/about', 'should navigate to about');
});
});
test('should list available rentals.', function (assert) {
visit('/items');
andThen(function() {
assert.equal(find('.listing').length, 3, 'should see 3 listings');
});
});
test('should filter the list of rentals by city.', function (assert) {
visit('/items');
fillIn('.list-filter input', 'urban');
keyEvent('.list-filter input', 'keyup', 69);
andThen(function () {
assert.equal(find('.listing').length, 1, 'should show 1 listing');
assert.equal(find('.listing .name:contains("Urban Living")').length, 1, 'should contain 1 listing with name Urban Living');
});
});
test('should show details for a specific rental', function (assert) {
visit('/items');
click('a:contains("Grand Old Mansion")');
andThen(function() {
assert.equal(currentURL(), '/items/grand-old-mansion', 'should navigate to show route');
assert.equal(find('.show-listing h2').text(), "Grand Old Mansion", 'should list rental title');
assert.equal(find('.description').length, 1, 'should list a description of the property');
});
});

View file

@ -0,0 +1,21 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
moduleForComponent('item-listing', 'Integration | Component | item listing', {
integration: true
});
test('it renders', function(assert) {
this.render(hbs`{{item-listing}}`);
assert.equal(this.$().text().trim(), '');
// Template block usage:
this.render(hbs`
{{#item-listing}}
template block text
{{/item-listing}}
`);
assert.equal(this.$().text().trim(), 'template block text');
});

View file

@ -0,0 +1,73 @@
import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import wait from 'ember-test-helpers/wait';
import RSVP from 'rsvp';
moduleForComponent('list-filter', 'Integration | Component | list filter', {
integration: true
});
const ITEMS = [{title: 'Grand Old Mansion'}, {title: 'Urban Living'}, {title: 'Downtown Charm'}];
const FILTERED_ITEMS = [{title: 'Grand Old Mansion'}];
test('should initially load all listings', function (assert) {
// we want our actions to return promises, since they are potentially fetching data asynchronously
this.on('filterByName', (val) => {
if (val === '') {
return RSVP.resolve(ITEMS);
} else {
return RSVP.resolve(FILTERED_ITEMS);
}
});
// with an integration test, you can set up and use your component in the same way your application
// will use it.
this.render(hbs`
{{#list-filter filter=(action 'filterByName') as |results|}}
<ul>
{{#each results as |item|}}
<li class="name">
{{item.title}}
</li>
{{/each}}
</ul>
{{/list-filter}}
`);
// the wait function will return a promise that will wait for all promises
// and xhr requests to resolve before running the contents of the then block.
return wait().then(() => {
assert.equal(this.$('.name').length, 3);
assert.equal(this.$('.name').first().text().trim(), 'Grand Old Mansion');
});
});
test('should update with matching listings', function (assert) {
this.on('filterByName', (val) => {
if (val === '') {
return RSVP.resolve(ITEMS);
} else {
return RSVP.resolve(FILTERED_ITEMS);
}
});
this.render(hbs`
{{#list-filter filter=(action 'filterByName') as |results|}}
<ul>
{{#each results as |item|}}
<li class="name">
{{item.title}}
</li>
{{/each}}
</ul>
{{/list-filter}}
`);
// The keyup event here should invoke an action that will cause the list to be filtered
this.$('.list-filter input').val('Grand').keyup();
return wait().then(() => {
assert.equal(this.$('.name').length, 1);
assert.equal(this.$('.name').text().trim(), 'Grand Old Mansion');
});
});

View file

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('adapter:application', 'Unit | Adapter | application', {
// Specify the other units that are required for this test.
// needs: ['serializer:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let adapter = this.subject();
assert.ok(adapter);
});

View file

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:items', 'Unit | Controller | items', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let controller = this.subject();
assert.ok(controller);
});

View file

@ -0,0 +1,12 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('controller:items/index', 'Unit | Controller | items/index', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
// Replace this with your real tests.
test('it exists', function(assert) {
let controller = this.subject();
assert.ok(controller);
});

View file

@ -0,0 +1,12 @@
import { moduleForModel, test } from 'ember-qunit';
moduleForModel('item', 'Unit | Model | item', {
// Specify the other units that are required for this test.
needs: []
});
test('it exists', function(assert) {
let model = this.subject();
// let store = this.store();
assert.ok(!!model);
});

View file

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:about', 'Unit | Route | about', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View file

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:index', 'Unit | Route | index', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View file

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:items', 'Unit | Route | items', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View file

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:items/index', 'Unit | Route | items/index', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});

View file

@ -0,0 +1,11 @@
import { moduleFor, test } from 'ember-qunit';
moduleFor('route:items/show', 'Unit | Route | items/show', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
let route = this.subject();
assert.ok(route);
});