Knockoutjs how to data-bind observable array members based on IDs - arrays

I'm not if the title explains what I need to achieve or not but I can change it later if some has a better suggestion.
I'm using KO to manage a whole bunch of data on the client side.
Here's the basic.
I have a list of training sessions
Each has a list of training session parts
Each training session parts are referencing items kept in other lists. For example, I have a list of activities (ex: biking, running, swimming, etc.)
Each activity is identified by an ID which is used in the training session parts to identify which activity was used for a particular session.
Now, all these list are stored as observable arrays, and each member of the lists are observables (I use KO.Mapping to map the JSON coming from the server)
When I display a training session in my UI, I want to display various information coming from various lists
Duration: 1h30
Activity: Biking
Process: Intervals
The only information I have in order to link the training session to its component is an ID which is fine. What I'm not sure is how to data-bind the name (text) of my activity to a <p> or <div> so that the name will change if I edit the activity (by using some functionality of the application).
The training session only has the ID to identify the activity, so I don’t know how to bind the name of the activity based on its ID.
Hopefully this makes senses and someone can help me figure it out. I found lots of info on how to bind to observable array but nothing addressing ID and linked information.

The easiest way would probably be to make your own constructors and link the data by hand. You can use mapping if you really want to, but you'll basically have to do the same manual linking, only in a more verbose format.
This is the fiddle with the example implementation: http://jsfiddle.net/aKpS9/3/
The most important part of the code is the linking, you have to take care to create the activity objects only once, and use the same objects everywhere, as opposed to creating new activity objects for the parts.
var TrainingSession = function(rawData, actualActivities){
var self = this;
self.name = ko.observable(rawData.name);
self.parts = ko.observableArray(ko.utils.arrayMap(rawData.parts, function(rawPart){
return ko.utils.arrayFirst(actualActivities(), function(ac){
return ac.ID() == rawPart.ID;
})
}));
}
var Activity = function(rawData){
var self = this;
self.ID = ko.observable(rawData.ID);
self.name = ko.observable(rawData.name);
}
var MainVM = function(rawData){
var self = this;
//first create an array of all activities
self.activities = ko.observableArray(ko.utils.arrayMap(rawData.activities, function(rawAc){
return new Activity(rawAc);
}));
self.trainingSessions = ko.observableArray(ko.utils.arrayMap(rawData.trainingSessions, function(session){
return new TrainingSession(session, self.activities);
}));
}

Related

Firebase cloud function not updating record

imagine this scenario:
You have a list with lets say 100 items, a user favorites 12 items from the list.
The user now wants these 12 items displayed in a new list with the up to date data (if data changes from the original list/node)
What would be the best way to accomplish this without having to denormalize all of the data for each item?
is there a way to orderByChild().equalTo(multiple items)
(the multiple items being the favorited items)
Currently, I am successfully displaying the favorites in a new list but I am pushing all the data to the users node with the favorites, problem is when i change data, their data from favorites wont change.
note - these changes are made manually in the db and cannot be changed by the user (meta data that can potentially change)
UPDATE
I'm trying to achieve this now with cloud functions. I am trying to update the item but I can't seem to get it working.
Here is my code:
exports.itemUpdate = functions.database
.ref('/items/{itemId}')
.onUpdate((change, context) => {
const before = change.before.val(); // DataSnapshot after the change
const after = change.after.val(); // DataSnapshot after the change
console.log(after);
if (before.effects === after.effects) {
console.log('effects didnt change')
return null;
}
const ref = admin.database().ref('users')
.orderByChild('likedItems')
.equalTo(before.title);
return ref.update(after);
});
I'm not to sure what I am doing wrong here.
Cheers!
There isn't a way to do multiple items in the orderByChild, denormalising in NoSQL is fine its just about keeping it in sync like you mention. I would recommend using a Cloud Function which will help you to keep things in sync.
Another option is to use Firestore instead of the Realtime Database as it has better querying capabilities where you could store a users id in the document and using an array contains filter you could get all the users posts.
The below is the start of a Cloud function for a realtime database trigger for an update of an item.
exports.itemUpdate = functions.database.ref('/items/{itemId}')
.onUpdate((snap, context) => {
// Query your users node for matching items and update them
});

How to Fetch a set of Specific Keys in Firebase?

