how to implement mutation responses on a local falcor Model dataset - falcor

Given that I have an example Model:
var model = new falcor.Model({
cache: {
userById: {
"1": {
name: "User",
email: "user#email.com"
}
},
users: {
current: null
}
}
});
This is a local model that I'm using for testing purposes, and I would like to implement it on a call to users.login so the user so that I can call:
model.call(['users', 'login'], ['user', 'password'])
I realized that if I do this:
var model = new falcor.Model({
cache: {
userById: {
"1": {
name: "User",
email: "user#email.com"
}
},
users: {
current: null,
login: function(user, password) {
console.log('this code is reached', user, password);
// what to return in order to mutate model?
}
},
}
});
When I do the call it gets there, but I can't figure out how to mutate the model as part of the response; on the server side we return the paths with values and invalidates, and it just works, but here I tried:
// trying returning as a jsonGraph response, don't work
login: function() {
return {
jsonGraph: {
users: {
current: {$type: "ref", value: ['userById', '1']}
}
},
paths: [['users', 'current']]
}
}
// trying returning as a path set mutation list, don't work
login: function() {
return [{path: ['users', 'current'], value: {$type: "ref", value: ['userById', '1']}}]
}
// trying force call to set on the model, don't work
login: function() {
this.set([
{path: ['users', 'current'], value: {$type: "ref", value: ['userById', '1']}}
])
}
// trying using ModelResponse, got an example on some external sources, don't work
login: funtion() {
return new ModelResponse((observer) => {
observer.onNext({
jsonGraph: {
users: {
current: {$type: "ref", value: ['userById', '1']}
}
},
paths: [['users', 'current']]
});
observer.onCompleted();
});
}
Now I don't know what else to try; I need a simple way to declare mutations after a call into a local model, if you know how to solve this, please let me know here.
Thanks.

The client model cache only supports JSONGraph, which b/c it is essentially just JSON with some conventions, doesn't support functions. So, when working with a falcor model cache and no dataSource/middle tier router, it is not possible to implement calls.
This can be kind of annoying when prototyping/testing, as a router is conceptually more difficult than a simple JSON cache object. I ran into this a while ago, so I wrote a dataSource module to support it: falcor-local-datasource. The dataSource is initialized with a graph object that does support function nodes, and as with your above examples, will mutate the graph based on the function's returned JSONGraphEnvelope or an array of PathValues.

Related

Loopback POST array of entry?

I want to insert 10 entries with one query against 10 queries.
I read that it's possible to do it by sending an array like this :
But I get this error:
Do I need to set something? I don't know what to do at all.
Repo with a sample : https://github.com/mathias22osterhagen22/loopback-array-post-sample
Edit:
people-model.ts:
import {Entity, model, property} from '#loopback/repository';
#model()
export class People extends Entity {
#property({
type: 'number',
id: true,
generated: true,
})
id?: number;
#property({
type: 'string',
required: true,
})
name: string;
constructor(data?: Partial<People>) {
super(data);
}
}
export interface PeopleRelations {
// describe navigational properties here
}
export type PeopleWithRelations = People & PeopleRelations;
The problem with your code was :
"name": "ValidationError", "message": "The People instance is not
valid. Details: 0 is not defined in the model (value: undefined);
1 is not defined in the model (value: undefined); name can't be
blank (value: undefined).",
Here in above as in your #requestBody schema, you are applying to insert a single object property, where as in your body are sending the array of [people] object.
As you can see in your people.model.ts you have declared property name to be required, so system finds for the property "name", which obviously not available in the given array of object as primary node.
As you are passing index array, so its obvious error that you don't have any property named 0 or 1, so it throws error.
The below is the code hat you should apply to get insert the multiple, items of the type.
#post('/peoples', {
responses: {
'200': {
description: 'People model instance',
content: {
'application/json': {
schema: getModelSchemaRef(People)
}
},
},
},
})
async create(
#requestBody({
content: {
'application/json': {
schema: {
type: 'array',
items: getModelSchemaRef(People, {
title: 'NewPeople',
exclude: ['id'],
}),
}
},
},
})
people: [Omit<People, 'id'>]
): Promise<{}> {
people.forEach(item => this.peopleRepository.create(item))
return people;
}
You can also use this below
Promise<People[]> {
return await this.peopleRepository.createAll(people)
}
You can pass the array of your people model by modifying the request body.If you need more help you can leave comment.
I think you have a clear solution now. "Happy Loopbacking :)"

Falcor - HTTPDataSource to post Json

