Wakanda : Modify field on entry creation - javascript

I am currently making an app for punching in and out employees.
I have three tables, one for the Employes, one for the PunchIn and one for the PunchOut.
What I want is the following : when I create a PunchIn, it change the is_in entry (of the table Employees) to true and when I create a PunchOut, the is_in is false.
How can I deal with relations to make this?
Thanks!

Assuming the employee entity already exists, it's just:
punchInEntity.Employee.is_in = true;
and
punchOutEntity.Employee.is_in = false;
Be sure to save both entities afterward:
punchOutEntity.save();
punchOutEntity.Employee.save();
However, you might want to consider making is_in a computed attribute. In it's method, you could search for a punchOutCollection entity that is after the current date/time. Return false if one is found and true if not (as long as there is at least one punchInCollection entity). That way, you don't have to worry about updating is_in and it will always be correct.

Relations are not the best use case here. You need your employee entity to create a Punchin entity. So, why not directly update your employee entity instead of using relations ?
An example:
var emp = ds.Employees.find("ID = 1");
var myPunchin = new ds.Punchin({
employee: emp
});
myPunchin.save();
myPunchin.employee.is_in = true;
myPunchin.employee.save();
Even better without relations, you can replace myPunchin.employee by emp.

Related

Firebase realtime database : how to check if child value exists

Given the document below.
website:{
uid1:{
foo:"bar",
username:"joff"
},
uid2:{
foo:"bar2",
username:"karla"
}
}
I am trying to check if the username already exist using the query below.
var ref = db.ref('website');
var queryString = 'joff';
ref.orderByChild('username').equalTo(queryString).once('value', function(snap){
if(snap.exists()){
// exists
return
}
console.log('available');
})
This approach is working without a problem, if I change the queryString accordingly I get the result that I want if it exists or not.
Firebase's default query limit is 100 and I can change it to my liking according to the documentation.
But what if I will query though a let's say 10,000 documents? I cant use paginated query here, because what if the query match is on the 2nd page? How do I handle such amount of data? Will it take too long before it respond? What is the right approach to do such query.
PS
I strictly need the data model to be the same as the given above.
Thanks in advance.
I thought that use of equalTo generated a "smart" query, and so the query limit wouldn't be factor in determining if record exists. I'm not positive though. Have you actually verified that having more than 100 records, with your 'joff` being at 100+ in the ordering, results in no match?
If that is the case (which would be very surprising), then you could probably restructure your query something like this:
ref.orderByChild('username').startAt(queryString).endAt(queryString).once('value', function(snap){
if(snap.exists()){
// exists
return
}
console.log('available');
})
... but again, I don't think that is required when using equalTo.

Breeze - Getting All Navigation Properties For Array of Entities