Say I'd like to fetch only items that contains keys: "-Ju2-oZ8sJIES8_shkTv", "-Ju2-zGVMuX9tMGfySko", and "-Ju202XUwybotkDPloeo".
var items = new Firebase("https://hello-cambodia.firebaseio.com/items");
items.orderByKey().equalTo("-Ju2-gVQbXNgxMlojo-T").once('value', function(snap1){
items.orderByKey().equalTo("-Ju2-zGVMuX9tMGfySko").once('value', function(snap2){
items.orderByKey().equalTo("-Ju202XUwybotkDPloeo").once('value', function(snap3){
console.log(snap1.val());
console.log(snap2.val());
console.log(snap3.val());
})
})
});
I don't feel that this is the right way to fetch the items, especially, when I have 1000 keys over to fetch from.
If possible, I really hope for something where I can give a set of array
like
var itemKeys = ["-Ju2-gVQbXNgxMlojo-T","-Ju2-zGVMuX9tMGfySko", "-Ju202XUwybotkDPloeo"];
var items = new Firebase("https://hello-cambodia.firebaseio.com/items");
items.orderByKey().equalTo(itemKeys).once('value', function(snap){
console.log(snap.val());
});
Any suggestions would be appreciated.
Thanks
Doing this:
items.orderByKey().equalTo("-Ju2-gVQbXNgxMlojo-T")
Gives exactly the same result as:
items.child("-Ju2-gVQbXNgxMlojo-T")
But the latter is not only more readable, it will also prevent the need for scanning indexes.
But what you have to answer is why want to select these three items? Is it because they all have the same status? Because they fell into a specific date range? Because the user selected them in a list? As soon as you can identify the reason for selecting these three items, you can look to convert the selection into a query. E.g.
var recentItems = ref.orderByChild("createdTimestamp")
.startAt(Date.now() - 24*60*60*1000)
.endAt(Date.now());
recentItems.on('child_added'...
This query would give you the items of the past day, if you had a field with the timestamp.
You can use Firebase child. For example,
var currFirebaseRoom = new Firebase(yourFirebaseURL)
var userRef = currFirebaseRoom.child('users');
Now you can access this child with
userRef.on('value', function(userSnapshot) {
//your code
}
You generally should not be access things using the Firebase keys. Create a child called data and put all your values there and then you can access them through that child reference.

How to get Entry Id for a Newly Created Entity in Breeze

i want to create a registration form that will be in batch with a continuation button, getting the id of the entry will help me to call the save method.
I want to immediately get the primary key of a new Entry Created using BreezeJS, Pls i need help on this.
Thanks
Not entirely sure I understand your question, but it sounds like you want to get the id of a newly saved record immediately after the save. If so then the answer below applies.
When the save promise resolves it returns both the list of saved entities as well as a keyMappings array for any entities whose ids changed as a result of the save. i.e. a mapping from temporary to real ids. i.e. (Documented here: http://www.breezejs.com/sites/all/apidocs/classes/EntityManager.html#method_saveChanges)
myEntityManager.saveChanges().then(function (saveResult) {
// entities is an array of entities that were just saved.
var entitites = saveResult.entities;
var keyMappings = saveResult.keyMappings;
keyMappings.forEach(function(km) {
var tempId = km.tempValue;
var newId = km.realValue;
});
});
On the other hand if you have an entity and you just want its 'key' you can use the EntityAspect.getKey method. (see http://www.breezejs.com/sites/all/apidocs/classes/EntityAspect.html#method_getKey)
// assume order is an order entity attached to an EntityManager.
var entityKey = order.entityAspect.getKey();

AngularFire - How do I query denormalised data?

Ok Im starting out fresh with Firebase. I've read this: https://www.firebase.com/docs/data-structure.html and I've read this: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html
So I'm suitably confused as one seems to contradict the other. You can structure your data hierarchically, but if you want it to be scalable then don't. However that's not the actual problem.
I have the following structure (please correct me if this is wrong) for a blog engine:
"authors" : {
"-JHvwkE8jHuhevZYrj3O" : {
"userUid" : "simplelogin:7",
"email" : "myemail#domain.com"
}
},
"posts" : {
"-JHvwkJ3ZOZAnTenIQFy" : {
"state" : "draft",
"body" : "This is my first post",
"title" : "My first blog",
"authorId" : "-JHvwkE8jHuhevZYrj3O"
}
}
A list of authors and a list of posts. First of all I want to get the Author where the userUid equals my current user's uid. Then I want to get the posts where the authorId is the one provided to the query.
But I have no idea how to do this. Any help would be appreciated! I'm using AngularFire if that makes a difference.
Firebase is a NoSQL data store. It's a JSON hierarchy and does not have SQL queries in the traditional sense (these aren't really compatible with lightning-fast real-time ops; they tend to be slow and expensive). There are plans for some map reduce style functionality (merged views and tools to assist with this) but your primary weapon at present is proper data structure.
First of all, let's tackle the tree hierarchy vs denormalized data. Here's a few things you should denormalize:
lists you want to be able to iterate quickly (a list of user names without having to download every message that user ever wrote or all the other meta info about a user)
large data sets that you view portions of, such as a list of rooms/groups a user belongs to (you should be able to fetch the list of rooms for a given user without downloading all groups/rooms in the system, so put the index one place, the master room data somewhere else)
anything with more than 1,000 records (keep it lean for speed)
children under a path that contain 1..n (i.e. possibly infinite) records (example chat messages from the chat room meta data, that way you can fetch info about the chat room without grabbing all messages)
Here's a few things it may not make sense to denormalize:
data you always fetch en toto and never iterate (if you always use .child(...).on('value', ...) to fetch some record and you display everything in that record, never referring to the parent list, there's no reason to optimize for iterability)
lists shorter than a hundred or so records that you always as a whole (e.g. the list of groups a user belongs to might always be fetched with that user and would average 5-10 items; probably no reason to keep it split apart)
Fetching the author is as simple as just adding the id to the URL:
var userId = 123;
new Firebase('https://INSTANCE.firebaseio.com/users/'+userId);
To fetch a list of posts belonging to a certain user, either maintain an index of that users' posts:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
fb.child('/my_posts/'+userId).on('child_added', function(indexSnap) {
fb.child('posts/'+indexSnap.name()).once('value', function(dataSnap) {
console.log('fetched post', indexSnap.name(), dataSnap.val());
});
});
A tool like Firebase.util can assist with normalizing data that has been split for storage until Firebase's views and advanced querying utils are released:
/posts/$post_id/...
/my_posts/$user_id/$post_id/true
var fb = new Firebase('https://INSTANCE.firebaseio.com');
var ref = Firebase.util.intersection( fb.child('my_posts/'+userId), fb.child('posts') );
ref.on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val();
});
Or simply store the posts by user id (depending on your use case for how that data is fetched later):
/posts/$user_id/$post_id/...
new Firebase('https://INSTANCE.firebaseio.com/posts/'+userId).on('child_added', function(snap) {
console.log('fetched post', snap.name(), snap.val());
});

Object Arrays in Azure Table Storage

I'm trying to build a simple Mobile Service on Azure and I'm having some problems while inserting my information. Right now, I've got two classes in my model, User and Car. A User has an AccountID, a Name (all these Strings) and an Array of Car. A Car has a Plate, a Color and a Model (all these Strings).
I'm serializing the User object correctly to JSON and when I try to do request.execute() it throws an error that says "The value of property 'cars' is of type object which is not supported". I know that only string, number, bool and date are suppported.
What I'd like to do, is to have two separate tables, one for users and another one for cars, and somehow establish a relationship between them. This is the script I've written so far
function insert(item, user, request) {
if(item.accountID !== user.userId){
request.respond(statusCodes.UNAUTHORIZED,
"Unauthorized user");
} else {
if(item.cars.length){
var tableCars = tables.getTable('cars');
populateTable(tableCars, request, item.cars);
}
request.execute();
}
}
function populateTable(table, request, array){
var index = 0;
var insertNext = function(){
if(index < array.length){
var toInsert = array[index];
table.insert(toInsert, {
success: function(){
index++;
insertNext();
}
});
}
};
insertNext();
}
At this point I've got several problems. If I leave it this way, it crashes because items.cars is an Array of Car (an object for JS) but I do want to have here some kind of id to find cars that belong to this User in its table. Maybe I should add some kind to 'owner' to Car, but I'm not sure, my knowledge of databases is somehow poor.
What should I do?
Azure table storage does not support relational tables. Furthermore, ATS does not support storing of strongly typed child objects as a part of parent entities. ATS is a key-value entity-based table storage. It only supports basic data types like string, date, double, boolean, etc.
If you want to store complex objects in ATS (complex, meaning objects that contain other objects), it is suggested that you should serialize the child objects as strings rather then objects, when storing the data and de-serialize the strings back into objects during retrieval.
Alternatively, you can get very fancy with your Row/PartitionKeys and store Parent object and child objects as different entities within the same PartitionKey - and when reading the values back, reconstruct the hierarchy.

Resources