Auto-updating model list with custom sorting - sorting

I am trying to apply custom sorting to the models; however, using the sort computed property is sorting the models on the initial load of the page but it is not auto-updating the page when more models are entered into the store.
Ember-Source: 4.7.0
Ember-Data: 4.7.3
Ember-CLI: 4.6.0
Ember-CLI-Typescript: 2.4.0
app/models/item.ts:
import Model, { attr } from '#ember-data/model';
export default class Item extends Model {
#attr('string')
declare name: string;
}
// DO NOT DELETE: this is how TypeScript knows how to look up your models.
declare module 'ember-data/types/registries/model' {
export default interface ModelRegistry {
'item': Item;
}
}
app/routes/index.ts:
import Route from '#ember/routing/route';
import { service } from '#ember/service';
import type Store from '#ember-data/store';
export default class Index extends Route {
#service
declare store : Store;
async model(){
return this.store.peekAll('item');
}
}
app/controllers/index.ts:
import Controller from '#ember/controller';
import { sort } from '#ember/object/computed';
import Item from 'sorting-test/models/item';
const sortNumeric = (a: Item, b: Item) => parseInt(a.id, 10) - parseInt(b.id, 10);
export default class Index extends Controller {
#sort("model", sortNumeric)
declare sortedModels: Item[]
}
// DO NOT DELETE: this is how TypeScript knows how to look up your controllers.
declare module '#ember/controller' {
interface Registry {
'index': Index;
}
}
app/templates/index.hbs:
{{#model.length}}:{{this.sortedModels.length}}
tests/acceptance/index-test.ts:
import { module, test } from 'qunit';
import { visit, settled } from '#ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import type Store from '#ember-data/store';
module('Acceptance | index', function (hooks) {
setupApplicationTest(hooks);
test('visiting /', async function (assert) {
const store = this.owner.lookup('service:store') as Store;
await visit('/');
assert.equal(this.element.textContent, '0:0');
store.createRecord('item', {id: '10', name: 'A'});
store.createRecord('item', {id: '2', name: 'B'});
await settled();
assert.equal(this.element.textContent, '2:2');
});
});
Passes the first assertion but the second assertion fails with:
actual: > 2:0
expected: > 2:2
I have tried changing the line #sort("model", sortNumeric) to use different combinations of model.[], model.#each, model.#each.id but none of them seem to have any effect.
How do get the template to auto-update with custom sorting? (and is this a reoccurrence of https://github.com/emberjs/ember.js/issues/19101)?

Change the controller to use a native getter rather than the sort computed property:
import Controller from '#ember/controller';
import Item from 'sorting-test/models/item';
const sortNumeric = (a: Item, b: Item) => parseInt(a.id, 10) - parseInt(b.id, 10);
export default class Index extends Controller {
// Declare the model otherwise typescript raises errors.
declare model: Item[];
get sortedModels(){
// Copy the array otherwise sort will try to re-order it in place and fail.
return this.model.slice().sort(sortNumeric);
}
}
// DO NOT DELETE: this is how TypeScript knows how to look up your controllers.
declare module '#ember/controller' {
interface Registry {
'index': Index;
}
}

Related

TypeGraphQL createUnionFunction with parameter

I'm trying to implement an extension of typegraphql's createUnionType() function to where I can pass in a class/ObjectType instead of hardcoding, and it will return a union type of both.
What I have so far doesn't work but I feel like it's possible. Could anyone provide any insight? Maybe it's not possible?
typedefs
import { ObjectType, Field, createUnionType, ClassType } from "type-graphql";
#ObjectType()
export default class MutationSuccess {
#Field()
success: boolean = true;
}
// Doesn't work
export const MutationResponse = (objectType: ClassType) => createUnionType({
name: 'MutationResponseType',
types: () => [MutationSuccess, objectType],
})
How I'm trying to use it in my resolver
#Resolver()
export default class RelationResolver {
#Mutation(() => MutationResponse(Relation), { description: 'follow user' })
async follow(
#Arg('relationInput') relationInput: RelationInput
): Promise<Relation | MutationSuccess> {
// do some stuff
}
}
error
UnhandledPromiseRejectionWarning: Error: Cannot determine GraphQL output type for follow
The Relation class need to be decorated with #ObjectType and the union type name has to be unique.

How to retrieve dropdown items from an observable?

How would you retrieve dropdown items from an observable in #rxweb/reactive-dynamic-forms ?
how do you inject service in FormControlConfig subclass? shared code here https://stackblitz.com/edit/angular-bs5yqt-nmzcwj
For injecting custom services into the dynamic form control source model you have to pass the respective service into the argument of model configuration.
Step 1
Create a Service according to your need, I am creating fake ConfigService.
import { HttpClient} from "#angular/common/http"
import { Injectable } from "#angular/core"
#Injectable()
export class ConfigService {
constructor(private http: HttpClient) { }
configUrl = 'assets/config.json';
getConfig() {
return this.http.get(this.configUrl);
}
}
Step 2
Let's create a Model, which is extended with FormControlConfig for async source binding.
import { FormControlConfig } from "#rxweb/reactive-dynamic-forms"
import { ConfigService } from "./config.service"
export class SourceAsyncConditionalModel extends FormControlConfig{
constructor(fieldConfig: { [key: string]: any }, public controlsConfig: { [key: string]: FormControlConfig },notificationId:number,private configService:ConfigService){
super(fieldConfig,controlsConfig,notificationId);
}
filter() {
let promise = new Promise<any>((resolve, reject) => {
/// call the service
if(this.configService)
this.configService.getConfig();
});
}
}
If you see the above the code where I have defined four parameters. The first three parameters are used in FormControlControl and the fourth parameter we can use in the model instance.
Step 3
Now, we have to pass the parameter with respective model. See the below code :
this.dynamicFormConfiguration = {
controlConfigModels: [{ modelName: 'sourceAsync', model: SourceAsyncConditionalModel,arguments:[this.configService] }],
}
this.dynamicFormBuildConfig = this.formBuilder.formGroup(this.serverData, this.dynamicFormConfiguration);
Here is the working Example

Define an arg type using just the typename in typegraphql

We have a factory which creates types using generic types:
export function GenericResolverFactory(props: FactoryProps) {
#GqlType(`${props.model.name}Response`)
class ModelPaginatedResponse extends ResponseFactory(props.modelClass) {
// you can add more fields here if you need
}
#GqlInputType(`${props.model.name}CreateInput`)
class CreateInput extends CreateInputFactory(props.createInput) { }
#Resolver(_of => props.modelClass, { isAbstract: true })
abstract class GenericResolver {
#Mutation(_returns => props.modelClass, { name: `create${startCaseName}` })
create(#Arg('data') data: CreateInput, #Ctx() _context: UserContext) {
return this.__getService().create(data);
}
}
return ModelResolverClass;
}
For now we have to declare the input args like: Arg('input') input: UpdateInput
As my types are generated using a factory the classes won't be available at (not runtime?). Is there a way to refer to those dynamically generated inputs? Is there a way to do something like: Arg('input') input: resolveType('ProductCreateInput')
Assuming that resolveType returns a class decorated with #InputType and #Fields:
#Arg('input', type => resolveType('ProductCreateInput')) input: ICreateInput`
You just need to provide a runtime value as the second parameter of #Arg decorator

load data to grid in angular 6 using http get

Im trying to load the following sample data to jqgrid(free) using http.get in angular 6.
[{"maker":"Toyota", "model":"Celica"},{ "maker": "Chrysler", "model":"Mondeo"}]
Model Class
export class Model{
maker : string
model : string
}
Component:
...
#Component({...})
export class SampleComponent implements OnInit {
private _sampleService;
columnModel : any[];
models : Model[];
constructor(_sampleService : SampleService) {
this._sampleService = _sampleService;
}
ngOnInit() {
this.columnModel = [{ name: "maker" },{ name: "model" }]
this.models = this._sampleService.getModelList().subscribe(models => this.models = models);
}
ngAfterViewInit() {
(<any>jQuery("#grid")).jqGrid({
colModel: this.columnModel,
data: this.models
});
}
}
Service:
....
#Injectable()
export class SampleService{
constructor(private http : HttpClient){}
getModelList():Observable<Model[]>{
return this.http.get<Model[]>
("http://localhost:8090/myapp/getModel");
}
}
If I do the following, I can see the data in the console.
this.http.get("http://localhost:8090/ducksoup/getModel")
.subscribe(data => {console.log(data)})
But,it is not rendering in the grid. Any help?
You can write your jqxGrid binding code inside of OnInit method, right after you fetch your records and it will show your data on datagrid

Cannot initialize the state of ngrx (v. 4.x) store

I am currently looking into using the ngrx store (v. 4.0.3) for state management. It seems like a great project.
I have hit a bit of a bump in the road while trying to initialize the state of my store. The documentation makes it look rather simple, but yet I am not able to see where I am going wrong.
Here's the relevant code snippets:
in app.state.ts
export interface AppState {
searchText: string;
}
In search-text.reducer.ts
export const UPDATE = 'UPDATE';
export class UpdateSearchTextAction implements Action {
readonly type: string = UPDATE;
constructor(public readonly text: string) {}
}
export function searchTextReducer(state: string, action: UpdateSearchTextAction) {
switch(action.type) {
case UPDATE:
return action.text;
}
};
In app.module.ts
export const reducers: ActionReducerMap<AppState, UpdateSearchTextAction> = {
searchText: searchTextReducer
};
export const initialState: InitialState<AppState> = {
searchText: 'sds'
};
....
imports: [
....
StoreModule.forRoot(reducers, initialState)
]
in some Component
constructor(private store: Store<AppState>) {
this.searchBoxText = store.select('searchText');
this.searchBoxText.subscribe(text => console.log('value = [' + text + "]"));
}
So, when the application loads, I would expect to see the following logged to the console:
value = [sds]
yet I see
value = [undefined]
Later, once I start typing in an input that triggers an UpdateSearchTextAction the console does indeed log the correct value. So it seems like I've setup the store correctly.
There is probably something real simple I'm missing. Can anyone provide some advice?
Since you are having it as readonly you are not allowed to assign the value,
export class UpdateSearchTextAction implements Action {
readonly type: string = UPDATE;
constructor(public text: string) {}
}
And you need to dispatch the value using a dispatch statement
this.store.dispatch(new UpdateSearchTextAction.UPDATE(<<string>>));
You must specify default value for state argument and return the same state if no action matches. Try to change your reducer to the following:
export function searchTextReducer(state: string = '', action: UpdateSearchTextAction) {
switch(action.type) {
case UPDATE:
return action.text;
default:
return state;
}
};

Resources