I'm trying to figure out with Breeze how to expand a specific navigation property for all items in an array of entities with a single request.
On this page of the Breeze documentation it shows the following way of achieving this:
var orderEntityType = selectedOrders[0].entityType;
var navProp = orderEntityType.getNavigationProperty("OrderDetails");
var navQuery = EntityQuery
.fromEntityNavigation(selectedOrders, navProp)
.expand("Product");
manager.executeQuery(navQuery).fail(handleFail);
However, when I tried this I get the error
The 'entity' parameter must be an entity
So I looked up in the documentation specifically for the EntityQuery.fromEntityNavigation method and it shows:
// 'employee' is a previously queried employee
var ordersNavProp = employee.entityType.getProperty("Orders");
var query = EntityQuery.fromEntityNavigation(employee, ordersNavProp);
The documentation indicates that it is for a specific entity, not multiple. Which is consistent with the error I'm getting.
Is it possible to get all the navigation properties in a single request, or is the preferred way to iterate over an array making a request for each entity?
Basically, I'm working on filtering a list of items. My goal is that when a user selects a filter it then expands the needed navigation property at that time instead of loading all the data up front.
Thanks for the help.
I think this might be a typo or some out of date information on the navigation properties documentation page. According to the API documentation for EntityQuery.fromEntityNavigation, the first parameter should be a single entity, not an array. Took a look at the breeze code, didn't see any evidence that an array of entities could be passed.
As a workaround, you could construct the query a bit differently. Continuing with the Order/OrderDetails scenario, you could do something like this:
var subsetOfOrders = ..., // array containing the subset of orders whose OrderDetails we need to load
predicates = subsetOfOrders.map(function(order) { return new breeze.Predicate('OrderId', '==', order.OrderId()); }),
predicate = breeze.Predicate.or(predicates),
query = new breeze.EntityQuery('Orders').expand('OrderDetails').where(predicate);
manager.executeQuery(query)...
If you're able to query the order details directly you don't even need expand. Breeze will wire up the freshly loaded OrderDetails to the respective orders entities that are already cached in the entity manager:
var subsetOfOrders = ..., // array containing the subset of orders whose OrderDetails we need to load
predicates = subsetOfOrders.map(function(order) { return new breeze.Predicate('OrderId', '==', order.OrderId()); }),
predicate = breeze.Predicate.or(predicates),
query = new breeze.EntityQuery('OrderDetails').where(predicate);
manager.executeQuery(query)...
This predicate based workaround may or may not be feasible depending on the number of orders you're dealing with. Could end up with a long query string. You could then consider using a dedicated controller action (ie "OrderDetailsByOrderId(int[] orderIds)" and use the withParameters EntityQuery method to load the order details using the new action.
The documentation was in error. I just corrected it.
#Jeremy Danyow offered a superb explanation and a solution. I probably would use his approach to solve a specific use case.
The documentation now discusses the problem and describes yet another approach that might be more appropriate if you were trying to write a general utility.
// create an array of filter criteria (`wherePredicate`) for each order
var predicates = orders.map(function (order) {
return EntityQuery.fromEntityNavigation(order,'OrderDetails')
.wherePredicate;
});
// OR the predicates together
var filter = breeze.Predicate.or(predicates);
EntityQuery.from('OrderDetails')
.where(filter)
.expand('Product')
.using(em).execute().catch(handleFail);
Thanks to you both for identifying the problem and working through it.

Temporarily accumulate objects depending on the state of a different stream

