Relay Modern- Object passed into QueryRenderer as variable breaks when edited with RefetchContainer - relayjs

Passing an object into QueryRenderer variable. In the refetch container rendered on success, when anything changes in the object, the container breaks. Also passing an integer as a variable that doesn't break on change via refetch.
i.e.-
<QueryRenderer
query={query}
variables={
count: 5,
testObject= {
something1: {
something2: ['something', 'something']
}
}
...
/>
Refetch container calls via
_onRefetch() => {
const newSearch = {
something1: {
something2: ['somethingElse'],
}
};
const refetchVariables = fragmentVariables => ({
testObject: newSearch,
count: this.state.currentCount + 10,
});
nullthrows(this.props.relay).refetch(
refetchVariables,
null);
}
Does relay modern refetch not handle objects, or is there something I'm doing wrong?

Your variables should be in double braces:
<QueryRenderer
query={query}
variables={{
count: 5,
testObject: {
something1: {
something2: ['something', 'something']
}
}
...
}}
...
/>
https://facebook.github.io/relay/docs/query-renderer.html

Ended up needing to rerender the QR from a parent component when the query level variables changed.

Related

How to compare two json responses and see the differences in Postman?

I need to create tests in Postman where I compare two responses for two different requests and I want to see what is (if there is anything) the difference between them.
Case is that I can get json response which can contain anything, then I need to check if on different environment the same request gave the same response.
Right now I do it that way:
In first request I save responsee:
pm.globals.set('response', pm.response.json());
In second request I compare response with saved one with:
pm.test('Should have identical responses as previous', () => {
pm.expect(pm.response.json()).to.deep.equal(pm.globals.get('response'));
});
But in this case I just see if there is any difference, so I have to go through a lot of lines each time to find what was wrong.
What I need to get is when I have first response like:
[
{
color: "red",
value: "#f00"
},
{
color: "green",
value: "#0f0"
}
]
And second like:
[
{
color: "red",
value: "#f00"
},
{
color: "green",
value: "#0f2"
}
]
I want to get info in run results like:
there is difference in line: value: "#0f2"
or
in first response there was value: "#0f0" and in second there is value: "#0f2"
Is it even possible to do?
Ok, the solution that I worked with:
In first request I saved response with
pm.globals.set('respa', pm.response.json());
In second I used function to find differences:
function diff(obj1, obj2) {
const result = {};
if (Object.is(obj1, obj2)) {
return undefined;
}
if (!obj2 || typeof obj2 !== 'object') {
return obj2;
}
Object.keys(obj1).concat(Object.keys(obj2)).forEach(key => {
if (obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) {
result[key] = obj2[key];
}
if (typeof obj2[key] === 'object' && typeof obj1[key] === 'object') {
const value = diff(obj1[key], obj2[key]);
if (value !== undefined) {
result[key] = value;
}
}
});
return result;
}
And then also in second response I've added 'if' which create test when there is a difference in responses, and that test is named as difference, and also it's saved in console as json.
pm.globals.set('respb', pm.response.json());
if (!Object.is(pm.globals.get('respb'), pm.globals.get('respa'))) {
const result = diff(pm.globals.get('respb'), pm.globals.get('res12'));
console.log(result);
pm.test('Difference' + JSON.stringify(result), () => {
pm.expect(0).to.equal(pm.globals.get(1));
});
}

Returning recursive result

I currently have this in a simple MVC Api controller:
var rootFolder = Umbraco.TypedMedia(200);
return rootFolder.Children().Select(s => new MediaItem
{
Name = s.Name,
Children = s.Children.Select(e => new MediaItem
{
Name = e.Name
})
});
It works, but only return level 1 and 2.
I tried using
return rootFolder.Descendants(), which returns all results from all levels - but "flattened out", so I cannot see the structure in the output.
The output is used in a simple app, navigating a tree structure.
Any ideas, as to how I can make it recursive?
Using Descendants, the output is returned like this
[
{
"Name":"dok1"
},
{
"Name":"dok2"
},
{
"Name":"dok21"
}
]
But it should be
[
{
"Name":"dok1"
},
{
"Name":"dok2"
"Children": [
{
"Name":"dok21"
}
]
}
Not sure you really need recursion here -- the solution below (or something similar) should suffice
// Dictionary between level/depth(int) and the files on that level/depth
var fileDictionary = new Dictionary<int, List<MediaItem>>();
var children = rootFolder.Children();
var depth = 1;
while (children.Any())
{
var tempList = new List<MediaItem>();
children.ForEach(child => {
tempList.Add(child);
});
fileDictionary.Add(depth, tempList);
children = children.Children();
depth++;
}
Then, you can do something like:
foreach (var key in fileDictionary.Keys)
{
// Access the key by key.Key (key would be "depth")
// Access the values by fileDictionary[key] (values would be list of MediaItem)
}
Why not just create a recursive function like so?
IEnumerable<MediaItem> ConvertToMediaItems(IEnumerable<IPublishedContent> items)
{
return items?.Select(i => new MediaItem
{
Name = i.Name,
Children = ConvertToMediaItems(i.Children)
}) ?? Enumerable.Empty<MediaItem>();
}
Then the usage would be
var rootFolder = Umbraco.TypedMedia(200);
return ConvertToMediaItems(rootFolder.Children());
You can also make the function a local function if it's only needed in one place.

Cannot read property 'length' of null when using MatPaginator

I keep on getting this error:
core.js:1542 ERROR TypeError: Cannot read property 'length' of null
at MatTableDataSource.push../node_modules/#angular/material/esm5/table.es5.js.MatTableDataSource._filterData (table.es5.js:702)
at MapSubscriber.project (table.es5.js:657)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/operators/map.js.MapSubscriber._next (map.js:35)
at MapSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at CombineLatestSubscriber.push../node_modules/rxjs/_esm5/internal/observable/combineLatest.js.CombineLatestSubscriber.notifyNext (combineLatest.js:83)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/InnerSubscriber.js.InnerSubscriber._next (InnerSubscriber.js:15)
at InnerSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.Subscriber.next (Subscriber.js:54)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/BehaviorSubject.js.BehaviorSubject._subscribe (BehaviorSubject.js:22)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Observable.js.Observable._trySubscribe (Observable.js:42)
at BehaviorSubject.push../node_modules/rxjs/_esm5/internal/Subject.js.Subject._trySubscribe (Subject.js:89)
I get my data from a service that gets from a database. It functions just fine. What I mean is the functionality is right. I can do the paging it's just that I get this error in the inspector. Please see my code below:
ngOnInit() {
if (this.genParams.buildingId === null || this.genParams.buildingId === undefined) {
this.router.navigate(['/main/viewproperties']);
}
this.buildingId = this.genParams.buildingId;
this.getUnits();
}
public getUnits() {
this.isLoading = true;
this.unitService.newGetUnits(this.buildingId)
.subscribe((data: any) => {
this.ELEMENT_DATA = data;
this.dataSource = new MatTableDataSource(this.ELEMENT_DATA);
this.dataSource.paginator = this.paginator;
this.isLoading = false;
console.log(this.ELEMENT_DATA);
}, err => {
console.log(err);
});
}
This is in my html:
<mat-paginator [pageSizeOptions]="[1, 5, 10, 20]" showFirstLastButtons></mat-paginator>
I would really appreciate your help.
I suspect that this is a scope issue. Try adding this to the class, not a function. You refer to this.datasource but setting it up as new in the function so I assume you just declare the var in the class. I've seen this error many times and it is when my dataSource is not receiving data but you seem to indicate that your table is functioning with data.
Stackblitz example
// For data table operations.
private dataSource = new MatTableDataSource();
private dataLength: number;

breeze observableArray binding - are properties observable?

I have a viewmodel which consists of a list(foreach loop) of DoctorPrices and when clicking on an item in the list it open up a CRUD form on the side. However when i update the values on the CRUD the observableArray that is bound to the foreach is not refreshing? (although the values are updates in the DB correctly)
From my data access module i call the following query.
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
return manager.executeQueryLocally(query);
}
In my viewmodel i have the following code:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
services is bound using a foreach loop (not posting here as the code is simple and works)
When i click on a one of the DoctorPrices it gets the data as follows and places it in an observable:
this.selectedPrice = function (data, event) {
self.currentService(data);
self.showEdit(true);
};
I then bind selectPrice to a simple form that has the properties on it to be modified by the user. I then call manager.SaveChanges().
This results in the following problem: the value is being updated correctly but the GUI / Original List that is bound in the foreach is not being updated? Are the properties in breeze not observables? What is the best way to work with something like this.
I thought of a workaround and changing the code with something like this:
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
});
But i feel that clearing the array in that way is sloppy and not the right way of doing things specially with long lists.
Can someone please point me in the right direction on how to bind observableArray properties properly so they are updated?
Additional code my VM Component:
function services() {
var self = this;
this.showForm = ko.observable(false);
this.currentService = ko.observable();
this.services = ko.observableArray(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
this.title = ko.observable();
doctorList.viewModel.instance.currentDoctorID.subscribe(function() {
self.services([]);
self.services(doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID()));
self.showDetails(false);
});
this.show = function (value) {
self.showForm(value);
};
this.showDetails = ko.observable(false);
this.addNewService = function() {
self.currentService(doctorServices.createService(doctorList.viewModel.instance.currentDoctorID()));
console.log(self.currentService().entityAspect.entityState);
self.showDetails(true);
};
this.showDelete = ko.computed(function() {
if (self.currentService() == null)
return false;
else if (self.currentService().entityAspect.entityState.isDetached()) {
self.title('Add new service');
return false;
} else {
self.title('Edit service');
return true;
}
});
this.deleteService = function() {
self.currentService().entityAspect.setDeleted();
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
};
this.closeDetails = function () {
doctorServices.manager.rejectChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.showDetails(false);
};
this.selectService = function (data, event) {
self.currentService(data);
self.showDetails(true);
};
this.saveChanges = function () {
console.log(self.currentService().entityAspect.entityState);
if (self.currentService().entityAspect.entityState.isDetached()) {
doctorServices.attachEntity(self.currentService());
}
console.log(self.currentService().entityAspect.entityState);
doctorServices.saveChanges();
doctorList.viewModel.instance.currentDoctorID.notifySubscribers();
self.currentService.notifySubscribers();
self.showDetails(true);
};
}
return {
viewModel: {
instance: new services()
},
template: servicesTemplate,
};
Below is my Breeze Data Class:
define('data/doctorServices', ['jquery', 'data/dataManager', 'knockout','mod/medappBase', 'breeze', 'breeze.savequeuing'], function ($, manager, ko,base, breeze, savequeuing) {
var services = ko.observableArray([]);
return {
attachEntity:attachEntity,
getServices: getServices,
services: services,
manager:manager,
getDoctorServices: getDoctorServices,
getServiceById: getServiceById,
createService:createService,
hasChanges: hasChanges,
saveChanges: saveChanges
};
function getServices() {
var query = breeze.EntityQuery.from("DoctorPrices");
return manager.executeQuery(query).then(function (data) {
services(data.results);
}).fail(function (data) {
console.log('fetch failed...');
console.log(data);
});;
}
function getDoctorServices(doctorId) {
var query = breeze.EntityQuery
.from('DoctorPrices')
.where('DoctorID', 'eq', doctorId).orderBy('ListOrder');
var set = manager.executeQueryLocally(query);
return set;
}
function getServiceById(serviceId) {
return manager.createEntity('DoctorPrice', serviceId);
//return manager.getEntityByKey('DoctorPrice', serviceId);
}
function handleSaveValidationError(error) {
var message = "Not saved due to validation error";
try { // fish out the first error
var firstErr = error.innerError.entityErrors[0];
message += ": " + firstErr.errorMessage;
base.addNotify('error', 'Could not save.', message);
} catch (e) { /* eat it for now */ }
return message;
}
function hasChanges() {
return manager.hasChanges();
}
function attachEntity(entity) {
manager.addEntity(entity);
}
function createService(doctorId) {
return manager.createEntity('DoctorPrice', { DoctorPricingID: breeze.core.getUuid(), DoctorID:doctorId }, breeze.EntityState.Detached);
};
function saveChanges() {
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(saveResult) {
base.addNotify('success', 'Saved.', 'Your updates have been saved.');
}
function saveFailed(error) {
var reason = error.message;
var detail = error.detail;
if (error.innerError.entityErrors) {
reason = handleSaveValidationError(error);
} else if (detail && detail.ExceptionType &&
detail.ExceptionType.indexOf('OptimisticConcurrencyException') !== -1) {
// Concurrency error
reason =
"Another user, perhaps the server, " +
"may have deleted one or all of the settings." +
" You may have to restart the app.";
} else {
reason = "Failed to save changes: " + reason +
" You may have to restart the app.";
}
console.log(error);
console.log(reason);
}
}
});
Please note this is my frist attempt at both a data class and VM. At the moment i am relying heavily on clearing the array ([]) and using notifySubscribers to make the array refresh :(
I bet you're missing an observable somewhere. I can't tell because you keep hopping from property to property whose definition is not shown.
For example, I don't know how you defined this.currentService.
I'm confused by this:
this.services = ko.computed(function() {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
});
Why is it a ko.computed? Why not just make it an observable array.
self.service = ko.observableArray();
// ... later replace the inner array in one step ...
self.service(doctorServices.getDoctorServices(
doctorList.viewModel.instance.currentDoctorID()));
I urge you to follow the observability trail, confident that your Breeze entity properties are indeed observable.
vm.selectedPrice = ko.dependentObservable(function () {
return doctorServices.getDoctorServices(doctorList.viewModel.instance.currentDoctorID());
}, vm);
vm is ur model on which u applied bindings , try this it will work.

How to use Q.js with breeze

This is my first attempt at using q.js. It appears to work, I have data being retrieved and then my function is called. The problem is the data is not being passed to the function. Is it a syntax problem or am I misusing Q?
getCategories = function (observable) {
var query = breeze.EntityQuery
.from("Categories")
.orderBy('Order');
Q(executeLocalQuery(query))
.then(processResult);
function processResult(data) { //data = undefined
if (data.results.length)
return observable(data.results)
else
return observable(create('Item', { CategoryId: id, Name: 'Add12', ImageName: 'icon.png', Order: '999' })); //create add thumbnail if zero records
};
},
executeLocalQuery = function (query) {
manager.executeQuery(query.using(breeze.FetchStrategy.FromLocalCache))
.then(localFetchSucceeded)
.fail(queryFailed);
function localFetchSucceeded(data) {
return data;
}
},
Solved it! No need to use Q when at executeLocalQuery. Changes are in the called function. Deferred waits for data.results and the resolves the defer. Then a promise is returned back to the caller. The caller can then process the .then and retrieve the data.
getCategories = function (observable) {
var query = breeze.EntityQuery
.from("Categories")
.orderBy('Order');
executeLocalQuery(query)
.then(processResult);
function processResult(data) {
if (data.results.length)
return observable(data.results)
else {
var addThumbnail = create('Category', { CategoryId: generateGUID(), Name: 'Add', ImageName: 'icon.png', Order: '999' });
observable(addThumbnail);
}
};
},
executeLocalQuery = function (query) {
var deferred = Q.defer();
manager.executeQuery(query.using(breeze.FetchStrategy.FromLocalCache))
.then(localFetchSucceeded)
.fail(queryFailed);
function localFetchSucceeded(data) {
deferred.resolve(data);
};
return deferred.promise;
},

Resources