How can I extend the users collection with Astronomy and accounts-ui? - meteor

I need to be able to extend the collection that accounts-ui creates by default of users.
import { Class } from 'meteor/jagi:astronomy';
import { Behavior } from 'meteor/jagi:astronomy-softremove-behavior';
/**
* #class User
*/
const User = Class.create ({
name: 'User',
collection: Meteor.Users,
secured: false,
fields: {
emails: {
type: Email,
optional: true
}
},
behaviors: {
softremove: {
removedFieldName: 'removed',
hasRemovedAtField: true,
removedAtFieldName: 'removedAt'
},
timestamp: {
hasCreatedField: true,
createdFieldName: 'createdAt',
hasUpdatedField: true,
updatedFieldName: 'updatedAt'
}
}
});
export default User;
I wan't use Meteor.user.(), only like to use the User class.

Welcome to Stack Overflow.
The users collection in Meteor is special. Under the hood in Mongo it is a regular collection.
For security reasons, you can't use the users collection like a regular collection, for example you can update your own user record, but you can't browse and edit other users records.
My advice is to create a separate collection for your user profile information. It's not a perfect solution, but it is better to leave the users collection under the control of the Meteor accounts package.
It is possible to extend the collection to store a few profile type data elements, but even that has some wrinkles, because when a user record is updated, the tracker runs, and your UI will refresh (with possible negative side effects if the refresh isn't expected)

Related

Displaying sidebar items according to user role

I want to display the sidebar items of vx-sidebar according to user role. I'm using acl plugin. There are two users admin and editor. Both have different sidebar items. I have set the roles and everything.But everytime user login, the sidebar items doesn't render initially, I have to refresh the page then the sidebar items render according to role. I am using firebase firestore. And i am setting the initial role as the user.uid found in two different collections of admin and editor.
I have also set the user role in the local storage and vuex.store, but still the elements don't render on initial login.Please help me with this. I have spend days with solving this. But doesn't find solution
acl.js
import Vue from 'vue'
import { AclInstaller, AclCreate, AclRule } from 'vue-acl'
import router from '#/router'
Vue.use(AclInstaller)
let initialRole = localStorage.getItem('userRole')
console.log(initialRole)
export default new AclCreate({
initial: initialRole,
notfound: '/pages/not-authorized',
router,
acceptLocalRules: true,
globalRules: {
admin: new AclRule('admin').generate(),
editor: new AclRule('editor').generate(),
}
})
VxSidebarItem.vue
<div :class="[{'vs-sidebar-item-active':activeLink}, {'disabled-item pointer-events-none': isDisabled}]" class="vs-sidebar--item" v-if="canSee">
computed: {
canSee() {
this.$acl.check(localStorage.getItem('userRole'));
console.log(localStorage.getItem('userRole'))
if(this.to) {
return this.$acl.check(this.$router.match(this.to).meta.rule)
}
return true
}
},
router.js
{
path: '/',
redirect: '/dashboard/jobs'
},
{
path: '/dashboard/jobs',
name: 'Jobs',
component: () => import('./views/tpo/Jobs.vue'),
meta: {
rule:'admin',
}
},

Redux: right way to handle concurrent requests

I just can't grasp the core concept I guess.
I have a page with contact list. But user can visit the page with contactID in URL, and in this case I should open a popup window with contact info. So, what am I doing:
My store looks like:
{
isRequested: boolean, // is initial loading done
isLoading: boolean, // is currently loading,
contacts: {[ key: number ]: IContact}
// where key is contact ID and IContact is generic a model
}
Open the page and start loading contacts list.
Without waiting for contacts I'm openinig the popup window.
So, how should I handle this situation? The only thing that come to my mind is to create dummy contact if it's not exist in store. Something like:
{
...,
contacts: {
1: {
isRequested: false,
isLoading: true,
isCorrupted: false // in case of bad ID
}
}
}
And subscribe to state change in popup window, to wait for this contact isRequested to change.
And when all contacts loaded - add all contacts without this one, or merge them.
Is it right approach, or is it a better solution do exists?
I would keep contacts empty and simply display a loading screen until the full list is loaded.
So your state looks like
{
isLoading: boolean, // is currently loading,
contacts: {[ key: number ]: IContact}
}
and the initial state
{
isLoading: false,
contacts: {}
}
In your view:
If contacts is an empty object, display nothing.
On first load (componentWillMount in React), load the list of contacts and set isLoading to true.
When isLoading is true, display a loading screen.
When the list is ready, display your information

Altering a returned collection in onBefore hook or similar hook

Is it possible to alter (add more fields for each record) a returned collection in onBeforeAction or similar hook?
I have InvoiceHistory collection which I am paginating through. I also want to display as part of each invoice the company name, business address, email address and VAT registration number - and these four fields are stored in another collection. So I would like to add these four fields to each record returned from InvoiceHistory. If there is another way to do it I am open to suggestions. I am using Alethes meteor pagination which loses the helper fields returned in its itemTemplate when you navigate/browse/page to the second page. Alethes pagination also relies on iron-router, is it maybe possible to achieve what I want with the help of iron-router
========================================================================
#Sean. Thanks. Below is the code that uses meteor publish composite:
if (Meteor.isServer) {
import { publishComposite } from 'meteor/reywood:publish-composite';
publishComposite('invoicesWithCompanyDetails', function(userId, startingDate,endingDate) {
return {
find() {
// Find purchase history for userId and the two dates that were entered. Note arguments for callback function
// being used in query.
return PurchaseHistory.find({Id:userId,transactionDate:{$gte:new Date(decodeURIComponent(startingDate)),
$lt:new Date(decodeURIComponent(endingDate))}});
},
children: [
{
find() {
return CompanySharedNumbers.find(
{ _id:"firstOccurrence" },
{ fields: { companyName: 1, companyAddress: 1 } }
);
}
}
]
}
});
}
Router.route('/store/invoices/:_username/:startingDate/:endingDate', { //:_startingDate/:_endingDate
name: 'invoices',
template: 'invoices',
onBeforeAction: function()
{
...
},
waitOn: function() {
var startingDate = this.params.startingDate;
var endingDate = this.params.endingDate;
return [Meteor.subscribe('systemInfo'),Meteor.subscribe('testingOn'),Meteor.subscribe('invoicesWithCompanyDetails',startingDate,endingDate)];
}
});
Pages = new Meteor.Pagination(PurchaseHistory, {
itemTemplate: "invoice",
availableSettings: {filters: true},
filters: {},
route: "/store/invoices/:_username/:startingDate/:endingDate/",
router: "iron-router",
routerTemplate: "invoices",
routerLayout: "main",
sort: {
transactionDate: 1
},
perPage: 1,
templateName: "invoices",
homeRoute:"home"
});
Instead of adding new fields to all your document. What you could do is to add a helper to load a company document :
Template.OneInvoice.helpers({
loadCompany(companyId){
return Company.findOne({_id: companyId});
}
});
And use it in your template code:
<template name="OneInvoice">
<p>Invoice id: {{invoice._id}}</p>
<p>Invoice total: {{invoice.total}}</p>
{{#let company=(loadCompany invoice.companyId)}}
<p>Company name: {{company.name}}</p>
<p>Company business address: {{company.businessAddress}}</p>
{{/let}}
</template>
This way, the code is very simple and easily maintainable.
If I'm understanding you correctly, this sounds like a job for a composite publication.
Using a composite publication you can return related data from multiple collections at the same time.
So, you can find all your invoices, then using the company ID stored in the invoice objects you can return the company name and other info from the Companies collection at the same time.
I've used composite publications before for complex data structures and they worked perfectly.
Hope that helps

Best practice for Meteor AutoForm validation of items not in the form?

AutoForm works great when you want to validate a form using the schema, but oftentimes the form doesn't contain all the data that's in the schema, so you get a validation error.
For example, I have a submit button that is disabled until it determines the form is completely valid, but because the form doesn't contain all the data that's in the schema, it can never show that it's completely valid.
Say that you've got a schema
ShoppingCartSchema = new SimpleSchema({
itemsOrdered: {
type: [Object],
optional: false,
},
totalPrice: {
type: Number,
optional: false,
},
customerAddress: {
type: Object,
optional: false
},
systemGeneratedInfo: {
type: Object,
optional: true,
blackbox: true
},
});
ShoppingCarts.attachSchema(ShoppingCartSchema);
My form code would be something like:
{{> quickForm collection="ShoppingCarts" id="shoppingCartForm" type="insert"}}
Obviously you don't want totalPrice to be an item on the form for the user to fill on their own. systemGeneratedInfo could be an object that your code generates based on the form values, but which doesn't appear on the form itself.
You could put totalPrice into a hidden form element but that seems sketchy. You really wouldn't want to do that with systemGeneratedInfo.
What other strategies would there be for tackling items on the object that don't show up on the form, but which still allow the form shown on the front end to be completely validated?
You have to make use of the schema field of the quickForm or autoForm. Then you create Template helpers to pass the schema to the form (or if your schemas are available globally you can just pass Schemas.someSchema to the form).
You can either define completely new schemas just for minor form functionality tweaks, which in my mind is overkill if you have a giant nested schema and you just want to omit a couple of fields or...
You can just import the original schema into your template.js and then do an EJSON.clone on it and modify the fields and attributes locally in the helper (or elsewhere) before you pass it to the template.
// Profile.js
import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { AutoForm } from 'meteor/aldeed:autoform';
import { EJSON } from 'meteor/ejson';
import { Schema } from '../../../api/profiles/profiles.js';
Template.profile.helpers({
profileSchema: function() {
let userSchemaForAutoForm = EJSON.clone(Schema.User);
userSchemaForAutoForm._schema.emails.optional = true;
return userSchemaForAutoForm;
}
});
// Profile.html
{{#autoForm id="profileEditForm" collection="Meteor.users" schema=profileSchema doc=currentUser type="update"}}

Updating data that is passed into child component Angular2

I am new to development in Angular2 - Meteor. I have stumbled upon a problem while working with parent-child components. In the parent component, I have meteor subscribing to a collection.
users: any;
constructor(private _zone:NgZone){
var sub = Meteor.subscribe("accounts/users");
Tracker.autorun(function() {
_zone.run(function() {
if(sub.ready()){
this.users = Meteor.users.find().fetch();
console.log(this.users);
}
});
});
}
The user collection has 90 users in total. When the app initially loads, only the current user is found, thus the console log shows one user.
I am not sure if my placement of Tracker.autorun() and NgZone are correct, but after a second of the app loading, the console log shows an array of all 90 users. I assume this happens because the subscribe is not ready at first.
My child component takes in the fetched users as a parameter like this <sd-table [data]="users"></sd-table>. Upon loading of the application, there is only one user that is seen on the drawn template of the child component. Is there any way that the template can be updated when the subscribe happens and all users become accessible?
If you want to refer to this of the current class, don't use function()
users: any;
constructor(private _zone:NgZone){
var sub = Meteor.subscribe("accounts/users");
Tracker.autorun(() => {
_zone.run(() => {
if(sub.ready()){
this.users = Meteor.users.find().fetch();
console.log(this.users);
}
});
});
}
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Resources