Is it possible to post a Json file using the falcor.browser's model? I have used the get method in it. Below is what I require, but it is not working.
<script src="./js/falcor.browser.js"></script>
function registerUser() {
var dataSource = new falcor.HttpDataSource("http://localhost/registerUser.json");
var model = new falcor.Model({
source: dataSource
});
var userJson = {"name":"John","age":"35","email":"john#abc.com"};
model.
set(userJson).
then(function(done){
console.log(done);
});
This is the server.js code:
app.use('/registerUser.json', falcorExpress.dataSourceRoute(function (req, res) {
return new Router([
{
route: "rating",
get: function() {
// Post call to external Api goes here
}
}
]);
}));
A few things:
The Model's set() method takes 1+ pathValues, so reformat your userJson object literal into a set of pathValues. Something like:
model.
set(
{ path: ['users', 'id1', 'name'], value: 'John' },
{ path: ['users', 'id1', 'age'], value: 35 },
{ path: ['users', 'id1', 'email'], value: 'john#abc.com' }
).
then(function(done){
console.log(done);
});
Second, your router must implement set handlers to correspond to the paths you are trying to set. These handlers should also return pathValues:
new Router([
{
route: 'users[{keys:ids}]["name", "age", "email"]',
set: function(jsonGraph) {
// jsonGraph looks like { users: { id1: { name: "John", age: 35, email: "john#abc.com" }
// make request to update name/age/email fields and return updated pathValues, e.g.
return [
{ path: ['users', 'id1', 'name'], value: 'John' },
{ path: ['users', 'id1', 'age'], value: 35 },
{ path: ['users', 'id1', 'email'], value: 'john#abc.com' },
];
}
}
]);
Given that your DB request is likely asynchronous, your route get handler will have to return a promise or observable. But the above should work as a demonstration.
Edit
You can also use route pattern matching on the third path key if the number of fields gets large, as was demonstrated above on the second id key.
{
route: 'users[{keys:ids}][{keys:fields}]',
set: function(jsonGraph) {
/* jsonGraph looks like
{
users: {
id1: { field1: "xxx", field2: "yyy", ... },
id1: { field1: "xxx", field2: "yyy", ... },
...
}
}
*/
}
}

How do you define parentName, parentId, and connectionName in getConfigs for a nested connection?

I am running into some issues writing a Relay Mutation on a nested connection. Here is the type structure:
{
viewer {
entity(id) {
events // mutate connection here
}
}
}
In the getConfigs I have both the parentName and parentID pointing to the "viewer", however the connectionName "events" does not exist on the viewer it exists on the "entity" type.
As you will see in the code snippet below I am also unsure how to use variables in the getFatQuery to fetch the mutated data with an entity ID.
getConfigs and getFatQuery:
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'viewer',
parentID: this.props.viewer.id,
connectionName: 'events',
edgeName: 'eventEdge',
rangeBehaviors: {
'': 'append'
},
}];
}
getFatQuery() {
return Relay.QL`
fragment on addEventPayload {
viewer {
entity(id: $entityId) // how do I use variables in the getFatQuery {
events(first: 20) {
edges {
node {
status
}
}
}
}
},
eventEdge
}
`;
}
I am more than happy to help clarify my question if it makes no sense so please feel free to ask questions about my question.
Thanks for the help!
For anyone in the future getting snagged on this you don't have to worry about passing in the id again or dealing with nested queries/fields. Relay will just find and update the appropriate record on the client. This issue on Github was helpful for figuring this out, especially the comments from freiksenet.
In the example above - Instead of going through the viewer we just go straight to the entity.
outputFields:
outputFields: {
eventEdge: {
type: eventEdge,
resolve: async({event}) => {
const eventsByOwner = await Event.getEventsByOwnerId(event.ownerId)
const eventIndex = eventsByOwner.findIndex(evt => evt.id == event.id);
const cursor = offsetToCursor(eventIndex);
return {
cursor: cursor,
node: event
};
}
},
entity: {
type: entity,
resolve: async({event}) => {
return Entity.getEntity(event.ownerId)
}
},
}
getConfigs and getFatQuery:
getConfigs() {
return [{
type: 'RANGE_ADD',
parentName: 'entity',
parentID: this.props.entityId,
connectionName: 'events',
edgeName: 'eventEdge',
rangeBehaviors: {
'': 'append'
},
}];
}
getFatQuery() {
return Relay.QL`
fragment on addEventPayload #relay(pattern: true) {
entity {
events
},
eventEdge
}
`;
}
Note: Using #relay(pattern: true) will make sure you don't run into issues when you don't pass in arguments for connection queries and will fall back to your last query of this type.

What is the best way to bind a two dimensional object array to the grid that is also type safe

