polymerfire create uid path in firebase database for new loggedin user - firebase

I am new to polymer and firebase. I could not find good documentation on firebase-document element for polymer.
My question is when a new user is logged in, how can I create an object in firebase database with his uid?
Also, please point me to some good examples on how to save and query the data using polymerfire firebase elements.

To begin with, you'll need firebase-auth to make authentication magic happen. For example, if you want to do a Google login, you need:
Assign these properties to firebase-auth element
<firebase-auth
id="auth"
user="{{user}}"
provider="google"
signed-in="{{signedIn}}">
</firebase-auth>
Assign click/tap event listener to button
<button on-tap="signInWithGoogle">Sign In With Google</button>
Call OAuth authentication method on button click/tap
signInWithGoogle: function() {
this.$.auth.signInWithPopup();
}
Now, here's how you save user information to firebase database:
Assign these properties to firebase-document element (mind that path property is irrelevant at this point as it will be overridden with save() method)
<firebase-document
id="authDocument"
data="{{userData}}">
</firebase-document>
Extend signInWithGoogle() method
signInWithGoogle: function() {
// Chain then() method to the Promise
this.$.auth.signInWithPopup().then(function(response) {
// Update userData object with user information
// returned with signInWithPopup()
this.userData.displayName = response.user.displayName;
this.userData.email = response.user.email;
// Save userData object under /users/$uid. Note that at this
// point authDocument's path is now changed to /users/$uid
this.$.authDocument.save('/users', response.user.uid);
// And don't forget to bind this
}.bind(this));
}
Hope that helped. Here's a full implementation in single element. Mind that firebase-app element is in parent.
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/polymerfire/firebase-auth.html">
<link rel="import" href="../bower_components/polymerfire/firebase-document.html">
<dom-module id="my-account">
<template>
<style>
:host {
display: block;
}
</style>
<firebase-auth
id="auth"
user="{{user}}"
provider="google"
signed-in="{{signedIn}}">
</firebase-auth>
<firebase-document
id="authDocument"
data="{{userData}}">
</firebase-document>
<div hidden$="[[user]]">
<button on-tap="signInWithGoogle">Sign In With Google</button>
</div>
<div hidden$="[[!user]]">
<table>
<tr><th>photoURL</th> <td><img src="[[user.photoURL]]" alt=""></td></tr>
<tr><th>uid</th> <td>[[user.uid]]</td></tr>
<tr><th>displayName</th> <td>[[user.displayName]]</td></tr>
<tr><th>email</th> <td>[[user.email]]</td></tr>
<tr><th>emailVerified</th> <td>[[user.emailVerified]]</td></tr>
</table>
<button on-tap="signOut">Sign Out</button>
</div>
</template>
<script>
Polymer({
is: 'my-account',
signInWithGoogle: function() {
this.$.auth.signInWithPopup().then(function(response) {
this.userData.displayName = response.user.displayName;
this.userData.email = response.user.email;
this.$.authDocument.save('/users', response.user.uid);
}.bind(this));
},
signOut: function() {
this.$.auth.signOut();
}
});
</script>
</dom-module>

Related

Attribute bind-value is not set in iron-input when initiated

