ember-arcgis-pages-block-renderer
v0.0.19
Published
The default blueprint for ember-cli addons.
Downloads
22
Readme
ember-arcgis-pages-block-renderer
Usage
Create a project using ember-cli. If you have an existing ember-cli project, skip this step.
$ ember new my-new-app
Add ember-redux and ember-arcgis-pages to the project:
$ ember install ember-redux
$ ember install ember-arcgis-pages-block-renderer
There are three reducers that need to be added to your redux store's reducers
so the blocks
, resourceData
, and resources
state will be tracked in your
application state. These reducers are accessible via an export from an instance-initializer
(see below) so they can be used at any level reducer easily.
Application Data Reducers
By default, ember-redux pulls the store's reducers from app/reducers/index.js
. Here's an
example that adds the reducers to the main reducer file. Both blocks
and resourceData
are reducers that return application data that will be saved on the server.
Save those under an appItem
reducer that combines them into a single reducer.
// app/reducers/index.js
import { combineReducers } from 'redux';
import { getAppBlockManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-block-manager';
import { getAppResourceManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-resource-manager';
const AppBlockManager = getAppBlockManager();
const AppResourceManager = getAppResourceManager();
export default combineReducers({
appItem: combineReducers({
blocks: AppBlockManager.storeReducer,
resources: AppResourceManager.resourceDataReducer,
}),
});
Since the app can add the reducers any place within the application state, you'll need
to tell the AppResourceManager
where to get the resource data from application state
(with redux, all the state will live in the application state object and not tracked separately
in our resource and block managers).
You can do this by setting the getResourceDataFromState
method on the AppResourceManager
.
This is just a simple function that should take the current state object from store.getState()
and return the resourceData
object within the state.
AppResourceManager.set('getResourceDataFromState', state => {
return state.appItem.resources;
});
Connecting the blocks
data will come later when you create a connected component.
Nesting all the application data that gets saved to the server allows you to watch for changes only on that one branch of the application state object and automatically save after a change occurs.
You can also extend this simple appItem
to make it easier to load and populate all
the application item reducers at once with an initial state.
// app/reducers/index.js
import { combineReducers } from 'redux';
import { getAppBlockManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-block-manager';
import { getAppResourceManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-resource-manager';
const AppBlockManager = getAppBlockManager();
const AppResourceManager = getAppResourceManager();
AppResourceManager.set('getResourceDataFromState', state => {
return state.appItem.resources;
});
export default combineReducers({
appItem(state = {}, action) {
switch (action.type) {
case 'APP_ITEM_RECIEVED': {
return action.payload;
}
default: {
return combineReducers({
blocks: AppBlockManager.storeReducer,
resources: AppResourceManager.resourceDataReducer,
})(state, action);
}
}
}
});
After you receive the initial data from our server, you can dispatch the action below to set the initial state of the app.
dispatch({
type: 'APP_ITEM_RECIEVED',
payload: {
blocks: [...],
resources: {...},
}
})
Application State reducers
Along with the reducers that set application state, there is another reducer that
returns the resources
state for the app. While the resourceData
is the minimum
required data to save a resource with the app, the resources
includes the full
resource object that gets created at runtime as well as its state and actions that
can be called the change that resource. You'll need to add that to the top level
of the app state, below the appItem
reducer.
// app/reducers/index.js
import { combineReducers } from 'redux';
import { getAppBlockManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-block-manager';
import { getAppResourceManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-resource-manager';
const AppBlockManager = getAppBlockManager();
const AppResourceManager = getAppResourceManager();
AppResourceManager.set('getResourceDataFromState', state => {
return state.appItem.resources;
});
export default combineReducers({
appItem(state = {}, action) {
switch (action.type) {
case 'APP_ITEM_RECIEVED': {
return action.payload;
}
default: {
return combineReducers({
blocks: AppBlockManager.storeReducer,
resources: AppResourceManager.resourceDataReducer,
})(state, action);
}
}
},
resources: AppResourceManager.resourceReducer
});
Just like you did for the resourceData
, you'll also need to tell the AppResourceManager
where to get it's loaded resources from.
AppResourceManager.set('getResourcesFromState', state => {
return state.resources;
});
// app/reducers/index.js
import { combineReducers } from 'redux';
import { getAppBlockManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-block-manager';
import { getAppResourceManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-resource-manager';
const AppBlockManager = getAppBlockManager();
const AppResourceManager = getAppResourceManager();
AppResourceManager.set('getResourceDataFromState', state => {
return state.appItem.resources;
});
AppResourceManager.set('getResourcesFromState', state => {
return state.resources;
});
export default combineReducers({
appItem(state = {}, action) {
switch (action.type) {
case 'APP_ITEM_RECIEVED': {
return action.payload;
}
default: {
return combineReducers({
blocks: AppBlockManager.storeReducer,
resources: AppResourceManager.resourceDataReducer,
})(state, action);
}
}
},
resources: AppResourceManager.resourceReducer
});
Add Redux Middleware
The AppResourceManager
also relies on a special middleware method to be added
to the store. This allows it to easily track changes to the resourceData
even
for unknown actions that it does not generate. This is why you can dispatch the APP_ITEM_RECIEVED
action you used above and have your resources
get populated.
Ember-redux provides a way to define custom middleware for the store by exporting
an array of middleware from app/middleware/index.js
. Just like you did for
the AppResourceManager
reducers, you can reference the required middleware
with a global variable:
// app/middleware/index.js
import thunk from 'redux-thunk';
import { getAppResourceManager } from 'ember-arcgis-pages-block-renderer/instance-initializers/app-resource-manager';
const AppResourceManager = getAppResourceManager();
const resourceDataMiddleware = AppResourceManager.resourceDataMiddleware;
// Adding back thunk that is included by default.
const middleware = [thunk, resourceDataMiddleware];
export default middleware;
Create Connected Component
In order to start rendering your blocks
, you need to create a new connected component
using ember-redux's connect
class.
// app/components/connected-component/component.js
import { connect } from 'ember-redux';
import Ember from 'ember';
const stateToComputed = state => {
return {
blocks: state.appItem.blocks,
resources: state.resources,
};
};
const ConnectedComponent = Ember.Component.extend({
redux: Ember.inject.service(),
});
export default connect(stateToComputed)(ConnectedComponent);
The code snippet above will add blocks
and resources
as computed properties on the
Ember component. Anytime their values are updated in the redux application state, the
their properties will be updated in the Ember component.
Now add the AppBlockManager
service to this connected-component to manager
our top-level blocks.
// app/components/connected-component/component.js
import { connect } from 'ember-redux';
import Ember from 'ember';
const stateToComputed = state => {
return {
blocks: state.appItem.blocks,
resources: state.resources,
};
};
const ConnectedComponent = Ember.Component.extend({
appBlockManager: Ember.inject.service(),
redux: Ember.inject.service(),
});
export default connect(stateToComputed)(ConnectedComponent);
Next you'll need to add a computed property (nestedBlocksOptions
) that will render your top-level
nested-blocks
. Rendering the top-level of blocks
in the nested blocks will then render
all blocks
nested in those blocks
.
// app/components/connected-component/component.js
import { connect } from 'ember-redux';
import Ember from 'ember';
const stateToComputed = state => {
return {
blocks: state.appItem.blocks,
resources: state.resources,
};
};
const ConnectedComponent = Ember.Component.extend({
appBlockManager: Ember.inject.service(),
redux: Ember.inject.service(),
nestedLayout: {
type: 'flex',
orientation: 'vertical',
},
nestedBlocksOptions: Ember.computed('blocks', 'redux', 'resources', function() {
return {
appDispatch: this.get('redux.store.dispatch'),
blockManager: this.get('appBlockManager'),
resources: this.get('resources'),
blocks: this.get('blocks'),
layoutOptions: this.get('nestedLayout'),
}
}),
});
export default connect(stateToComputed)(ConnectedComponent);
The layoutOptions
property under the nestedBlocksOptions
is an optional property
that allows you to set a layout structure that will be used by the top-level nested-blocks
component. In your sample, we're just setting it to a vertical flex layout.
Finally, you'll add the nested-blocks
component helper to our template file.
// app/components/connected-component/template.hasBlocks
{{nested-blocks blockManager=blockManager blocks=blocks layoutOptions=nestedBlocksLayout resources=resources}}
Now go add some data by dispatching a APP_ITEM_RECIEVED
action and watch
your app come to life!
Contributing
Installation
git clone <repository-url>
this repositorycd ember-arcgis-pages/packages/ember-arcgis-pages-block-renderer
yarn install
Running
ember serve
- Visit your app at http://localhost:4200.
Running Tests
yarn test
(Runsember try:each
to test your addon against multiple Ember versions)ember test
ember test --server
Building
ember build
For more information on using ember-cli, visit https://ember-cli.com/.