I have a kendo UI Grid from Telerik
I want to bind a two dimensional object array to the grid. I work in Visual Studio 2012 in ASP.NET MVC. I have a solution where I use a javascript solution. The datatype for the datasource is a two dimensional object array. This is because all the rows and columns need to be dynamic in our solution. Here's the JavaScript code to bind the grid:
function createGrid() {
var url = '#Url.Action("GetSheetData")';
$.get(url, { hospitalId: 100, screenCode: "Ledger", revisionId: 1, applicationUser: "TestUser" }, function (result) {
var columnDefs = result.Columns;
var data = result.Data;
// Now, create the grid using columnDefs as argument
var grid = $("#grid").kendoGrid({
dataSource: {
data: jQuery.parseJSON(data)
},
columns: columnDefs,
height: 430,
editable: "incell",
batch: true,
sortable: {
mode: "single",
allowUnsort: false
},
filterable: {
extra: false,
operators: {
string: { contains: "Contains" }
}
},
scrollable: {
virtual: true
},
navigatable: true
}).data("kendoGrid");
});
}
And the function to post the grid back to the server:
function saveGrid() {
var gridDataArray = $('#grid').data('kendoGrid')._data;
var url = '#Url.Action("SetSheetData")';
$.post(url, {
hospitalId: 100
, screenCode: "Ledger"
, revisionId: 1
, applicationUser: "TestUser"
, dataGrid: JSON.stringify(gridDataArray)
}
, function (result) {
var grid = $("#grid").data("kendoGrid");
});
}
The problem with this method is dat when we call the code:
var gridDataArray = $('#grid').data('kendoGrid')._data;
and post the data with:
JSON.stringify(gridDataArray);
all the items in the stringified object become string types. Even those who are numeric. I want my data to maintain the right datatypes
Does anyone know how to keep my grid data type safe?
Any other solutions that do not contain the JavaScript method are fine as well, as long as it supports a two dimensional object array as a type.
I hope the question is clear. Thanks in advance
1) _data is private (as per the _), you should not really be calling that! ds.data() will do the job.
2) the way you retrieving/storing data is not wrong, but I would suggest you to make your life easier and define kendo Transport properly :
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: "http://demos.kendoui.com/service/Products",
dataType: "jsonp"
}
}
3) as far as the types are involved, use model allowing you define types.
var dataSource = new kendo.data.DataSource({
schema: {
model: {
id: "ProductID",
fields: {
//data type of the field {Number|String|Boolean|Date} default is String
UnitPrice: {
type: "number",
defaultValue: 42
}
}
}
}
});

ExtJS4.1: Why model.save() sends wrong thing to server?

I tried to use model.save() to POST a new user. But I check request payload and found that it not only sent the data, but also sent other parts of the model. That makes my server cannot parse the payload.
The request payload generated :
{"phantom":true,"internalId":"ext-record-58","raw":{},"data":{"userId":0,"userName":"Amy"},"modified":{"userName":""},"hasListeners":{},"events":{},"stores":[],"dirty":true,"id":"AM.model.User-ext-record-58"}
But the desired request payload should be :
{"userId":0,"userName":"Amy"}
And I am aware that the "phantom" of my model is false before I call model.save(). But it becomes true in the request payload. Is it a clue?
Model:
Ext.define('AM.model.User',{
extend: 'Ext.data.Model',
fields: [
{ name: 'userId', type: 'int' },
{ name: 'userName', type: 'string' },
{ name: 'createdTime', type: 'string' },
],
idProperty: 'userId',
associations: [
{
type: 'hasOne',
model: 'AM.model.ModelA',
name:'modelA',
associationKey:'modelA',
getterName:'modelA'
},
{
type: 'hasOne',
model: 'AM.model.ModelB',
name:'modelB',
associationKey:'modelB',
getterName:'modelB'
}
],
proxy: {
type: 'rest',
success:true,
url:'../restful/users',
writer:{
type:'json',
getRecordData:function(record){ //parse createdTime to the format Y-m-d
record.set('createdTime', Ext.Date.format(new Date(record.get('createdTime')), "Y-m-d"));
return record;
}
},
reader: {
type: 'json'
}
}
});
This is the view which has the data to be posted. The view will fill the data to the model:
Ext.define('AM.view.UserRegisterForm',{
extend:'Ext.form.Panel.',
alias:'widget.userRegisterForm',
fields:new Array(), //I want to render the fields in xtemplate, so instead of adding the fields to items, I use an array to manage them.
retrieveData(model){
model.set('userName', this.fields[0].getValue());
model.set('createdTime',this.fields[1].getValue());
}
}
The function in the controller, which sends the POST request:
postUser:function(){
var userRegisterForm= this.getUserRegisterForm();
var userModel = this.getUserModel();
var user= new userModel();
var me = this;
userRegisterForm.retrieveFieldData(user);
console.log(user); //the data in console looks fine!
user.save({
success: function(response) {
//do something...
},failure:function(response) {
alert('fail');
}
});
}
You are returning the full record when you override getRecordData Where as you are just meant to return the records data. record.getData()
Some extra advice. Don't override getRecordData to set the models creation date. Use the models defaultValue property to give assign it a new Date if one doesn't exist.

Resources