Here's a pattern I try to do with Polymer:
<link rel="import" href="/bower_components/polymer/polymer.html" />
<link rel="import" href="/bower_components/iron-ajax/iron-ajax.html" />
<link rel="import" href="/bower_components/iron-input/iron-input.html" />
<dom-module id="my-entity">
<template>
<iron-ajax auto url="{{urlGet}}" handle-as="json" last-response="{{entity}}"></iron-ajax>
<iron-ajax id="updateEntityCall" url="{{urlUpdate}}" handle-as="json" on-response="handleUpdateResponse"></iron-ajax>
<div>
<div class="input-wrapper">
<label>Name : </label>
<input is="iron-input" bind-value="{{entityName}}" value="{{entity.name}}" />
</div>
<div>
<button on-click="saveEntity">Save</button>
</div>
</div>
</template>
<script>
Polymer(
{
is: "my-entity",
properties: {
entityid: {
notify: true
},
entityName: {
notify: true
}
urlGet: {
computed: 'getUrlGet(entityid)'
},
urlTemplate: {
computed: 'getUrlUpdate(entityid, entityName)'
},
},
getUrlGet: function (entityId) {
return ['/path/to/get', entityId].join('/');
},
getUrlUpdate: function (entityId, entityName) {
return ['/path/to/update', entityId, entityName].join('/');
},
saveEntity: function (e, detail, sender) {
this.$.updateEntityCall.generateRequest();
}
}
);
</script>
</dom-module>
I first create a iron-ajax that will get some data from an entity. This will be printed in a iron-input element so a person will be able to update this entity.
The iron-ajax is bound to a {{entity}} variable that is used by the value in the iron-input element (entity.name). I have created a property called entityName that will be bound at the bind-value attribute of this same iron-input.
Once someone has made the change, the goal is that he/she clicks on the button that calls the function saveEntity, that basically call the other iron-ajax element.
There is no problem with this code when someone actually change the value of the iron-input. The problem appear when the user don't change the value and click the button. It sends nothing, like if entityName is not set.
This is a simple example, in my use-case I have 3 fields and 2 of them can have "empty strings" as valid value.

Accessing Firebase from Polymer Element

I am working on an app that uses Polymer and Firebase. I am trying to figure out how to access my Firebase document from my custom element. Currently, I have a view defined like this:
<body>
<template is="dom-bind" id="dialog">
<firebase-document location="https://my-app.firebaseio.com/" data="{{ orders }}"></firebase-document>
<iron-pages attr-for-selected="data-route" selected="{{route}}">
<section data-route="home">
<h3>Hello</h3>
</section>
<section data-route="orders">
<h3>Orders</h3>
<user-orders orders="{{ orders }}"></user-orders>
</section>
</iron-pages>
</template>
</body>
The user-orders element is defined like this:
<dom-module id="user-orders">
<template>
<template is="dom-repeat" items="[[ orders ]]" as="order">
<div>[[ order.date ]]</div>
<div>[[ order.status ]]</div>
<div>[[ order.description ]]</div>
</template>
<button on-click="test">Test</button>
<button>Add New Order</button>
</template>
<script>
Polymer({
is: "user-orders",
properties: {
orders: {
type: Array,
notify: true,
value: function() {
return [];
}
}
},
test: function() {
alert(JSON.stringify(this.orders));
}
})
</script>
</dom-module>
The view associated with my route appears correctly. However, when I click my "Test" button, an alert window appears that says null. Its like either a) I've setup my firebase connection improperly, which I'm not sure how to actually confirm or b) My data binding is setup improperly.
What am I doing wrong?
Are you working with a brand new Firebase app or an existing Firebase app? A brand new Firebase app without any data will return null.

Content wrapped in currentUser re-rendering when user updated

