ember-arcgis-pages-block-renderer

0.0.19 • Public • Published

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 repository
  • cd ember-arcgis-pages/packages/ember-arcgis-pages-block-renderer
  • yarn install

Running

Running Tests

  • yarn test (Runs ember 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/.

Readme

Keywords

Package Sidebar

Install

npm i ember-arcgis-pages-block-renderer

Weekly Downloads

0

Version

0.0.19

License

Apache-2.0

Last publish

Collaborators

  • skitterm
  • ssylvia