Skip to content

Instantly share code, notes, and snippets.

@neborn
Last active February 8, 2021 16:12
Show Gist options
  • Save neborn/5233c2f6f8b140a009d4af7f8e12235f to your computer and use it in GitHub Desktop.
Save neborn/5233c2f6f8b140a009d4af7f8e12235f to your computer and use it in GitHub Desktop.
Employee Search Example - Complete
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
export default class ActionBarComponent extends Component {
@service employeeSearch;
}
import Component from '@glimmer/component';
export default class AddEmployeesToGroupModal extends Component {
}
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
export default class extends Component {
@service employeeSearch;
_addEmployeesToGroupFlowController;
_employeeSearchQuery;
get addEmployeesToGroupFlowController() {
return this._addEmployeesToGroupFlowController;
}
get employeeSearchQuery() {
return this._employeeSearchQuery;
}
constructor() {
super(...arguments);
this._employeeSearchQuery = this.employeeSearch.createEmployeeSearchQuery();
this._addEmployeesToGroupFlowController =
this.employeeSearch.createAddEmployeesToGroupFlowController();
}
willDestroy() {
this.addEmployeesToGroupFlowController.cleanup();
}
}
import EmberRouter from '@ember/routing/router';
import config from './config/environment';
const Router = EmberRouter.extend({
location: 'none',
rootURL: config.rootURL
});
Router.map(function() {
this.route('employee-search', { path: '/employee-search' });
});
export default Router;
import Service from '@ember/service';
import { tracked } from '@glimmer/tracking';
export default class ConfigurationService extends Service {
@tracked currentEmployerId = 'employer1';
@tracked currentEmployerName = 'Test Employer';
}
import Service, { inject as service } from '@ember/service';
import Object from '@ember/object';
import { action } from '@ember/object';
import EmployeeSearchQuery from '../view-models/employee-search-query';
import AddEmployeesToGroupFlow from '../view-models/add-employees-to-group-flow';
import AddEmployeesToGroupFlowController from '../view-models/add-employees-to-group-flow-controller';
import Evented from '@ember/object/evented';
export const Events = {
ADD_EMPLOYEES_TO_GROUP: 'ADD_EMPLOYEES_TO_GROUP'
};
class EventBus extends Object.extend(Evented) {}
export default class EmployeeSearchService extends Service {
@service configuration;
_eventBus = EventBus.create();
get eventBus() {
return this._eventBus;
}
@action
performEmployeeSearch(params) {
// use configuration service to construct the API request
const results = [];
return new Promise(
resolve => setTimeout(() => resolve(results), 500)
);
}
@action
performEmployeeTypeaheadSearch(params) {
// use configuration service to construct the API request
const results = [];
return new Promise(
resolve => setTimeout(() => resolve(results), 500)
);
}
@action
performAddEmployeesToGroup(params) {
// use configuration service to construct the API request
return new Promise(resolve => setTimeout(resolve, 500));
}
@action
createEmployeeSearchQuery() {
return new EmployeeSearchQuery({
searchService: this
});
}
@action
createAddEmployeesToGroupFlow(group, onSuccess) {
return new AddEmployeesToGroupFlow({
group,
onSuccess,
searchService: this
});
}
@action
createAddEmployeesToGroupFlowController() {
return new AddEmployeesToGroupFlowController({
searchService: this
});
}
@action
startAddEmployeesToGroupFlow(group) {
this.eventBus.trigger(Events.ADD_EMPLOYEES_TO_GROUP, { group });
}
}
<nav>
<ul>
<li><LinkTo @route="employee-search">Employee Search</LinkTo></li>
</ul>
</nav>
<main>
{{outlet}}
</main>
<ul>
<li>
<button {{on "click" (fn this.employeeSearch.startAddEmployeesToGroupFlow @groupId)}}>
Add Employees To Group
</button>
</li>
</ul>
<section>
<h2>Add Employees to Group</h2>
<button type="button" {{on "click" @onClose}}>Close</button>
</section>
<h1>Employee Search</h1>
<section>
<h2>Group View</h2>
<ActionBar @groupId="group1"/>
</section>
{{#if this.addEmployeesToGroupFlowController.modalIsOpen}}
<AddEmployeesToGroupModal
@flowModel={{this.addEmployeesToGroupFlowController.addEmployeesToGroupFlow}}
@onClose={{this.addEmployeesToGroupFlowController.closeModal}}/>
{{/if}}
{
"version": "0.17.1",
"EmberENV": {
"FEATURES": {},
"_TEMPLATE_ONLY_GLIMMER_COMPONENTS": false,
"_APPLICATION_TEMPLATE_WRAPPER": true,
"_JQUERY_INTEGRATION": true
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js",
"ember": "3.18.1",
"ember-template-compiler": "3.18.1",
"ember-testing": "3.18.1"
},
"addons": {
"@glimmer/component": "1.0.0",
"ember-concurrency": "1.3.0",
"tracked-built-ins": "1.0.2",
"ember-lifeline": "6.0.1"
}
}
import { Events } from '../services/employee-search';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
export default class AddEmployeesToGroupFlowController {
_searchService;
@tracked
_addEmployeesToGroupFlow = null;
get addEmployeesToGroupFlow() {
return this._addEmployeesToGroupFlow;
}
get modalIsOpen() {
return !!this.addEmployeesToGroupFlow;
}
constructor({
searchService
}) {
this._searchService = searchService;
this._searchService.eventBus.on(
Events.ADD_EMPLOYEES_TO_GROUP,
this.openModal
);
}
@action
cleanup() {
this._searchService.eventBus.off(
Events.ADD_EMPLOYEES_TO_GROUP,
this,
this.openModal
);
}
@action
openModal(event) {
if (this.addEmployeesToGroupFlow) {
return;
}
const {
group
} = event;
this._addEmployeesToGroupFlow = this._searchService.createAddEmployeesToGroupFlow({
group,
onSuccess: this.closeModal
})
}
@action
closeModal() {
this._addEmployeesToGroupFlow = null;
}
}
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { task } from 'ember-concurrency';
import { TrackedSet } from 'tracked-built-ins';
export default class AddEmployeesToGroupFlow {
_group;
_onSuccess;
_searchService;
@tracked
_employeeList = new TrackedSet();
@tracked
_typeaheadResults;
@tracked
_typeaheadSearchQuery = '';
get employeeList() {
return this._employeeList;
}
get group() {
return this._group;
}
get typeaheadResults() {
return this._typaheadResults;
}
get typeaheadSearchQuery() {
}
constructor({
group,
onSuccess,
searchService
}) {
this._group = group;
this._onSuccess = onSuccess;
this._searchService = searchService;
}
@action
updateTypeaheadSearchQuery() {
// update value
// trigger search
// throttle request
}
@action
selectEmployee(employee) {
this._employeeList.delete(employee);
}
@action
unselectEmployee(employee) {
this._employeeList.delete(employee);
}
@action
submitEmployees() {
// error handling
}
}
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { task } from 'ember-concurrency';
const PAGE_SIZE = 20;
const SortOrder = {
ASCENDING: 'ASCENDING',
DESCENDING: 'DESCENDING'
};
export default class EmployeeSearchQuery {
_searchService;
_pagesLoaded = 0;
@tracked
_resultsList;
@tracked
_searchString = '';
@tracked
_sortField;
@tracked
_sortOrder;
get resultsList() {
return this._resultsList;
}
get searchString() {
return this._searchString;
}
get sortField() {
return this._sortField;
}
get sortOrder() {
return this._sortOrder;
}
constructor({ searchService }) {
this._searchService = searchService;
}
@action
updateSearchString(value) {
this._searchString = value;
}
@action
triggerSearch() {
this._searchTask.perform();
}
@action
updateSortField(fieldName) {
if (this._fieldName === fieldName) {
return;
}
this._fieldName = fieldName;
this._sortOrder = SortOrder.ASCENDING;
this._newSearchTask.perform();
}
@action
toggleSortOrder() {
this._sortOrder = this._sortOrder === SortOrder.ASCENDING ?
SortOrder.DESCENDING : SortOrder.ASCENDING;
this._newSearchTask.perform();
}
@action
fetchAdditionalPage() {
this._fetchPageTask.perform();
}
@(task(function * (pageNumber = 0) {
const {
searchString,
sortField,
sortOrder
} = this;
const results = yield this._searchService.performSearchQuery({
pageNumber,
pageSize: PAGE_SIZE,
searchString,
sortField,
sortOrder
});
return results;
}).restartable()) _searchTask;
@(task(function * () {
if (this._searchTask.isRunning()) {
return;
}
const results = yield this._searchTask.perform(this._pagesLoaded + 1);
this._pagesLoaded += 1;
this._resultsList = this._resultsList.concat(results);
}).drop()) _fetchPageTask;
@(task(function * () {
this._pagesLoaded = 0;
this._resultsList = yield this._searchTask.perform();
}).restartable()) _newSearchTask;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment