Shorting list by name - Angular 5 + Firebase - list

I have created a service where I get all the elements of my database:
Service
getElements() {
return (this.eleList= this.firebase.list("elements"));
}
Component
eleList: Element[];
getBets() {
return this.databaseService
.getElements()
.snapshotChanges()
.subscribe(item => {
this.eleList= [];
item.forEach(element => {
let x = element.payload.toJSON();
x["$key"] = element.key;
this.eleList.push(x as Element);
});
});
}
With these two methods what I do is to store all my elements in this.eleList.
I would like to create a new method, named filterByName(name), where I would update this.eleList to an array which contains only the ones that contain namein the object, for example, this.eleList[1].name
I do not know if Firebase provides a way to short it, or I need to use Javascript/Typescript for it.

Firebase takes full advantage of the observables and async pipes.
You should take advantage of that :
eleList$ = new Subject();
getElements() {
this.this.firebase.list("elements")
.pipe(take(1))
.subscribe(list => this.eleList$.next(list));
}
getBets() {
this.databaseService
.getElements()
.snapshotChanges()
.pipe(
map(item => items.map(element => ({
...element.payload.toJSON(),
'$key': element.key
})))
)
.subscribe(elements => this.eleList$.next(list));
}
Now for a sorted list :
sortedList$ = this.eleList$.pipe(
map(elements => elements.filter(element => !!element.name))
);

Related

Unit testing sessionStorage value in emberJS

I'm new to ember and trying to figure out how to unit test, using sinon, the sessionStorage based on url parameters when that page is visited. I've tried a few things but still can't get the desired result. It passes even if I change the 'sessionValue' without editing the query param.
Thank you in advance.
ember component
beforeModel(transition) {
//transition will contain an object containing a query parameter. '?userid=1234' and is set in the sessionStorage.
if(transition.queryparam.hasOwnProperty('userid')){
sessionStorage.setItem('user:id', transition.queryparam)
}
}
Ember test
test('Session Storage contains query param value', async assert => {
let sessionKey = "user:id";
let sessionValue = "1234"
let store = {};
const mockLocalStorage = {
getItem: (key) => {
return key in store ? store[key] : null;
},
setItem: (key, value) => {
store[key] = `${value}`;
},
clear: () => {
store = {};
}
};
asserts.expect(1);
let spy = sinon.spy(sessionStorage, "setItem");
spy.calledWith(mockLocalStorage.setItem);
let stub = sinon.stub(sessionStorage, "getItem");
stub.calledWith(mockLocalStorage.getItem);
stub.returns(sessionValue);
await visit('/page?userid=1234');
mockLocalStorage.setItem(sessionKey, sessionValue);
assert.equal(mockLocalStorage.getItem(sessionKey), sessionValue, 'storage contains value');
})
Welcome to Ember!
There are many ways to test, and the below suggestion is one way (how I would approach interacting with the SessionStorage).
Instead of re-creating the SessionStorage API in your test, how do you feel about using a pre-made proxy around the Session Storage? (ie: "Don't mock what you don't own")
Using: https://github.com/CrowdStrike/ember-browser-services/#sessionstorage
Your app code would look like:
#service('browser/session-storage') sessionStorage;
beforeModel(transition) {
// ... details omitted ...
// note the addition of `this` -- the apis are entirely the same
// as SessionStorage
this.sessionStorage.setItem('user:id', ...)
}
then in your test:
module('Scenario Name', function (hooks) {
setupApplicationTest(hooks);
setupBrowserFakes(hooks, { sessionStorage: true });
test('Session Storage contains query param value', async assert => {
let sessionKey = "user:id";
let sessionValue = "1234"
let sessionStorage = this.owner.lookup('browser/session-storage');
await visit('/page?userid=1234');
assert.equal(sessionStorage.getItem(sessionKey), '1234', 'storage contains value');
});
})
With this approach, sinon isn't even needed :)

Using Linq (Select, Where...) On asynchronous list