I've been trying to teach myself FRP (and bacon.js specifically) by diving in head first on a new project. I've gotten pretty far on my own but recently ran into a problem that I can't seem to fight my way through:
I have an interface with a set of clickable objects. When an object is clicked, detailed information for that object is loaded in a panel to the right.
What I need is the ability to select multiple, to accumulate those objects into an array and show a "bulk actions" panel when more than one is selected.
So far I have:
a SelectMultiple boolean property that represents the current UI mode
a CurrentObject stream that holds the currently selected object
I've gotten somewhat close with this:
var SelectedObjects = CurrentObject.filter(SelectMultiple).skipDuplicates().scan([], function(a,b){
return a.concat([b]);
};
There are a few problems:
The value of SelectedObjects represents the objects selected over
all time, it doesn't reset when SelectMultiple state changes.
The value of SelectObjects does not include the original
CurrentObject (of course because the scan accumulator seed is an
empty array, not the current value of CurrentObject).
The fact that I'm looking to use the current value of a property directly seems to be a hint that there's a fundamental issue here. I have a notion that the answer involves flapMapLatest and spawning a new stream every time SelectMultiple changes, funneling selected orders into this new stream and accumulating, but I can't quite work out what that should look like.
Of course there is an additional problem that skipDuplicates only skips consecutive duplicates. I can probably work this one out on my own but a solution that addresses that issue would be ideal.
Any suggestions would be greatly appreciated!
This might work (coffeescript):
var selectMultiple # Property[Boolean] - whether in multiselect mode
var selectedObject # Property[Object] - latest selected object
var selectedObjects = selectMultiple.flatMapLatest((multiple) ->
if !multiple
selectedObject.map((obj) -> [obj])
else
selectedObject.scan([], (xs, x) ->
xs.concat(x)
)
).toProperty()
On each value of selectMultiple flag we start a new stream that'll either just track the current single selection or start accumulating from the single selection, adding items as they're selected. It doesn't support de-selection by toggling, but that's straightforward to add into the scan part.
Ok I figured out a solution. I realized that I could use a dynamically-sized slidingWindow combinator. I found the basis for the answer in the Implementing Snake in Bacon.js tutorial.
I got an error when I tried adding directly to the Bacon prototype (as described in the tutorial) so I just made a function that takes the stream to observe and a boolean that determines if it should capture values:
slidingWindowWhile = function(sourceStream, toTakeOrNotToTake) {
return new Bacon.EventStream(function(sink){
var buf = [];
var take = false;
sourceStream.onValue(function(x){
if (! take) {
buf = [];
}
buf.push(x);
sink(new Bacon.Next(buf));
});
toTakeOrNotToTake.onValue(function(v){
take = v;
});
});
};
It still seems like there should be a way to do this without using local variables to track state but at least this solution is pretty well encapsulated.

Correct way to model a collection of items in Firebase

In the docs I see a lot of examples using index values as a part of the key name for a particular item --- but I don't understand how this is a consistent way to model your data.
For example let's say I have a list of articles:
https://gigablox.firebaseio.com/articles/
article1
article2
article3
When I'm ready to add article4 I know I can use:
var length = Object.keys($scope.articles).length;
And using AngularFire 0.5.0 I can save it with:
var name = 'article' + length + 1;
$scope.articles[name] = $scope.article;
$scope.articles.$save(name);
But what happens if I:
$scope.articles.$remove('article2');
And add another record using the same approach? We're likely to create duplicate key names.
To add a little complexity, let's add a single relationship and say that each article has comments.
What is the correct way to model this data in a Firebase collection?
Please use $add and let Firebase automatically generate chronologically ordered lists for you.
var ref = new Firebase("https://gigablox.firebaseio.com/articles/");
$scope.articles = $firebase(ref);
$scope.addArticle = function() {
$scope.articles.$add($scope.article);
}
$scope.removeArticle = function(id) {
$scope.articles.$remove(id);
}
Firebase automatically creates key names when you call $add. You can iterate over the key names using ng-repeat:
<div ng-repeat="(key, article) in articles">
<div ng-model="article"><a ng-click="removeArticle(key)">Remove</a></div>
</div>
EDIT: You should follow the suggestion from #Anant if you want an array-based collection.
However, for this specific scenario as outlined by #Dan Kanze, if you want to pull the key out of the URL (as would be done for a content management system, etc), you should generate your own keys unique to the content. For example, if you know that article names need to be unique, create a slug function that will:
Lowercase the article name
Replace spaces with underscores
etc..
If the article name changes, you would not delete the old entry. Instead, create a new entry in Firebase and use the old key to point to the new location for 301 redirects, etc.

Safely using eval to use variable as an object name

As shown in this example
javascript-use-variable-as-object-name
I am using eval to use a DOM attribute to select an element from an array. Though there is no direct way for the user to change the input, I want to be as secure as possible and make sure that the variable is indeed an integer before I evaluated it.
Which of the following would be the best, most secure, way?
$(".listitem").click(function(){
var id = $(this).attr("record-id");
if(!isNaN(new Number(id))){
Storage.search.nearby.currec = rowsHolder[eval(id)];
}else{
// send email to admin, shut down
}
});
or
$(".listitem").click(function(){
var id = $(this).attr("record-id");
if(parseInt(id)){
Storage.search.nearby.currec = rowsHolder[eval(id)];
}else{
// send email to admin, shut down
}
});
More, but not required info:
Basically I am pulling down a large JSON string from online, containing an array of records. Upon building a table from the info using a for statement ( for(i in array) ), I push each row into an array called rowsHolder and give the tr an attribute of record-id="i". Then when the user clicks the row, I call the method you see above. I am using PhoneGap with JQuery Mobile.
As always, thanks for the input
-D
There is absolutely no reason to use eval here.
If your id is kind of a number, use parseFloat(id) to get it. Unnecessary as it would be converted back to a string when used as a property name, though.
If your id is an integer, use parseInt(id, 10) to get it. Unnecessary as it would be converted back to a string when used as a property name, though.
If your id is a string, just let it be a string. The property name you use it for would be one anyway.

Categories

Resources