I'm very new to developing mobile applications with telerik appbuilder. There are some things I have a hard time to understand with fetching data from Everlive.
Lets consider a simple example. Lets say I have Blog Posts and Comments that belong to those Posts. And both Posts and Comments are made by Users.
In one view I want to present the Post with corresponding Comments and I also need the Username of the User who posted the Comment (Comment table only contains userId).
Both the Post and the Comments are easy to fetch since I have the id of the Post. But how do I fetch the corresponding user for each Comment?
The FriendsApp example does something very similar but it uses this line to get the user:
var user = $.grep(app.Users.users(), function (e) {
return e.Id === userId;
})[0];
This fetches all users and filters them client side? I guess this is okay if you have like 10 users. But what if you have a million users?
I am guessing that the FriendsApp uses this way of resolving the relations just for keeping the simplicity of the sample. Everlive offers a far more meaningful toolset for resolving relation fields called Expand. You can explore the REST API here:
http://docs.telerik.com/platform/backend-services/development/rest-api/relations/simple-expanding
or the JS SDK function here:
http://docs.telerik.com/platform/backend-services/development/javascript-sdk/relations/simple-expanding.
As the Friends app uses the Kendo UI data source component you can send an Expand header with the request. The following configuration of the data source will return the DisplayName of the user in each Activity/Comments entity:
var expandObject = {
"CreatedBy": {
"ReturnAs": "User",
"SingleField": "DisplayName"
}
};
var dataSource = new kendo.data.DataSource({
type: "everlive",
transport: {
typeName: 'Activities', // replace this with Comments
read: {
beforeSend: function (xhr) {
xhr.setRequestHeader("X-Everlive-Expand", JSON.stringify(expandObject))
},
}
},
schema: {
model: {
id: Everlive.idField
}
}
});
dataSource.fetch(function (data) {
console.log(data.items);
});
Same could be applied for resolving the comments for each Blog post. Given the Friends data schema you will need to use the External Relation resolver of the Everlive API. Note that it is available only in a GetById scenario, e.g. when retrieving an Activity by Id, you can resolve the Comments that point to this activity, which is generally very handy in master-detail views.
Related
Hi I’m migration from strapi v3 to v4. I’m stuck for quite sometime regarding population of relations in v4.
BEHAVIOUR IN V3
Previously in v3 if I queried a service, it populated uptill the 2nd level by default if I’m not wrong, and in the second level if returned foreign keys from the further nested tables/relations respectively.
Example case I have following relations:
activity → cities → province , [images]
Querying activity like this in code:
const activity = await strapi.services.activity.findOne({ id });
would return activity → cities → { provinceforeignkey , images }
e.g. sample response
{
id: activity_id,
cities: [
id,
province: id_of_province,
images: [
// IMAGES DETAILED RESPONSE
]
]
}
BEHAVIOUR IN V4
I’m not able to get the desired response as above either by direct querying:
const activity = await strapi
.service("api::activity.activity")
.findOne(id,populate: ["cities"]);
Or by using entityService:
await strapi.entityService.findOne(
"api::activity.activity",
id,
populate: ["cities"]
);
I know we can use populate field to populate the desired relation, but on second level e.g. province case inside cities I need the id of province not the entire object population.
This is really important to us as we’ve tonnes of custom apis and custom code already implemented in v3 as per the following approach, otherwise we would have to go and change each case specifically in frontend and backend.
Can anyone guide me on this?
the entityService and db.query accept fields parameter that is undocumented but may work? However, what i would recommend is doing your own normalize function like:
let activities = await strapi.db.query('api::activity.activity')
.findOne({where: { id }, populate: { cities: { populate: ['state'] } } );
return normalizeManyActivities(activities)
and
const normalizeActivity = (activity) =>
({...activity,
cities: activity.cities.map(city =>
({...city, state: city.state.id })
})
);
const normalizeManyActivities = (activities) =>
activities.map(activity => normalizeActivity(activity));
and the second approach is to use middleware's witch you can take reference from here:
https://www.youtube.com/watch?v=YIbhKm1o0fE
Lets say i create an entry where i can select two collections like this
now , how to detec when someone changed first collection and then apply some filter to data in second collection ?
not sure if you're still looking for the answer but your question ranked high on my search so here's an answer for future visitors.
Say you add a post entry for your Posts collection. In the ./api/post/models/post.js you can create a hook like this
'use strict';
module.exports = {
lifecycles: {
async afterCreate(result, data) {
const id = result.streakID;
const streak = strapi.services.streaks.findOne({ id });
strapi.services.streaks.update({ id }, { counter: streak.counter++ });
},
},
};
My source
Best regards
EDIT: the hook runs on Post creation and accesses the Streak model by way of strapi.services.streaks. To be clear.
Beginner question : I've worked through the Try Meteor tutorial. I've got fields in my HTML doc, backed by helper functions that reference collections, and BOOM --> the fields are updated when the data changes in the DB.
With the "Hide completed" checkbox, I've also seen data-binding to a session variable. The state of the checkbox is stored in the Session object by an event handler and BOOM --> the list view is updated "automatically" by its helper when this value changes. It seems a little odd to be assigning to a session object in a single page application.
Through all this, my js assigns nothing in global scope, I've created no objects, and I've mostly seen just pipeline code, getting values from one spot to another. The little conditional logic is sprayed about wherever it is needed.
THE QUESTION... Now I want to construct a model of my business data in javascript, modelling my business rules, and then bind html fields to this model. For example, I want to model a user, giving it an isVeryBusy property, and a rule that sets isVeryBusy=true if noTasks > 5. I want the property and the rule to be isolated in a "pure" business object, away from helpers, events, and the meteor user object. I want these business objects available everywhere, so I could make a restriction, say, to not assign tasks to users who are very busy, enforced on the server. I might also want a display rule to only display the first 100 chars of other peoples tasks if a user isVeryBusy. Where is the right place to create this user object, and how do I bind to it from my HTML?
You can (and probably should) use any package which allows you to attach a Schema to your models.
Have a look at:
https://github.com/aldeed/meteor-collection2
https://github.com/aldeed/meteor-simple-schema
By using a schema you can define fields, which are calculated based on other fields, see the autoValue property: https://github.com/aldeed/meteor-collection2#autovalue
Then you can do something like this:
// Schema definition of User
{
...,
isVeryBusy: {
type: Boolean,
autoValue: function() {
return this.tasks.length > 5;
}
},
...
}
For all your basic questions, I can strongly recommend to read the DiscoverMeteor Book (https://www.discovermeteor.com/). You can read it in like 1-2 days and it will explain all those basic questions in a really comprehensible way.
Best Regards,
There is a very good package to implement the solution you are looking for. It is created by David Burles and it's called "meteor-collection-helper". Here it the atmosphere link:
You should check the link to see the examples presented there but according to the description you could implement some of the functionality you mentioned like this:
// Define the collections
Clients = new Mongo.Collection('clients');
Tasks = new Mongo.Collection('tasks');
// Define the Clients collection helpers
Clients.helpers({
isVeryBusy: function(){
return this.tasks.length > 5;
}
});
// Now we can call it either on the client or on the server
if (Meteor.isClient){
var client = Clients.findOne({_id: 123});
if ( client.isVeryBusy() ) runSomeCode();
}
// Of course you can use them inside a Meteor Method.
Meteor.methods({
addTaskToClient: function(id, task){
var client = Clients.findOne({_id: id});
if (!client.isVeryBusy()){
task._client = id;
Tasks.insert(task, function(err, _id){
Clients.update({_id: client._id}, { $addToSet: { tasks: _id } });
});
}
}
});
// You can also refer to other collections inside the helpers
Tasks.helpers({
client: function(){
return Clients.findOne({_id: this._client});
}
});
You can see that inside the helper the context is the document transformed with all the methods you provided. Since Collections are ussually available to both the client and the server, you can access this functionality everywhere.
I hope this helps.
I've created a OData V4 Service described in the articles on the ASP.NET Homepage.
I basically have a table Events where I assign Guests to. I need additional information to this many to many relationship, so I have created a EventGuest table.
Inserting Events and inserting Guests via OData just works fine. It just doesn't want to work, as soon as I want to insert related entities.
This is my Controller-Code:
public async Task<IHttpActionResult> Post(EventGuest eventGuest)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_db.EventGuest.AddOrUpdate(eventGuest);
await _db.SaveChangesAsync();
return Created(eventGuest);
}
And this is how I want to insert the relationship. For the Client Code I use the official T4 template.
var ev = container.Event.FirstOrDefault();
var guest = container.Guest.FirstOrDefault();
var evGuest = new EventGuest();
evGuest.Guid = Guid.NewGuid();
container.AddObject("EventGuest", evGuest);
container.SetLink(evGuest, "Event", ev);
container.SetLink(evGuest, "Guest", guest);
container.SaveChanges();
The request sent to the Server doesn't look too bad for me:
{"#odata.type":"#Entities.EventGuest","CreationTimestamp":"0001-01-01T00:00:00Z","Guid":"adf500e3-e3a1-4841-883e-2322ed863321","ID":0,"Event#odata.bind":"http://localhost/odata/Event(1)","Guest#odata.bind":"http://localhost/odata/Guest(1)"}
So the Server tries to use #odata.bind, but unfortunately in the POST-Method of the Controller the referenced entities "Guest" and "Event" are null.
Have you tried following the example at the MSDN Documentation? I noticed it always uses AddLink rather than SetLink, even in the SetLink documentation.
MSN Article is here: DataServiceContext.SetLink Method
In my Ext JS 4.1 application, I have a model Project with an belongsTo association to ProjectCategory:
Ext.define('MyApp.model.Project', {
extend: 'Ext.data.Model',
fields: ['id', 'project_category_id', 'title'],
belongsTo: {
model: 'MyApp.model.ProjectCategory',
associationKey: 'ProjectCategory',
getterName: 'getCategory'
}
// ...
});
Ext.define('MyApp.model.ProjectCategory', {
extend: 'Ext.data.Model',
fields: ['id', 'title']
});
The project is read via a Direct proxy, and ProjectCategory details are included as nested values in the response (mostly for display purposes). When loading a store, the associated data is read correctly, and I'm able to read the ProjectCategory's title in a grid via a custom renderer:
renderer: function(v, p, rec) {
return v ? rec.getCategory().get('title') : '';
}
However, when editing and saving the parent Project record through form.updateRecord(record), the associated record's fields aren't updated with values from the server response, unlike the Project's "native" fields. So when changing the project_category_id, even though the server will respond with a correctly nested ProjectCategory field containing the new category, getCategory will still return the old category.
Why isn't this updated from the server response, and what do I have to do to update it?
I already found out that in the store's write listener, I have access to the record and the returned association data. But I can't figure out how to update the record silently without triggering any other events.
It seems like I found my solution. Add this to the Project store:
listeners: {
write: function(store, operation, opt) {
var c = operation.records[0].getCategory();
c.set(operation.response.result.ProjectCategory);
c.commit(true);
}
}
The key element is the commit(true) call, which will skip notifying the store/proxy about the changes.
It's still a bummer that this isn't done automatically, though.