In the angular2-meteor tutorial step3, we use a zone method.
Code:
import { Component } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Parties } from '../../both/collections/parties.collection';
...some lines skipped...
template
})
export class AppComponent {
parties: Observable<any[]>;
constructor() {
this.parties = Parties.find({}).zone();
}
}
What exactly does Parties.find({}).zone() do?
According to the article you linked:
.zone() is a wrapper for the regular Observable that connects the collections changes to the view using the Component's Zone.
For more information, here is an article that goes in depth into what Zones are in Angular 2.
Essentially, Parties.find({}).zone() will .find() all data in the Parties collection and connect that data to the components Zone. Since the mongo collection is a reactive data source, this should allow the component to reactively update its data as the data is updated in the Mongo collection.
Related
I have been trying to make my Angular application to an Angular Universal application which gets the data from Firebase Firestore.
I have been following the guide below:
https://fireship.io/lessons/angular-universal-firebase/
The initial compilation and things work all fine and even when getting the data from Firestore with the help of valueChanges() method does the trick.
However if I change valueChanges() to get() and adjust the required things on the DocumentSnapshot it seems that the universal doesn't wait for the data to be fetch and gets rendered.
TEMPLATE FILE
<div class="desc" *ngIf="(task | async) as ts">
{{ts.description}}
</div>
WORKING CODE
export class Task {
colRef: AngularFirestoreCollection;
constructor(private db: AngularFirestore) {
this.colRef = this.db.collection(this.COL_NAME);
}
ngOnInit() {
this.task = this.colRef.doc(id).valueChanges();
}
}
NOT WORKING CODE
export class Task {
colRef: AngularFirestoreCollection;
constructor(private db: AngularFirestore) {
this.colRef = this.db.collection(this.COL_NAME);
}
ngOnInit() {
this.task = this.colRef.doc(id).get().pipe(map(docSnapshot => docSnapshot.data()));
}
}
valuechanges() is a method inside the package angularFire and it returns an Observable while get() method is inside the firestore package and it returns a Promise. Therefore you cannot use pipe with get()
Angularfire:
https://github.com/angular/angularfire/blob/master/docs/firestore/documents.md#valuechanges
get():
https://firebase.google.com/docs/firestore/query-data/get-data#get_a_document
I am starting on a new project and I want to keep my code as structured as possible. I was planning on using the flux pattern for the front-end but it feels like the event driven process that flux follows, goes against the grain of the reactionary way that meteor handles data and view updates.
Here is an example of what one of my stores might look like using flux and meteor
import { EventEmitter } from 'events'
import appDispatcher from '../Dispatcher'
import Meteor from 'meteor/meteor'
class TaskStore extends EventEmitter {
constructor() {
super();
this.tasks = [];
Meteor.subscribe('tasks');
}
addTask(task) {
Meteor.addTask(task)
this.tasks = Meteor.findAll();
}
getTasks() {
return this.tasks
}
}
const taskStore = new TaskStore();
appDispatcher.register((payload) => {
switch (payload.actionName) {
case 'CLICK':
taskStore.addTask(payload.newItem.action);
taskStore.emit('ADD_TASK');
break;
default:
}
});
export default taskStore
It's pretty straight forward, the store responds to the dispatcher by adding a task to the mongo database, then updates the local model with the data from the database and emits a change event. The view would respond by calling the getTasks() method and updating the state.
This works, but it doesn't feel very reactionary. I need a separate method exposed for finding all the tasks for example, where as in the documentation for react views that meteor provides, they have their own special function that wraps components and updates the props of the component whenever the data changes
export default createContainer(() => {
Meteor.subscribe('tasks');
return {
tasks: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),
incompleteCount: Tasks.find({ checked: { $ne: true } }).count()
})
This seems to be the way that meteor was designed. Views react to data changes and update in real time across all platforms, and I'm just not certain if my implementation of the flux pattern is the best way to stay true to that design, or if I should even bother trying to stay true to that design at all.
As a disclaimer, I'm still extremely new to both the flux pattern, and the Meteor framework.
Is this possible?
I'm using redux store in an IoC environment and want to add middleware to the store after it is created.
e.g.:
class MyApp {
store = createStore(...);
}
let app = new MyApp();
// later on
import thunk from 'redux-thunk';
app.store.addEnhancer(thunk);
I have created a function to do this. If redux think this is valuable, I can do a PR.
This is code that tailored to my module. The actual one add to PR will look a bit different.
addMiddleware(middleware: Middleware) {
const middlewareAPI: MiddlewareAPI<any> = {
getState: this.getState,
dispatch: (action) => this.dispatch(action)
};
this.dispatch = compose(middleware(middlewareAPI))(this.dispatch);
}
I was able to use the code from above from unional but I needed to change it slightly to get it working with 2018 redux.
constructor(myMiddleware: myMiddleware) {
this.addMiddleware(myMiddleware)
}
addMiddleware(middleware: Middleware) {
this.redux.dispatch = compose(applyMiddleware(middleware))(this.redux.dispatch);
}
All:
I am pretty new to Redux, when I follow its Reddit API example, there is one code snippet confuse me so much:
In AsyncApp.js, there is:
componentDidMount() {
const { dispatch, selectedSubreddit } = this.props
dispatch(fetchPostsIfNeeded(selectedSubreddit))
}
I wonder where the dispatch and selectedSubreddit get bind to this.props?
Thanks
That example is using the connect() function from react-redux to inject certain parts of the Redux state and the store's dispatch() function as props in that component. See the 'Usage With React' part of the Redux docs for more information.
For example:
App.js:
export class App extends Component {
//...
}
function mapStateToProps(state) {
const { selectedReddit, postsByReddit } = state
const {
isFetching,
lastUpdated,
items: posts
} = postsByReddit[selectedReddit] || {
isFetching: true,
items: []
}
return {
selectedReddit,
posts,
isFetching,
lastUpdated
}
}
export default connect(mapStateToProps)(App)
The connect() function here is taking the mapStateToProps() function above to inject the appropriate parts of the Redux state as props in the <App /> component. The keys of the object returned by mapStateToProps() correspond to the names of the props injected, and the corresponding values are the values of those injected props.
connect() can also take a second argument, matchDispatchToProps(), which can be used to inject specific action dispatch functions as props in your component. Whether or not you supply any arguments to connect(), it will inject your Redux store's dispatch() function as a prop called dispatch.
These connected container components receive state updates from the store, so when your Redux state changes, the connected container components will receive new props accordingly.
I have a component that is reliant upon an asynchronously fetched object. There are also child components in the template that rely on some data from this same object.
The problem I am having seems to be a classic race condition, but I'm not familiar enough with Angular 2 to understand the solution.
Take this for instance:
export class SampleComponent {
constructor(service: SomeService) {
this.service = service;
this._loadData();
}
private _loadData() {
this.service.getData().subscribe(data => this.data = data);
}
}
But in the template, I have child components to display certain parts of this.data:
<taglist tags="data?.tags"></taglist>
Now the Component for taglist looks something like:
#Component({
selector: 'taglist',
directives: [NgFor],
inputs: ['tags'],
template: `<span *ngFor="#tag of tags">{{ tag }}</span>`
})
export class TagList {
public tags: Array<string> = [];
constructor() {
//
}
}
Because tags input is received from an async loaded dataset, its not present when the Tag Component is initialized. What can I do so that when this.data load is finished, the sub components that use it will automatically access the newly loaded data?
Thank you for any insight you may be able to provide me!
Implement ngOnChanges() (https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html)
#Component({
selector: 'taglist',
inputs: ['tags'],
template: `<span *ngFor="let tag of tags">{{ tag }}</span>`
})
export class TagList {
public tags: Array<string> = [];
constructor() {
//
}
ngOnChanges(changes: {[propName: string]: SimpleChange}) {
console.log('ngOnChanges - tags = ' + changes['tags'].currentValue);
}
}
For angular to handle the binding also use
<taglist [tags]="data?.tags"></taglist> or <taglist tags="{{data?.tags}}"></taglist>
otherwise it's a simple string to attribute assignment Angular doesn't handle.
This is the usual use case for the built-in async pipe, that is meant to be used to easily consume observables.
Try the following, first create a getter than returns directly the service layer observable:
get data(): Observable {
return this.service.getData();
}
Then you can consume this observable directly in the template using the async pipe:
<span *ngFor="let tag of data | async">{{ tag }}</span>