According to the angularfire2 documentation the following can be done when you wan't to update a item in a list :
const items = af.database.list('/items');
// to get a key, check the Example app below
items.update('key-of-some-data', { size: newSize });
But is is possible to update an item in the list, without having to specify key:values for the object like this?
items.update('key-of-some-data', item);
In angularfire this is possible to do like this:
<li ng-repeat="item in list">
<input type="text" ng-model="item.title" ng-change="list.$save(item)" />
</li>
Thanks for taking your time to read this question :)
The implementation of update looks like this:
update(item: FirebaseOperation, value: Object): firebase.Promise<void> {
return this._checkOperationCases(item, {
stringCase: () => this.$ref.ref.child(<string>item).update(value),
firebaseCase: () => (<firebase.database.Reference>item).update(value),
snapshotCase: () => (<firebase.database.DataSnapshot>item).ref.update(value),
unwrappedSnapshotCase: () => this.$ref.ref.child((<AFUnwrappedDataSnapshot>item).$key).update(value)
});
}
So it's possible to call update in the following ways:
Using a string key and a value:
const items = af.database.list('/items');
items.update('key-of-some-data', { size: newSize });
Using a Firebase ref and a value:
const items = af.database.list('/items');
const ref = items.$ref.ref;
items.update(ref.child('key-of-some-data'), { size: newSize });
Using a Firebase snapshot and a value:
const items = af.database.list('/items', { preserveSnapshot: true });
items.subscribe(list => {
const snapshot = list[0];
items.update(snapshot, { size: newSize });
});
Using an unwrapped list item and a value:
const items = af.database.list('/items');
items.subscribe(list => {
const item = list[0];
items.update(item, { size: newSize });
});
(The snippets above that call subscribe are only to illustrate that the snapshot and unwrapped items are the list observable's emitted values. Using subscribe like this to perform an update makes no sense.)
AngularFire2 is currently undergoing some refactoring and rearranging in preparation for a release candidate. If you have a use case for which none of the above options is suitable, now is the time to speak up. The discussion is here. However, for something this specific, you should create a new issue.
Related
I'm pretty new on Vue JS and dbs like Firebase and I'm having some trouble with what I'd like to do.
Here is the idea: I have repeated components ('LaundryMachine.vue') which each have a boolean property (computed property) available.
I want users to be able to change the state of these components. The change is sent to the Firestore DB and the app needs to read the data from the DB.
I have been able link the VueJS code and the DB and edit the DB data within the app. I have however not been successful at reading the data from the DB.
More precisely, I have only been able to read the data from one or several documents of the DB and log at the console. But i can't manage to link the data to properties.
Here is what I have on the LaundryMachine.vue:
<template>
<div class="about">
<h2>Machine {{ this.machineNum }}</h2>
<img src="../assets/washing_machine.png" /><br />
<v-btn v-bind:color="buttonColor" v-on:click="changeAvailability">
{{ this.availability }}</v-btn
>
<v-btn v-bind:color="buttonColor" v-on:click="editState">Edit state</v-btn>
</div>
</template>
<script>
import db from './firebaseInit.js';
export default {
name: 'LaundryMachine',
props: {
name: String,
machineNum: Number,
residenceNum: Number,
},
methods: {
editState: function(event) {
console.log('available:' + this.available);
// Emit to parent component which succesfully edits the fields in the DB
this.$emit('update-availability', this.machineNum, this.residenceNum);
// This part is just about logging out on the console the db documents data which works fine
db.collection('Machines')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id + doc.data() + doc.data().available);
});
});
}
},
computed: {
available: function() {
// This is where I want to link my computed property to the db document field but which doesn't work. If i print {{this.available}}, i'll get an undefined
let ref = db
.collection('Machines')
.doc('machine' + this.residenceNum + this.machineNum);
ref.get().then(snapshot => {
if (snapshot.exists) {
ref.get().then(snapshot => {
return snapshot.data().available;
});
} else {
return true;
}
});
},
availability: function() {
if (this.available) {
return 'disponible';
} else {
return 'indisponible';
}
},
buttonColor: function() {
if (this.available) {
return 'primary';
} else {
return 'red';
}
}
}
};
</script>
This is honestly really frustrating as I'm able to log on the console the fields of the data base but I can't link them to my "available" computed property. I've looked through a lot of posts and firestore doc and I can't find something which works.
Thanks in advance !
The component:
https://ant.design/components/tree-select/
There is no example with the loadData option.
async getChildren(node) {
console.log(node);
let r = $.get("/tree", {id: node.value})
console.log(await r); // request works
return r;
}
With this code I just see the tree loading and nothing happens. Not an error, but the child nodes are not appended to the tree.
If I don't return a Promise, I get a huge error and a blank page.
this is an example of loadData function:
onLoadData = (treeNode) => {
console.log('load data...');
return new Promise((resolve) => {
setTimeout(() => {
const treeData = [...this.state.treeData];
getNewTreeData(treeData, treeNode.props.eventKey, generateTreeNodes(treeNode), 2);
this.setState({ treeData });
resolve();
}, 500);
});
}
You can find it here with more in-deep examples
To make it clearer:
TreeData is an Array of TreeNode
source
antd tree select uses rc-tree because antd is built on top of rc-components you can see the source
For lazy load you need to manipulate the treeNode, what above snippet
does is: everytime loaded new data it will be a treeNode object, and
will call the onLoadData() callback where you provide the code what
to do with that node. (the sample just append to the state's treeData
variable]
I started using ngrx/entity package, where I can manage store by adapter. There is addOne method I'd like to use, but it adds item to the end of collection. I wanna add one at the beginning. Could you please help me with that? How to add item at the beginning with EntityAdapter.
How I create entity adapter:
export const adapter: EntityAdapter<AssetTreeNode> = createEntityAdapter({
selectId: (model: AssetTreeNode) => model.Id
});
Reducer looks like that:
export function reducer(state: AssetListState = initialState, action: AssetListAction) {
switch (action.type) {
(...)
case ASSET_LIST_ADD_ITEM:
let assetToAdd: AssetTreeNode = Object.assign({} as AssetTreeNode,
action.payload.asset,
{ Id: action.payload.createdAssetId });
return adapter.addOne(assetToAdd, state); <--- I wanna add here at the end.
(...)
default:
return state;
}
}
There is no proper way provided by #ngrx/entity team. One of the answer mentions to use sort-comparator. But i believe using sort-comparator is not the right way to go. Suppose there is two click actions and in one action we need to append item below and in other action on top. here we will run into the same problem again.
I had run into the same issue and my solution to the problem is to reconstruct the list when we want the item on top of the list.
To add at the top of entity list
const { selectAll } = myAdapter.getSelectors();
...
...
on(MyActions.addItem, (state, { item }) =>{
return myAdapter.setAll([item ,...selectAll(state)], { ...state})
}),
To add at the bottom of entity list
on(MyActions.addItem, (state, { item}) =>{
return myAdapter.addOne(item, state)
}),
The only way to change this behavior would be to use the sortComparer when you create the adapter - docs.
export const adapter: EntityAdapter<User> = createEntityAdapter<User>({
sortComparer: (a: User, b: User) => a.name.localeCompare(b.name),
});
Maybe you could place the item at the begining and replace the list
on(addAsset, (state, { payload }) => {
const currentList = Object.values(state.entities);
const newList = [payload, ...currentList];
return adapter.setAll(newList, state);
});
I want to grab data from firebase and then put all gather data into a for loop for ionic content to generate buttons.
my data is
I want to be able to grab all the phones that are currently in the database and create buttons --Don't remember the right code as typing this.
so being able to grab all available phones in Ionic Framework 3.
As I see, You saved all children in root.
so retrive all data,
let user_data= [];
firebase.database().ref().on('value', (snapshot) => {
let result = snapshot.value();
for(let k in result){ //"k" provides key Id of each object
user_data.push({
id : k,
name : result[k].name,
phone : result[k].phone,
});
}
});
To show data on the front view, you can listed all phones like this way
<div *ngFor="let phone of user_data" >
{{phone.phone}}
</div>
hope this answer will help you.
Try this instead
data = [];
constructor(af: AngularFireDatabase...) {
this.data = [];
}
this.af.object('/FirebaseData/')
.valueChanges()
.subscribe(data => {
let arr = Object.keys(data);
this.data = [];
for (var i = 0; i < arr.length; i++) {
const object2 = Object.assign({ Key: arr[i] }, data[arr[i]]);
this.data.push(object2);
}
Yo! I'm using Redux and Normalizr. The API I'm working with sends down objects that look like this:
{
name: 'Foo',
type: 'ABCD-EFGH-IJKL-MNOP'
}
or like this
{
name: 'Foo2',
children: [
'ABCD-EFGH-IJKL-MNOP',
'QRST-UVWX-YZAB-CDEF'
]
}
I want to be able to asynchronously fetch those related entities (type and children) when the above objects are accessed from the state (in mapStateToProps). Unfortunately, this does not seem to mesh with the Redux way as mapStateToProps is not the right place to call actions. Is there an obvious solution to this case that I'm overlooking (other than pre-fetching all of my data)?
Not sure that I have correctly understood your use-case, but if you want to fetch data, one simple common way is to trigger it from a React component:
var Component = React.createClass({
componentDidMount: function() {
if (!this.props.myObject) {
dispatch(actions.loadObject(this.props.myObjectId));
}
},
render: function() {
const heading = this.props.myObject ?
'My object name is ' + this.props.myObject.name
: 'No object loaded';
return (
<div>
{heading}
</div>
);
},
});
Given the "myObjectId" prop, the component triggers the "myObject" fetching after mounting.
Another common way would be to fetch the data, if it's not already here, from a Redux async action creator (see Redux's doc for more details about this pattern):
// sync action creator:
const FETCH_OBJECT_SUCCESS = 'FETCH_OBJECT_SUCCESS';
function fetchObjectSuccess(objectId, myObject) {
return {
type: FETCH_OBJECT_SUCCESS,
objectId,
myObject,
};
}
// async action creator:
function fetchObject(objectId) {
return (dispatch, getState) => {
const currentAppState = getState();
if (!currentAppState.allObjects[objectId]) {
// fetches the object if not already present in app state:
return fetch('some_url_.../' + objectId)
.then(myObject => (
dispatch(fetchObjectSuccess(objectId, myObject))
));
} else {
return Promise.resolve(); // nothing to wait for
}
};
}