I'm using a Redux Store within an Angular app to hold application state.
But in my view I want to assign this Redux store state to an Observable$ variable that the Angular view can bind to and update as state changes.
import { of } from 'rxjs';
.....
// Using RxJS operator to cast Redux store state to an Observable
public viewData: any[] = [];
this.viewData$ = of(this.store.getState());
But when I try to bind viewData to the view in an ngFor I get an error stating the viewData is not in the correct format of an array ..ngFor didn't expect [Object object]
<div *ngFor="let data of viewData| async">
<div>{{ data }}</div>
</div>
Question
How can you cast Redux store state to an Angular Observable using RxJs operators?
I know if I was to use NgRx Store I could just wrap it in a store.select like viewData = this.store.select(store.getState); but Redux doesn't have an equivalent select method to cast state as an Observable.
See: https://ngrx.io/api/store/select
viewData is not in the correct format of an array ..ngFor didn't expect [Object object] shows that you are trying to iterate over an object with an array method. Check that your returned data is of type array, if not convert the result to an array.
Related
I have a component that receives data from an emit function and I wish to push object's onto an array. The trouble is when I push object onto array the object is empty?? Here is the code :
<script setup>
let formDataHistory = ref([]);
// Method to be called when there is an emiterUIUpdate event emiited
// from form-modal.vue #param(data) is the form data sent from the
// form submission via the event bus. We will then send this data back
// down to child display-scrap component via a prop.
const updateUI = (data) => {
console.log(data);
formDataHistory.value.push(data);
console.log(formDataHistory);
};
</script>
And a snapshot in devtools after pushing item onto array:
formdataHistory's first element is an empty object after the push action. Any help on proper way to mutate an array is most welcome.
Two suggestions:
Always use const on reactive variables: const formDataHistory = ref([]);. The reference itself should never change, only the value.
Try formDataHistory.value = [...formDataHistory.value, data];. It might be that refs only update if the content is replaced instead of mutated.
My component needs data from the API.
It lets the store dispatches an action, but before dispatching this action some other data needs to be obtained first. Thus, dispatched action is dependent on data that needs to be fetched before the actual action is called and the fetched data is passed to the action.
this.d1 = this.store.pipe(select(selectData1));
this.d2 = this.store.pipe(select(selectData2));
// ...
// dispatch the actions below to and pass identifier (id) in order to get its object from the API and put the fetched data in store.
this.store.dispatch(action1({data1Id}));
this.store.dispatch(action1({data2Id}));
// dispatch the actual action and pass this.d1 and this.d2
this.store.dispatch(targetAction({this.d1, this.d2}));
What is the best practise for the situation described above?
One way you could do this is to subscribe to this.d1 and this.d2 and then dispatch the target action when they emit:
combineLatest([this.d1 this.d2])
.subscribe(([d1, d2]) => {
if (d1 && d2) {
this.store.dispatch(targetAction(d1, d2));
}
})
However, it might be better to use an effect for this:
https://ngrx.io/guide/effects
I need to get Redux store data but not always and different data for each component instantiate.
How can I use useSelector with Conditional statement?
The component should get data from store only when some child components are rendered and also different data each time depending on the child component.
useSelector takes in a callback that has access to the redux state as an argument
so assuming you control whether the child component is rendered in a boolean flag called isComponentRendered, and want to select data when it is true and else nothing, you could try the following:
const data = useSelector(state => {
if(state.isComponentRendered) {
return state.data
}
return null;
})
I want to use Meteor.userId() inside of a viewmodel using Mobx for state management.
i.e
#observable isLoggedIn = Boolean(Meteor.userId());
The issue is, I receive this error when I attempt to do this
"Error running template:
`Error: Meteor.userId can only be invoked in method calls or publications."`
I don't believe createContainer is applicable here as it's designed for React components and this is just a standard es6 JS class.
I probably could use createContainer on my main App component and just set the loggedIn observable in an ApplicationModel or something of that sort. But that just feels hacky.
Any ideas or solutions would be greatly appreciated!
Thanks!
Attempt to wrap the entire Meteo class in an persistent observable array:
import {observable, toJS} from 'mobx';
import {persist} from 'mobx-persist';
#persist #observable _meteo = Meteo
You can then call the state when required by the component:
const {_meteo } = props.store
_meteo.userId()
..else review the toJS() data!
I used vue-resource to fetch data from firebase. basically when I loop through each object under the orders directory, I match the userId of that object, find the object under the users directory, and save it on my local array as a nested object (userData) along with the retrieved orders objects. here's my code:
//retrieve objects from orders
this.$http.get('https://nots-76611.firebaseio.com/Orders.json').then(function(data){
return data.json();
}).then(function(data){
var ordersArray = [];
for (let key in data){
data[key].id = key;
data[key].measurementsArray = Object.entries(data[key].measurements).sort();
//retrieve a specific user based on the userId of each orders object
this.$http.get('https://nots-76611.firebaseio.com/Users/' + data[key].userId + '.json').then(function(userdata){
return userdata.json();
}).then(function(userdata){
data[key].userData = userdata; //store the object
});
ordersArray.push(data[key]); //pass the object along the userData
}
this.orders = ordersArray;
console.log(this.orders);
});
the object structured shown in the console is perfectly fine:
but when I try to, access the nested object in the dom via {{ order.userData.Address }}:
<tr v-for="order in orders"
<dialog class="mdl-dialog" ref="userDialog">
<h4 class="mdl-dialog__title">Customer's Information</h4>
<div class="mdl-dialog__content">
{{ order.userData.Address }}
</div>
<div class="mdl-dialog__actions">
<button class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored mdl-js-ripple-effect" v-on:click="closeUser(ndx)">
OK
</button>
</div>
</dialog>
</tr>
it says Error in render function: "TypeError: Cannot read property 'Address' of undefined". I don't understand it because I can easily retrieve the properties in the measurements object (which is also a nested object)
did I messed up in the retrieval process? or in the DOM rendering?
EDIT:
I tried using {{ order }} instead of {{ order.userData.Address }} and it seems like the userData object was not stored in each of the orders object
Also, I noticed something strange about how the userData object is shown differently from the measurements object which is originally stored there along with the parent object:
There are two primary issues here.
First, the data is retrieved asynchronously which means, it does not exist when the component is first rendered. In the template you use
order.userData.Address
There is a point in time when there are orders but there is no userData, because the data has not been retrieved yet. That being the case, Vue attempts to render the orders, and tries to render order.userData.Address, but there is no userData. That's why you get the error, "Cannot read property 'Address' of undefined". In order to fix that, you should use a guard to make sure to only try to render Address when it is available.
{{orders.userData && orders.userData.Address}}
That will prevent the error.
There is a secondary error that you probably have not yet noticed. userData is not reactive. The reason for this is because Vue cannot detect when properties are added to an object after that object has been added to data. The code is setting userData like so:
data[key].userData = userdata;
and this occurs after the orders array has been added to the Vue because it is performed in an asynchronous call. Instead, you should use,
this.$set(data[key], 'userData', userdata)