This package is currently in BETA
This is a node package that contains an implementation of the flux pattern using Angular2 and the redux package.
To install this package execute the following command in the console from within your project.
npm install --save flux-angular2
This package is a collection of classes that loosley correspond to each of the items defined in the flux pattern. A short description of each class in this package is below. See the API section for more detailed information on each class.
Page- Ties together all of the different parts of an application. There should only ever be one instance on a page.View- Corresponds to a flux view component.AppView- Controls one or more Views on the page.Reducer- Contains all of the business logic for managing data.PageBuilder- Used on the server side to generate pages.
What follows is an example of an application built using this package.
For my example I will create an application that allows a user to log questions with a subject and body input.
Note that you will need to use a bundle and transpiler package to transpile from ECMAScript 6/TypeScript and
deliver your code to the client which is not shown in the examples.
The first thing to do is define a reducer for my application. For this example the reducer is simply going to save and retrieve data from
memory but normally there would be some kind of communication with a backend service.
Below is code that I've put in a file named questionReducer.js:
import Flux from 'flux-angular2';
export default class QuestionReducer extends Flux.Reducer {
constructor(opts) {
super(opts);
this.mQuestionIdNext = 0;
}
actionAddQuestion(state, action) {
return [...state, {
id: ++this.mQuestionIdNext,
subject: action.subject,
body: action.body
}];
}
}As you can see above the QuestionReducer class code is pretty simple.
The name given to the actionAddQuestion function has significance as it begins with the text action. Any function defined in a Reducer class
that begins with the text action will be called automatically by the dispatcher when the lower camel case version of the text that follows action
is set in type.
For my example this function gets executed whenever a dispatch is sent with the action.type set to addQuestion.
You can also use action types with periods if you want. These get translated into underscores in function names. For eaxmple the action
.my.example.action will map to the function action_my_example_action.
Now that I have my Reducer defined I'm going to define what will be displayed for UI on the page. To do this I'm going to break up the different parts of my
display into a couple of different views.
First up I'll write the view for displaying questions that have been added. This code will be in a file called QuestionListView.js:
import Flux from 'flux-angular2';
@Flux.View.component({
selector: 'QuestionListView',
template: (`<div>
<div *ngFor="let question of state">
<b>{{question.subject}}</b> - {{question.body}}
</div>
</div>`),
inputs: ['state']
})
export default class QuestionListView extends Flux.View {
}As you can see, this class extends Flux.View and uses the Flux.View.Component annotation. Flux.View.component is simply a convenience function
and is equivalent to angular2/core/Component.
Now that I've defined the view for displaying questions I'll define the view for adding questions in a file named QuestionAddView.js:
import Flux from 'flux-angular2';
@Flux.View.component({
selector: 'QuestionAddView',
template: (`<div>
<form>
<input id="questionSubject" [value]="data.subject" (change)="data.subject = $event.target.value" type="text" placeholder="subject" />
<input id="questionBody" [value]="data.body" (change)="data.body = $event.target.value" type="text" placeholder="body" />
<button id="questionAdd" type="button" (click)="handleClick()">Create</button>
</form>
</div>`)
})
export default class QuestionAddView extends Flux.View {
constructor() {
super();
// initialize the local state for this view
this.data.subject = '';
this.data.body = '';
}
handleClick() {
// dispatch an addQuestion event
this.dispatch({
type: 'addQuestion',
subject: this.data.subject,
body: this.data.body
});
// clear out inputs after creating question
this.data.subject = '';
this.data.body = '';
}
}There are a number of things going on in the code above. First, the user input is being stored in the local data object of the view and the data is
tied to the text input boxes through the template.
You'll also notice that the handleClick function is dispatching a message using the dispatch function defined in the View class. The page property is also
defined in Flux.View and references the current singleton instance of the Page class.
Now that I've defined all of the views I'll need I can create a class that will bring them together to be displayed. I'll do this with an AppView I'll define in a file named QuestionAppView.js:
import Flux from 'flux-angular2';
import QuestionListView from './questionListView';
import QuestionAddView from './questionAddView';
import QuestionReducer from './questionReducer';
@Flux.View.component({
selector: 'QuestionAppView',
template: (`<div>
<QuestionAddView></QuestionAddView>
<div>Size: <span id="questionSize">{{state.questions.length}}</span></div>
<QuestionListView [state]="state.questions"></QuestionListView>
</div>`),
directives: [QuestionAddView, QuestionListView]
})
export default class QuestionAppView extends Flux.AppView {
constructor() {
super();
this.questionReducer = new QuestionReducer({ initialState: this.props.questions });
}
reduce(state, action) {
return {
questions: this.questionReducer.reduce(state.questions, action)
};
}
initialState() {
return {
questions: this.questionReducer.initialState()
};
}
}As you can see the QuestionAppView class extends the Flux.AppView class.
In this class I've brought together the QuestionAddView and QuestionListView views and I am displaying the one on top of the other.
This class also expects a questions property that will be used used to set the initial state for a question reducer.
Now that I have all the parts needed for my application I can render it into a page. First I'll use the PageBuilder to generate html that will be rendered on the browser. I will also need to write some code to have my app load when the browser loads the page.
The following is the code needed to generate html for the browser.
import PageBuilder from 'flux-angular2/lib/local/pageBuilder';
import QuestionAppView from './questionAppView';
const pb = new PageBuilder();
pb.scripts = ['<script src="myscripts.js"></script>']; // this would be set with the scripts that load your application
const html = pb.renderToString(QuestionAppView, { questions: [] });The value that is held in the html variable is a string that is the rendered value of the QuestionAppView page which would then be returned from a server call or maybe written out to an html file to ultimately be loaded into a browser.
This is the code that is needed in order to load the application when the page loads. It should be included in the scripts that are rendered by the PageBuilder class in the example above.
import Flux from 'flux-angular2';
import QuestionAppView from './questionAppView';
Flux.Page.load(QuestionsAppView).then(function (page) {
console.log('the page has been loaded');
});The Flux.Page.load function returns a promise that will resolve with the page that has been loaded.
Type: Class
Page objects are created from calls to Page.load which create a new Page instance with the specified View loaded.
Type: Page
This is a static property that holds the singleton instance of the Page for an application. It gets set when the Page is first instantiated so any code that depends on it must be run after the page has been created or within the contructor of a class that extends the Page class. Whenever a new Page is instantiated it will override this property so having multiple Pages in an application may have unpredictible results.
Type: Store
This is a property on the object that will be an instance of a Redux store as defined in the Redux package.
Type: Boolean
This is a property that indicates if the current code being run is within the context of a client browser.
Type: Boolean
This is a property that indicates if the current code being run is in the development context.
Type: String
This is a property that corresponds to the title displayed in the browser. It can be both read from and updated.
Type: Storage
The storage object for the session.
Type: Storage
The storage object for the client.
Type: Function
Navigate to the given url when in the browser context. If not in the browser context this function will have no effect.
Type: String
The url to navigate to.
Type: Function
This is a static function that creates a new page instance with the given view and loads it.
Type: View
The view to load into the page.
Type: Object
The properties for the view that is loaded.
Type: Object
Options for the page.
Type: String
The title for the page.
Type: Function
This function calls the static load method but only when in the browser context. All of the parameters are passed through.
Type: Function
This function is used to manually run a check for any changed state on the page. It should only be used as part of unit testing.
Type: Function
Handle the given error by writing the error to the console.
Type: Error
The error to handle.
Type: Function
Execute an http request. This will return a promise that resolves to the resulting object if the response has a content type of application/json otherwise it will resolve with the completed XMLHttpRequest.
Type: Object
The options for the request.
Type: String
The type of http method. i.e. GET, POST, PUT, etc.
Type: String
The url for the request.
Type: String or Object
The body for the request. If this is a string it is sent as is in the body. If it is an object then it will be stringified and the content type will be set to application/json.
Type: Array
An array of header objects for the request that each must have a key and value parameter.
Type: String
The type of response to return.
Type: Function
This function will be called with the result of a sucessful call and the return from this function will be what is resolved in the promise.
Type: Class
Abstract definition of a View. View classes are not intended to be used directly but rather to be extended by other classes to provide custom logic.
Type: Page
This property is a convenience property and returns the value from Page.current.
Type: Function
This function is called by the framework when the view has been initialized.
Type: Function
This function is called after the view has been initialized and after the page has loaded.
Type: Function
This function is called by the framework when the view has been destroyed.
Type: Function
A convenience property that is the same as calling Page.current.store.dispatch asynchronously.
Type: Function
A convenience property that is the same as calling Page.current.store.dispatch synchronously.
Type: Component
A convenience property that is the same as angular2/core/Component.
Type: Object Default: Empty Object
This property will hold an immutable state object that is passed in from the container of the view and will contain state values for the view.
Type: Object Default: Empty Object
This property will hold an immutable object that contains property values for the view that are in addition to the state values.
Type: Object Default: Empty Object
This property is used to store state that is local to the view.
Type: Class
The AppView class extends the View class and adds additional functionality for sub classes. It is not intended to be used directly but rather to be extended by other classes to provide custom logic.
Type: Function
This function is called by the Redux package whenever an action has been dispatched. It should return the new state based on the action.
Type: Function
This is called by the Redux package to get the initial state when the page is loaded.
Type: Function
This is called by the page after the state has been changed.
Type: Object
The state property gets set to the state from the page store whenver it's updated.
Type: Object
The props property gets set to the value returned from calling page.getProps().
Type: Class
This class is a base class for all Reducer classes. It is not intended to be used directly but rather to be extended by other classes to provide custom logic. Classes that override this class can wire up functions to be called when a given action is dispatched. Any function that has a name that begins with the text action will be wired up to the corresponding action name.
Type: Function
The constructor for reducer classes.
Type: Object
The options for the reducer.
Type: Object
When this is set it will be returned from calls to the initialState function of this class.
Type: Function
This function will create a shallow copy of the source parameter.
Type: Object
The object to clone.
Type: Object
Optional values to copy into the cloned result.
Type: Function
This function will return the value passed into the constructor of this class by default. It can also be overridden by sub classes to provide a different behavior.
Type: Function
A convenience property that is the same as calling Page.current.store.dispatch asynchronously.
Type: Function
A convenience property that is the same as calling Page.current.store.dispatch.
Type: Class
This class is intended to be used on the server to render Pages on the server to be delivered to client browsers.
Type: String or String[]
A property that contains the style sheet tags that should be included in the page.
Type: String or String[]
A property that contains the script tags that should be included in the page.
Type: String Default: /
A property that contains the base url for the page.
Type: Function
This function generates an HTML string that will load the given page when the page is loaded in a browser. In the future this function will generate the rendered output from the page.
Type: View
The view to render.
Type: Object
The properties for the view.
Type: Function
This function is used to help with unit testing of pages. It returns a promies that resolves with the loaded page.
Type: View
The view to load for testing.
Type: Object
The properties for the loaded view.
Type: Object
Values to set for session storage.
Type: Object
Values to set for local storage.
Type: Object or Function
This is used to listen to the state of the view after certain actions have been dispatched and finished being processed. If this is an object then any function on the object that matches the name of an action that has been processed will will be called with an object that includes the current state and the name of the action that was executed. If it is a function then that function will be execute whenever any action has been processed and will be passed the current state and the name of the action that was executed.
Type: Object
When this is set to an object it will override reducing actions. Set a property on this object to the same name as an action.type and it will be used instead of the normal reducing function. The property can be a reducing function or an object which will be merged with the current state.
Type: Object or Function
This is used to override results that would normally come back from a call to Page.request. When it is a function then it must match the signature and return type of the Page.request function as it will be used instead.
When it is an Object then the property names will be matched up with the method and url for calls to the Page.request function. The following table describes how to create mappings with property names:
'GET:http://fake.org/'will be used whenPage.request({ method: 'GET', url: 'http://fake.org' })is called.'GET:*'will be used when any calls to Page.request havemethod: 'GET'and there are no specific property name matches.'*'will be used for any calls to Page.request that don't have a specific property name match or method and wildcard match.
Each property can be any one of the following:
- A function which must match the signature and return type of the Page.request function as it will be used instead.
- An object that has a property named
rejectwhich will be returned with Promise.reject. - An object that has a property named
resolvewhich will be returned with Promise.resolve. - An object with neither reject or resolve properties which will be returned with Promise.resolve.