I have an asynchronous list (toListAsync) and I need to perform a linq select statement on it but it doesn't work because Task does not contain a definition for select.
What can I do?
public async Task<List<FilesTable>> List()
{
using (var ctx = Configuration.OpenContext(false))
{
return await ctx.FilesTables.Include(e => e.Subsection).ToListAsync();
}
}
// The above is in the first file(FilesTable.cs),
// the below in a different file(file_apis.cs)
public async Task<List<FilesModel>> GetFiles()
{
return await new FilesTable().List().Select(e => (FilesModel)e).ToList();
}
Add using System.Linq; namespace.
Then you can do this:
(await yourMethod.ToListAsync()).Select(a => new { });
You should add parenthesis before await keyword and after ToListAsync() method
From performance PoV it is better to use filters first, then at the end use ToListAsync, so you can use:
MyList.Where(x => ...).Select(x => ...).ToListAsync();
Otherwise you will be loading unnecessary amount of data to the memory, and this will reduce the performance depending on the amount of data. But if you do filter before ToList only the required data will be loaded.
[Update]
With reference to your code, you can a new method to the FilesTable.cs that will do filter and return the desired list:
public async Task<List<FilesModel>> ListFiles()
{
using(var cts = Configuration.OpenContext(false))
{
return await ctx.FilesTables.Include(e => e.Subsection).Select(e => (FilesModel)e).ToListAsync();
}
}
You can use or brekets for waiting the iteration end for doing the select, or you can use IAsyncEnumerable
public async IAsyncEnumerable<FilesTable> List()
{
using (var ctx = Configuration.OpenContext(false))
{
return ctx.FilesTables.Include(e => e.Subsection).AsAsyncEnumerable();
}
}
public async IAsyncEnumerable<FilesModel> GetFiles()
{
await foreach(var value in new FilesTable().List())
{
yield return value;
}
}
// or using that nuget package System.Linq.Async NuGet package
public async Task<List<FilesModel>> GetFiles()
{
return await new FilesTable().List().ToListAsync();
}

Enzyme sub element has no 'find()'

I have a simple test where I am searching on '.shop' elements, returning two of them, and then I want to get the first element and look at an embedded element : '.shop__title'.
See code :
describe('Shop Page', () => {
let wrapper
let store = createStore(reducers, mockStoreData);
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<Dashboard />
</Provider>,
);
});
afterEach(() => {
wrapper.unmount();
});
it('test ', () => {
let elems = wrapper.find('.shop');
expect(elems.length).toBe(2); //yes, i have two shops!
let e = elems.get(0);
e.find('.shop__title') //find function does not exist!!!
})
});
I am not quite sure what this 'e' element is (the first element in the array that I am looking at), but my IDE shows me this :
What sort of object is this? How do I get the 'find' and 'simulate' functions working on this element?
You need to use at instead of get
let e = elems.at(0);
e.find('.shop__title') //find function does not exist!!!
The problem is get returns you the ReactElement but at returns a ShallowWrapper which has the find method.

Refetch queries with any combination of parameters

