SailsJS stop unwanted data entering the database - javascript

So, with sailsJS you can just put any old data into your model and put it into the database even if the item is not defined in the attributes object.
Is there any way to prevent this happening? is seems silly to try and do an if and then throw errors because there would be too many things to stop getting through.
For example I have just ran into the problem where I have a list of users and I'm displaying them through angular ng-repeat on the frontend and when I put the data to save a user it also decides that "$$hashKey": "00I" is going with them too !
So it automatically saved to the database and when I refresh and get the data again, the "$$hashKey": "00I" is coming back, and therefore causing this error
Error: [ngRepeat:dupes] Duplicates in a repeater are not allowed. Use 'track by'
expression to specify unique keys.
Repeater: (key, user) in admins | filter:userSearch, Duplicate key: object:00I
This is coming from angular.
ps.
as far as i know, the server is using the default sails-disk

The default mode for Sails.js models is schemaless, which is probably not what you need. So, all you have to do is to add schema to your model:
module.exports = {
schema: true,
attributes: {
// Define all your attributes here...
}
};
After that all the values that are not described in the attributes will be automatically filtered out during the model saving.

Related

Reactive forms without initial view model in Vue and Vuetify

We are using forms for inserting new record or updating existing records (Update record scenerio includes view record scenerio)
Update record scenerio is easy one .
https://codepen.io/glikoz/pen/YjbeMY
But if we want to create "insert form" we don't have record, maybe we can create empty one. This resolves problem but we have to fill each property with null (Do you know any C# library for it :))
data:
{
errors:[],
name:null,
age:null,
movie:null,
city:{
id:null,
name:null
}
}
So for creating "insert form" normally we don't have a record (instance).
But if don't give record to viewModel, we are getting "cannot read property 'name' of undefined (city property is undefined)
This is natural viewmodel for insert form:
data: {
errors:[]
}
For seeing problem:
https://codepen.io/glikoz/pen/NBVOOe
I expect that v-model binding must handle this problem, is there any library, directive, approach to solve this problem?
Update (What's wrong with just creating an "instance")
I'm preparing "Insert forms" dynamically (template and data) and sending them to client side, than I'm rendering them (template and data) using this great work-> https://github.com/alexjoverm/v-runtime-template on client side.
So creating "empty" instance with each nested property is seems a little wrong.
I want to say that to component:
"You are component and I bind you with my model, If you could not find your data in my model, stop throwing exception show your default(empty) version"
I know what can I do to solve the problem in javascript -> https://medium.com/javascript-inside/safely-accessing-deeply-nested-values-in-javascript-99bf72a0855a
But I don't know Vue enough :(

Meteor: Data from External API call not rendering

I am relatively new to Meteor, and I'm trying to create a web store for my sister-in-law that takes data from her existing Etsy store and puts a custom skin on it. I've defined all of my Meteor.methods to retrieve the data, and I've proofed the data with a series of console.log statements... So, the data is there, but it won't render on the screen. Here is an example of some of the code on the server side:
Meteor.methods({
...
'getShopSections': function() {
this.unblock();
var URL = baseURL + "/sections?api_key="+apiKey;
var response = Meteor.http.get(URL).data.results;
return response;
}
...
});
This method returns an array of Object. A sample bit of JSON string from one of the returned Objects from the array:
{
active_listing_count: 20,
rank: 2,
shop_section_id: 1******0,
title: "Example Title",
user_id: 2******7
}
After fetching this data without a hitch, I was ready to make the call from the client side, and I tried and failed in several different ways before a Google search landed me at this tutorial here: https://dzone.com/articles/integrating-external-apis-your
On the client side, I have a nav.js file with the following bit of code, adapted from the above tutorial:
Template.nav.rendered = function() {
Meteor.call('getShopSections', function(err, res) {
Session.set('sections', res);
return res;
});
};
Template.nav.helpers({
category: function() {
var sections = Session.get('sections');
return sections;
}
});
And a sample call from inside my nav.html template...
<ul>
{{#each category}}
<li>{{category.title}}</li>
{{/each}}
</ul>
So, there's a few things going on here that I'm unsure of. First and foremost, the DOM is not rendering any of the category.title String despite showing the appropriate number of li placeholders. Secondly, before I followed the above tutorial, I didn't define a Session variable. Considering that the list of shop categories should remain static once the template is loaded, I didn't think it was necessary from what I understand about Session variables... but for some reason this was the difference between the template displaying a single empty <li> tag versus a number of empty <li>'s equal to category.length --- so, even though I can't comprehend why the Session variable is needed in this instance, it did bring me one perceived step closer to my goal... I have tried a number of console.log statements on the client side, and I am 100% sure the data is defined and available, but when I check the source code in my Developer Tools window, the DOM just shows a number of empty li brackets.
Can any Meteor gurus explain why 1) the DOM is not rendering any of the titles, and 2) if the Session variable indeed necessary? Please let me know if more information is needed, and I'll be very happy to provide it. Thanks!
You set the data context when you use #each, so simply use:
<li>{{title}}</li>
If a Session is the right type of reactive variable to use here or not is hard to determine without knowing what you are doing but my rough guess is that a Mini Mongo collection may be better suited for what it appears you are doing.
To get you started on deciding the correct type of reactive variable to use for this head over to the full Meteor documentation and investigate: collections, sessions, and reactive vars.
Edit: To step back and clarify a bit, a Template helper is called a reactive computation. Reactive computations inside of helpers will only execute if they are used in their respective templates AND if you use a reactive variable inside of the computation. There are multiple types of reactive variable, each with their own attributes. Your code likely didn't work at all before you used Session because you were not using a reactive variable.

Passing array via ReactiveVar in Meteor

I have a Meteor method that returns all user accounts on my application
returnUsers: function(){
return Meteor.users.find().fetch();
}
I'm using new ReactiveVar to pass the return value of the Meteor method into my template helper:
Template.listViewTemplate.created = function (){
var self = this;
self.myAsyncValue = new ReactiveVar("Waiting for response from serv er...");
Meteor.call('returnUsers', function (err, users) {
if (err)
console.log(err);
else
self.myAsyncValue.set(users);
});
}
Template.listViewTemplate.helpers({
userCollection: function(){
return Template.instance().myAsyncValue.get();
}
});
But when I go to render the users into the view, I get a console error that reads
{{#each}} currently only accepts arrays
When I render without the #each iterator, using
<ul id='usersList'>
{{userCollection}}
</ul>
the output on my web-page accurately reflects the number of users (2), but reads
[object Object],[object Object]
I'm pretty sure that there is some funkiness going on here because I'm using a global Meteor collection (Meteor.users.find().fetch(), as opposed to having defined my own collection), but I'm not sure how to get around it.
I want to display a list of all users so the current user can click another user and share a document with them--not sure how to get around this.
You don't need to use a reactive variable for this. The function at Template.listViewTemplate.created is not container in an autorun, which means: It won't get recomputed.
The best approach for your scenario is: Use a variable to get the status ( loading, loaded, error) and another variable to save the array itself attach to self. Reactivity is cool but you should only use it when needed.
About:
[object Object],[object Object]
This is happening because you're not extracting any value form the object provided nor looping using {{#each}}.
Your solutions for listing users is dangerous and inefficient. You're sending to the client all the fields from the user collection, including login tokens.
The best approach is to create a subscription that send only the necessaries fields like: _id, info.firstName. You should also have some criteria to the list users and use pagination. Consider also a search feature for such purpose.
ReactiveVar doesn't like arrays. You could install the ReactiveArray package which should accomplish exactly what you want.
Update
Based on comment of mper
In the latest versions of Meteor you can put an array in a ReactiveVar.
Tested on
meteor#1.6.0
reactive-var#1.0.11
I have several remarks about your question:
Do not fetch
You don't need .fetch() on your method. When you call find() on collections, such as Meteor.users a cursor is returned. The template (and #each in particular) can iterate through cursors. Cursors are usually better because you don't load the entire collection into memory at once - fetch does.
Meteor collections are reactive
Meteor collections are already reactive, meaning that if they change, they will trigger changes on your templates as well. So, you don't need to use a ReactiveVar to wrap your collection.
Query your local database
You don't need to use a method to get the users and in fact, you shouldn't, because usually you want to make queries to the database stored locally, not make calls to the server. Just call Meteor.users.find() directly in your template helper. You can (and should) control what is available locally through subscriptions.
Use #each with else
You can use the following in your template:
{{#each userCollection}}
...
{{else}}
Waiting for response from server...
{{/each}}
If userCollection is empty, the template will render the else block, just like you wanted.
Summarizing
Delete your method and onCreated with everything inside, change whatever is inside your template helper to only return Meteor.users.find() and use {{#each userCollection}}...{{else}}Waiting for response from server...{{/else}}
By the way
In the latest versions of Meteor you can put an array in a ReactiveVar.
Template.onCreated(function(){}) only gets run once and meteor methods only run once
You need reactivity here.
Collections sre reactive meaning pub/sub.
You need to create a publish function that allows certain users to fetch other users in the database. So all uses with maybe if the currentUser has permission to read all user info. Id limit the fields too.

How to access generateIdForRecord in Ember-Data?

I'm trying to develop an application using the Fixture Adapter with Ember-Data.
When I try and create a new object (based on a model I've defined), it won't work unless I specify the ID.
If I specify the ID and do this:
var person = SD.Person.createRecord({
id: 234,
name: "test"
});
var person.save();
I get:
Error: assertion failed: An adapter cannot assign a new id to a record
that already has an id. had id: 234 and you
tried to update it with 234. This likely happened because your server
returned data in response to a find or update that had a different id
than the one you sent.
Which makes it sound like somehow I'm updating an existing record (I'm not, there's only 2 fixtures for my Person object with ID's of 1 and 2 respectively).
Is Ember trying to save my object twice somehow?
I thought I may have to try and use generateIdForRecord to set the ID, but I can't reference that function no matter what I try.
newBooking.set('id', this.store.generateIdForRecord(this.store, newBooking));
newBooking.set('id', DS.generateIdForRecord(this.store, newBooking));
newBooking.set('id', this.generateIdForRecord(this.store, newBooking));
newBooking.set('id', generateIdForRecord(this.store, newBooking));
TypeError: this.store.generateIdForRecord is not a function
I'm using the latest releases of Ember and Ember-Data (have tried previous releases too). My model is implemented no differently to the TodoMVC tutorial in the Ember guides and in the tutorial nothing fancy needs to be done to manage ID's with the Fixture adapter so I've really no idea what's going on.
How do I create a new Person object (as per my example, just one 'name' field and persist it using Ember-Data's fixture adapter without the aforementioned errors?
In the end, my first code snippet worked fine.
For some reason, I was expecting to see the new object being persisted in the developer console. My models were being listed in a different view so I didn't realize it was actually working as intended.

How to Avoid Empty Record When Requested ID Doesn't Exist? (Ember)

I've got the following model (simplified for this question):
App.Manager = DS.Model.extend({
name: DS.attr('string'),
teamMembers: DS.hasMany('App.Employee')
});
When this model is loaded (say with App.Manager.find(1)), it's coming back from my server with a teamMembers array:
[10, 11, 12]
My view needs data from these team members, so Ember automatically loads them with a findMany() request, as expected. The problem I'm running into is that employee #11 doesn't exist. The server responds to the findMany() request with just employees 10 & 12:
{
"employees": [
{
"id": 10,
"name": "John Doe"
},
{
"id": 12,
"name": "Jane Doe"
}
}
But, Ember-Data still seems to keep around an empty (and fulfilled) promise for Employee 11 even though the data for that employee was never returned. So now when my view renders, I get a table with 3 rows (one for each employee), and one of those rows is completely blank (because the record is empty).
Checking the state of the record:
{
isLoaded: true,
isDirty: false,
isSaving: false,
isDeleted: false,
isError: false,
isNew: false,
isValid: true
}
So, I'm not sure how to keep this empty record out of my view without checking if every property I need is empty. Is there a way for the server to respond that tells ember not to fulfill this promise? Is there a way to configure ember to recognize when data is not returned?
Edit: I realize that, ideally, the server wouldn't return an ID for an employee that doesn't exist. But the reality is that sometimes data is unreliable, or poorly maintained. If employee 11 were coming back with inaccurate data, then I would agree that the problem is with the data and/or the service and not with Ember. However, in this case, employee 11 is not returning inaccurate data, it's returning NO data at all. In this case, it seems to me like ember should, at minimum, set a flag (i.e. isValid: false) indicating that the record is empty/invalid/not found, if not just flat out destroy the reference to the empty record altogether.
Edit 2: Here's an Issue on Github
It's hard to say whether or not Ember-data should try to provide some magic for this scenario. By including the ID in a relationship you've effectively told ember-data that it exists.
The REST adapter (which request data from the server) is separate from the serializer (which converts the response data to your models), so it would be difficult to know whether or not all of the requested data was actually returned. If you don't mind tightly coupling your adapter to your serializer you may be able to verify that the server gave you everything you were expecting.
Workaround:
Add a verification property to your employee model to use as a kind of pseudo-state. This should default to false or null and every record your API returns must overwrite it. Pretty hackish. Luckily this same concept can be accomplished using an existing property such as the employee's name.
if (App.Employee.find(11).get('name') != null) { dance('thriller'); }
If you consider the concept above as a standard validation requirement you may choose to implement it as such in either the model or serializer.

Categories