Angularfire: database.object returns object with added $key!

Created on 9 Aug 2016  路  13Comments  路  Source: angular/angularfire

When I call
this.af.database.object("/users/" + uid)
it returns the user object with the key uid but it adds a $key property to the returned user object.
Which means I cannot use the returned object for manipulation and saving back to the db.

Is this a bug?
If not, I can't see what the motivation could have been to always add the key that you need anyways in order to get the object. It doesn't add any information. On the contrary, I have to add an additional map call to delete the $key property in order to convert the returned object into what it should have been in the first place.

Most helpful comment

For example:

this.item = af.database.object('/item', { preserveSnapshot: true });
this.item.subscribe(snapshot => {
  this.values= snapshot.val();
});

in your HTML file you have:
<input [(ngModel)]=values.name">

when you want to save your information you update item

function save() {
  this.item.update(this.values);
}

This only pushes the changes to your database when you update it, not by changing the the input field.

All 13 comments

this.item = af.database.object('/item', { preserveSnapshot: true });
this.item.subscribe(snapshot => {
  console.log(snapshot.val())
});

will return all values, but without the $key value

How would you bind the returned object properties to a [(ngModel)] afterwards?
The documentation only shows how to use interpolation syntax such as {{(foo|async)?.bar}}

This part is driving me crazy...

Thx!

For example:

this.item = af.database.object('/item', { preserveSnapshot: true });
this.item.subscribe(snapshot => {
  this.values= snapshot.val();
});

in your HTML file you have:
<input [(ngModel)]=values.name">

when you want to save your information you update item

function save() {
  this.item.update(this.values);
}

This only pushes the changes to your database when you update it, not by changing the the input field.

Thanks, cre8, that works.
So does my solution to remove the superfluous $key property in a map.

However, the point remains:
The current behavior is counter-intuitive and requires an additional and superfluous step.
When I call database.object I want the object and nothing else.
I want the object in a state that I can change it and save it back.
That should be the default.
If I want additional snapshot info then - and only then - would I want to add the preserveSnapshot: true option.

@cre8 Doesn't work for me...

TypeError: Cannot read property 'title' of undefined

Component

export class EditNewsComponent {
  news: FirebaseObjectObservable<any>;
  values: any;

  constructor(
    private route: ActivatedRoute,
    private af: AngularFire) { 
      this.news = af.database.object('news/'+this.route.snapshot.params['id'],{ preserveSnapshot: true });
      this.news.subscribe(snapshot =>{
        this.values = snapshot.val();
        console.warn(this.values);
      });
    }

Warn prints the correct object, but somehow it's not available for the view...

<input type="text" #title [(ngModel)]="values.title">

The weirdest part is that {{values | json}} prints the whole object correctly...

Any clues?

Thx

Try this, cerealexx:
<input type="text" #title [(ngModel)]="values?.title">
The elvis operator ('?') should fix your issue.

It seems you can't do that...

EXCEPTION: Error: Uncaught (in promise): Template parse errors: Parser Error: The '?.' operator cannot be used in the assignment at column 16 in [values?.title=$event]

Ah, yes, I overlooked that you use two-way binding.

Either avoid two-way binding altogether or assign a dummy object to value.
The issue is the asynchronous nature of the firebase call. The data arrives after the view has been initialized. Two-way binding also prevents you from using the async pipe.
Two-way binding is often more trouble than convenient.

The problem is that I'm going for a news editor. So whenever the user wants to edit some news, having inputs with <input value="{{(foo|async)?.bar}}"> solves the issue because they still see the values that they are editing on initialization.

But, I'm also using ng2-ckeditor so the user can write html inside the news.content instead of a regular textarea. And... ng2-ckeditor overlooks the value attr and only takes a [(ngModel)] to correctly print the value of its content. So implementing this with AngularFire2 so far has been a living nightmare...

Can you be more specific with the dummy object option? I'm also new to Angular 2...

Thx!

When you declare values in your class, try this:
values: any = {title: ""};
That should get it over the initial hurdle that values is undefined. The firebase data will arrive soon after, with little to no humanly detectable delay.

Oh god... It worked! Thanks!

Hey every body all solutions not working for angular version 2.3.1 :

"foo.val" is not a function

@davideast Just FYI; this issue that @cerealexx had, really should be added to your documentation. I ran into the same issue just trying to do a simple CRUD app - display a list of people with af.database.list(), tap a person's name and pass their $key to the next page, on that page grab the af.database.object() using the $key passed along, and bind the values to a form for editing - but it's evidently just not that simple. Thanks @harald for a solution.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

avanderbergh picture avanderbergh  路  3Comments

aucevica picture aucevica  路  3Comments

StephenFluin picture StephenFluin  路  3Comments

harrylincoln picture harrylincoln  路  3Comments

cre8 picture cre8  路  3Comments