I have faced with a problem when refetching queries after mutation. If query has no parameters thats ok, but if query has several parameters, and different pages uses different of them. For example, GET_ITEMS query accepts parameters: userId, companyId, categoryId. How can I say to Apollo to refetch all this queries with any combination of parameters?
It seem there is no way I can make it now with Apollo Client. So I had to save the parameters of all GET_ITEMS calls from all pages, and then transfer the saved parameters to the refetchQueries mutation method. The code turned out like this:
ItemsContext.js
const ItemsContext = React.createContext({
cachedQueryVars: [],
});
ItemsList.js
...
render() {
...
return <ItemsContext.Consumer>{({cachedQueryVars}) => {
cachedQueryVars.push(variables);
return <Query query={GET_ITEMS} variables={variables} >
...
ItemEdit.js
...
render() {
...
return <ItemsContext.Consumer>{({cachedQueryVars}) =>
<Mutation mutation={UPDATE_ITEM_MUTATION}
refetchQueries={({data}) => this.handleRefetchQueries(data.updateItem, cachedQueryVars)}
...
}
handleRefetchQueries(newItem, cachedItemsQueryVars) {
let result = [];
let filtered = null;
if(this.state.oldCategoryId != newItem.category.id) {
filtered = cachedItemsQueryVars.filter(v => v.categoryId == this.state.oldCategoryId);
result = this.concatItemQueryVars(result, filtered);
filtered = cachedItemsQueryVars.filter(v => v.categoryId == newItem.category.id);
result = this.concatItemQueryVars(result, filtered);
}
if(this.state.oldCompanyId != newItem.company.id) {
filtered = cachedItemsQueryVars.filter(v => v.companyId == this.state.oldCompanyId);
result = this.concatItemQueryVars(result, filtered);
filtered = cachedItemsQueryVars.filter(v => v.companyId == newItem.company.id);
result = this.concatItemQueryVars(result, filtered);
}
...
return result;
}
concatItemQueryVars(result, filtered) {
return result.concat(filtered.map(v => ({
query: GET_ITEMS,
variables: v
})));
}

Problem detect observable OnCompleted in RX

I have problem with detect onComplited action in RX.
I'm loading data to ListBox from web service.
Basic situation is working great: - "Complited item" is on bottom
App.ViewModel.LoadData();
IObservable<WebServiceClass.ItemGetValues> observable = App.ViewModel.Items
.ToObservable(Scheduler.NewThread);
var items = new ObservableCollection<WebServiceClass.ItemGetValues>();
ListBox1.ItemsSource = items;
observable.ObserveOnDispatcher().Subscribe(
item => { items.Add( new WebServiceClass.ItemGetValues(item.nazwa, item.skrot, item.id) ); },
() => { items.Add( new WebServiceClass.ItemGetValues("Complited", "", "") ); }
);
But when I call web service for each item with code below the "Complited item" is added at first
App.ViewModel.LoadData();
IObservable<WebServiceClass.ItemGetValues> observable = App.ViewModel.Items
.ToObservable(Scheduler.NewThread);
var items = new ObservableCollection<WebServiceClass.ItemGetValues>();
ListBox1.ItemsSource = items;
observable.ObserveOnDispatcher().Subscribe
(item =>
{
//items.Add(item);
var request = Observable.FromAsyncPattern<string, string>(client.BeginGetLastValue, client.EndGetLastValue);
request(item.skrot).ObserveOnDispatcher().Subscribe(
(it) =>
{
Func<string, WebServiceClass.ItemGetValues> deserializeFirst = r =>
((List<WebServiceClass.ItemGetValues>)JsonConvert
.DeserializeObject(r,
typeof(List<WebServiceClass.ItemGetValues>)))
.First();
item.zm_dzienna = deserializeFirst(it).zm_dzienna;
items.Add(item);
}
);
},
() => { items.Add(new WebServiceClass.ItemGetValues("Complited", "Complited", "0")); }
);
How can I properly detect onComplited action?
Solution
Problem with nullReference exception in deserializeFirst func.
App.ViewModel.LoadData();
IObservable<WebServiceClass.ItemGetValues> observable = App.ViewModel.Items
.ToObservable(Scheduler.NewThread);
var items = new ObservableCollection<WebServiceClass.ItemGetValues>();
ListBox1.ItemsSource = items;
var request = Observable.FromAsyncPattern<string, string>(client.BeginGetLastValue, client.EndGetLastValue);
observable.SelectMany(
item => request(item.skrot).Select(it => {
Func<string, WebServiceClass.ItemGetValues> deserializeFirst = r =>
((List<WebServiceClass.ItemGetValues>)JsonConvert
.DeserializeObject(r,
typeof(List<WebServiceClass.ItemGetValues>)))
.First();
item.zm_dzienna = deserializeFirst(it).zm_dzienna;
return item;
})
).SubscribeOnDispatcher().Subscribe(
result => { Dispatcher.BeginInvoke(delegate { items.Add(result); }); },
() => { Dispatcher.BeginInvoke(delegate { items.Add(new WebServiceClass.ItemGetValues("c","c","c")); }); }
);
Best regards,
Ɓukasz
You need to pull the inner query into to Rx sequence, not run it from your subscription. Doing so gives you the power of Rx, of composability and testability. And gets you the results you want.
This is a start towards what is needed.
Func<string, WebServiceClass.ItemGetValues> deserializeFirst = r =>
((List<WebServiceClass.ItemGetValues>)JsonConvert
.DeserializeObject(r,typeof(List<WebServiceClass.ItemGetValues>)))
.First();
var request = Observable.FromAsyncPattern<string, string>(client.BeginGetLastValue,
client.EndGetLastValue);
observable
.SelectMany((item) => request(item.skrot))
.Select((it) => item.zm_dzienna = deserializeFirst(it).zm_dzienna)
.ObserveOnDispatcher()
.Subscribe(
item => items.Add(item),
() => items.Add(new WebServiceClass.ItemGetValues("Complited", "Complited", "0")));