I'm using Meteor and having an issue where my content is being re-rendered when I don't want it to.
I have my main content wrapped in a currentUser if statement which I feel is fairly standard.
{{#if currentUser}}
{{> content}}
{{/if}}
The problem with this is my content template is being re-rendered when I update my user object. Is there any way around this? I don't reference users anywhere inside the content template.
Thank you!
Here's a sample app to replicate my problem:
HTML
<head>
<title>Render Test</title>
</head>
<body>
{{loginButtons}}
{{> userUpdate}}
{{#if currentUser}}
{{> content}}
{{/if}}
</body>
<template name="userUpdate">
<p>
<input id="updateUser" type="button" value="Update User Value" />
User last update: <span id="lastUpdated">{{lastUpdated}}</span>
</p>
</template>
<template name="content">
<p>Render count: <span id="renderCount"></span></p>
</template>
JavaScript
if (Meteor.isClient) {
Meteor.startup(function() {
Session.set("contentRenderedCount", 0);
});
Template.content.rendered = function() {
var renderCount = Session.get("contentRenderedCount") + 1;
Session.set("contentRenderedCount", renderCount);
document.getElementById("renderCount").innerText = renderCount;
};
Template.userUpdate.events = {
"click #updateUser": function() {
Meteor.users.update({_id: Meteor.userId()}, {$set: {lastActive: new Date()}});
}
};
Template.userUpdate.lastUpdated = function() {
return Meteor.user().lastActive;
};
}
if (Meteor.isServer) {
Meteor.users.allow({
'update': function () {
return true;
}
});
}
Update:
I should've explained this example a little. After creating a user, clicking the Update User Value button, causes the render count to increment. This is because it's wrapped in a {{#if currentUser}}. If this is if is removed, you'll notice the render count remains at 1.
Also, you'll need to add the accounts-ui and accounts-password packages to your project.
Meteor will re-render any template containing reactive variables that are altered. In your case the {{currentUser}} is Meteor.user() which is an object containing the user's data. When you update the users profile, the object changes and it tells meteor to re-calculate everything reactive involving the object.
We could alter the reactivity a bit so it only reacts to changes in whether the user logs in/out and not anything within the object itself:
Meteor.autorun(function() {
Session.set("meteor_loggedin",!!Meteor.user());
});
Handlebars.registerHelper('session',function(input){
return Session.get(input);
});
Your html
{{#if session "meteor_loggedin"}}
{{> content}}
{{/if}}

How to create users client side?

I'm working on a intern document-sharing project for a small company. I want to do this with meteor. I'm very common with html/javascript but not with databases.
My problem is to handle the users. Because of my researches here I'm not sure if it's already possible to create users on client side. The official documentation shows some methodes how to deal with users but no examples.
I tried to create a list on server side like this:
Users = new Meteor.Collection("users");
Then I want to insert a user on startup like this:
//on Client side
if (Meteor.isClient) {
var username = "My Name";
Meteor.call("create_user", username, function(error, user_id) {
Session.set("user_id", user_id);
});
}
//on Server side
if(Meteor.is_server) {
Meteor.methods({
create_user: function(username) {
console.log("CREATING USER");
var USER_id = Users.insert({name: username});
return user_id;
},
});
}
But reading the username in the html template doesn't work...
Are there any good examples with a register and login?
Cheers
Adding the accounts functionality is very easy in Meteor.. may it be simple email, password, or by using facebook connect/twitter etc..
do the following to get a simple meteor app with user accounts set up..
meteor create simpleapp
cd simpleapp
add the accounts-ui and accounts-password packages
meteor add accounts-ui
meteor add accounts-password
you simply add other accounts related packages for implementing facebook/twitter/github/google login etc
to list other available meteor packages use this command
meteor list
now edit your simpleapp.html file to this to add login buttons etc..
<head>
<title>simpleapp</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
{{loginButtons}}
</template>
I simply added {{loginButtons}} to the default html file to add the default login buttons..
now run the meteor app and go to localhost:3000
meteor
you implemented the login functionality without doing much work. 4-5 lines of code, it even takes care of things like forgot password, registering new user etc
next thing is you need to display a particular html when the user is signed in.
you do this using the {{currentUser}} global
you implement it accordingly
<template name="hello">
<h1>Hello World!</h1>
{{greeting}}
<input type="button" value="Click" />
{{loginButtons}}
{{#if currentUser}}
{{> loggedInTemplate}}
{{else}}
{{> loggedOutTemplate}}
{{/if}}
</template>
<template name="loggedInTemplate">
<!-- user is logged in -->
</template>
<template name="loggedOutTemplate">
<!-- user is logged out -->
</template>
You don't need to create a user system manually. Just use the accounts package:
The Meteor Accounts system builds on top of the userId support in publish and methods. The core packages add the concept of user documents stored in the database, and additional packages add secure password authentication, integration with third party login services, and a pre-built user interface.
Your code should work, though. But you're not exposing the username property to the client, so maybe that's why you can't see it in your template.
Ok thank you, that's easy. But so I can not modify the loggedInTemplate or the loggedOutTemplate.
I show you what I've got:
//the html
<head>
<title>myApp | documents</title>
</head>
<body>
<div id="headerBox">
{{> header}}
</div>
<div id="sideBox">
{{> side}}
</div>
<div id="contentsBox">
{{> contents}}
</div>
</body>
<template name="header">
<h1>Company name</h1>
</template>
<template name="contents">
{{#if currentUser}}
<p>Welcome to documents</p>
{{else}}
<h3>Hello! Please log in!</h3>
<p><input type="text" id="username" placeholder="Username"><input type="text" id="password" placeholder="Password"><input type="button" value="log me in!"></p>
{{/if}}
</template>
<template name="side">
<p>Hello {{ activeUser }}</p>
<p><input type="button" value="Create New Document" onclick="createPage()"></p>
<h3>Documents:</h3>
</template>
and
//client.js
Pages = new Meteor.Collection("pages");
Meteor.startup(function() {
Session.set("activeUser", "please log in!");
});
Template.side.activeUser = function() {
return Session.get("activeUser");
};
and
//server.js
Meteor.startup(function() {
Accounts.createUser({username: "MyName", email: "me#example.com", password: "123456"});
});
and I'm searching for a manual way to log this user created on startup in. And of course later to allow this user to create new users...
The problem is adding
// Returns an event_map key for attaching "ok/cancel" events to
// a text input (given by selector)
var okcancel_events = function (selector) {
return 'keyup '+selector+', keydown '+selector+', focusout '+selector;
};
// Creates an event handler for interpreting "escape", "return", and "blur"
// on a text field and calling "ok" or "cancel" callbacks.
var make_okcancel_handler = function (options) {
var ok = options.ok || function () {};
var cancel = options.cancel || function () {};
return function (evt) {
if (evt.type === "keydown" && evt.which === 27) {
// escape = cancel
cancel.call(this, evt);
} else if (evt.type === "keyup" && evt.which === 13) {
// blur/return/enter = ok/submit if non-empty
var value = String(evt.target.value || "");
if (value)
ok.call(this, value, evt);
else
cancel.call(this, evt);
}
};
};
Template.contents.events = {};
Template.contents.events[okcancel_events('#password')] = make_okcancel_handler({
ok: function (text, event){
var usernameEntry = document.getElementById('username');
var passwordEntry = document.getElementById('password');
Meteor.loginWithPassword({usernameEntry, passwordEntry});
event.target.value = "";
}
});
to the client doesn't work...

returning findOne object to template

Having troubles understanding how to return and use an object from findOne().
my code is this:
Html:
<head>
<title>count</title>
</head>
<body>
{{> hello}}
</body>
<template name="hello">
{{showcount}}
</template>
Js:
var Database = new Meteor.Collection("counters");
if(Meteor.is_client) {
Template.hello.showcount = function () {
var c = Database.findOne();
return c;
};
}
if (Meteor.is_server) {
Meteor.startup(function () {
if(Database.find().count() === 0)
{
Database.insert({name: "counter", value: 0});
}
});
}
Now I'm wondering if there is any way I can access the data from my object.
changing from {{showcount}} to {{showcount.name}} doesn't seem to work at all.
This same issue got me a few times when I started out with Meteor...
When the Meteor client connects to the server the template is being rendered before the collections have finished being synchronised. i.e. The client collection is empty at the point you are calling findOne.
To see this in action stick a console.log(c) after your findOne call then try reloading the page. You will see two log entries; Once on initial page load and then again when the collection has finished being synchronised.
To fix this all you need to do is update your hello template to handle the fact the collection might not have been synchronised.
{{#if showcount}}
{{showcount.name}}
{{/if}}
I tested your code with the above change and it works.
The proper way to do this is with the #with tag.
<template name="hello">
{{#with showcount}}
{{name}}
{{/with}}
</template>
See the documentation below for more info on the #with tag
https://github.com/meteor/meteor/blob/devel/packages/spacebars/README.md

Resources