I want to watch the nested property of a json. Whenever this nested property changes call a fn().
export class HeaderComponent {
user: any;
constructor(){
this.user = {
options: [
{ name: 'Jenny Hess', img: 'assets/img/avatar/small/jenny.jpg' },
{ name: 'Elliot Fu', img: 'assets/img/avatar/small/elliot.jpg' },
{ name: 'Stevie Feliciano', img: 'assets/img/avatar/small/stevie.jpg' }
],
selected: { name: 'Jenny Hess', img: 'assets/img/avatar/small/jenny.jpg' }
}
}
Fn changes values
public changeUser(item) {
this.user.selected = item;
/*Some Code here*/
}
public customLogin(user) {
/*Some Code here*/
this.user.selected = user;
/*Some Code here*/
}
Whenever the value of this.user.selected changes call a function.
I'm using rxjx as well.
Any suggestion??
You could do something like this:
export class HeaderComponent implements OnDestroy {
user: any;
userSelectSubject: BehaviorSubject<{name: string, img: string}>;
private userSelectSubscription: Subscription;
constructor(){
this.user = {
options: [
{ name: 'Jenny Hess', img: 'assets/img/avatar/small/jenny.jpg' },
{ name: 'Elliot Fu', img: 'assets/img/avatar/small/elliot.jpg' },
{ name: 'Stevie Feliciano', img: 'assets/img/avatar/small/stevie.jpg' }
]
}
this.userSelectSubject = new BehaviorSubject<{name: string, img: string}>({ name: 'Jenny Hess', img: 'assets/img/avatar/small/jenny.jpg' });
this.userSelectSubscription = this.userSelectSubject.subscribe((newSelectedUser) => {
this.user.selected = newSelectedUser;
});
}
ngOnDestroy() {
this.userSelectSubscription.unsubscribe();
}
}
Then you just need to call this.userSelectSubject.next({...}) passing the new selected user as parameter.
Related
I'm new to learn JS design pattern. I had a basic function before, receiving an object and do some extra actions and ouput an array.
If I use a normal basic function method, it's only few lines. However, if I use a factory method design pattern, I can get the same result, but the code amount is much bigger.
What's the benifit using a factory method design pattern? With my code, did I overuse that factory method design pattern?
Here is the Code
You can ignore the functionality of combineWithSection, the real one is more complex.
export {};
const detailBasicDetail = [
{
basicDetailA1P1: {
name: "basicDetailA1P1",
label: "basicDetailA1P1",
section: "basicDetail"
},
basicDetailA1P2: {
name: "basicDetailA1P2",
label: "basicDetailA1P2",
section: "basicDetail"
}
},
{
basicDetailA2P1: {
name: "basicDetailA2P1",
label: "basicDetailA2P1",
section: "basicDetail"
}
},
{
basicDetailA3P1: {
name: "basicDetailA3P1",
label: "basicDetailA3P1",
section: "basicDetail"
}
},
{
basicDetailA3P2: {
name: "basicDetailA3P2",
label: "basicDetailA3P2",
section: "basicDetail"
}
},
{
basicDetailA3P3: {
name: "basicDetailA3P3",
label: "basicDetailA3P3",
section: "basicDetail"
}
}
];
const detailIncidentDetail = [
{
incidentDetailA1P1: {
name: "incidentDetailA1P1",
label: "incidentDetailA1P1",
section: "incidentDetail"
}
},
{
incidentDetailA2P1: {
name: "incidentDetailA2P1",
label: "incidentDetailA2P1",
section: "incidentDetail"
},
incidentDetailA2P2: {
name: "incidentDetailA2P2",
label: "incidentDetailA2P2",
section: "incidentDetail"
}
},
{
incidentDetailA3P1: {
name: "incidentDetailA3P1",
label: "incidentDetailA3P1",
section: "incidentDetail"
}
},
{
incidentDetailA3P2: {
name: "incidentDetailA3P2",
label: "incidentDetailA3P2",
section: "incidentDetail"
}
},
{
incidentDetailA3P3: {
name: "incidentDetailA3P3",
label: "incidentDetailA3P3",
section: "incidentDetail"
}
}
];
const detailOtherDetail = [
{
otherDetailA1P1: {
name: "otherDetailA1P1",
label: "otherDetailA1P1",
section: "otherDetail"
}
},
{
otherDetailA2P1: {
name: "otherDetailA2P1",
label: "otherDetailA2P1",
section: "otherDetail"
},
otherDetailA2P2: {
name: "otherDetailA2P2",
label: "otherDetailA2P2",
section: "otherDetail"
}
},
{
otherDetailA3P1: {
name: "otherDetailA3P1",
label: "otherDetailA3P1",
section: "otherDetail"
}
},
{
otherDetailA3P2: {
name: "otherDetailA3P2",
label: "otherDetailA3P2",
section: "otherDetail"
}
},
{
otherDetailA3P3: {
name: "otherDetailA3P3",
label: "otherDetailA3P3",
section: "otherDetail"
}
}
];
const combineWithSection = (detailBasicDetail, sectionName) => {
return [...detailBasicDetail, sectionName];
};
// basic function method
const generateDetail = (detail, sectionName) => {
return combineWithSection(detail, sectionName);
};
const detailFinalBasicFromFn = generateDetail(detailBasicDetail, "basicDetail");
const detailFinalIncidentFromFn = generateDetail(
detailIncidentDetail,
"incidentDetail"
);
const detailFinalOtherFromFn = generateDetail(detailOtherDetail, "otherDetail");
console.log("detailFinalBasic_1", detailFinalBasicFromFn);
console.log("detailFinalIncident_1", detailFinalIncidentFromFn);
console.log("detailFinalOther_1", detailFinalOtherFromFn);
// factory method
abstract class Detail {
finalDetail;
constructor(public detail, public sectionName) {
this.finalDetail = combineWithSection(detail, sectionName);
}
}
abstract class Factory {
abstract combineWithSectionArray();
}
class BasicDetail extends Detail {
constructor(public detail, public sectionName) {
super(detail, sectionName);
}
}
class BasicDetailFactory extends Factory {
combineWithSectionArray() {
return new BasicDetail(detailBasicDetail, "basicDetail");
}
}
class IncidentDetail extends Detail {
constructor(public detail, public sectionName) {
super(detail, sectionName);
}
}
class IncidentDetailFactory extends Factory {
combineWithSectionArray() {
return new IncidentDetail(detailIncidentDetail, "incidentDetail");
}
}
class OtherDetail extends Detail {
constructor(public detail, public sectionName) {
super(detail, sectionName);
}
}
class OtherDetailFactory extends Factory {
combineWithSectionArray() {
return new OtherDetail(detailOtherDetail, "otherDetail");
}
}
class FinalDetail {
static generateFinalDetail(name: string) {
switch (name) {
case "basicDetail":
return new BasicDetailFactory().combineWithSectionArray();
case "incidentDetail":
return new IncidentDetailFactory().combineWithSectionArray();
case "otherDetail":
return new OtherDetailFactory().combineWithSectionArray();
default:
return null;
}
}
}
const detailFinalBasic = FinalDetail.generateFinalDetail("basicDetail");
const detailFinalIncident = FinalDetail.generateFinalDetail("incidentDetail");
const detailFinalOther = FinalDetail.generateFinalDetail("otherDetail");
console.log("detailFinalBasic_2", detailFinalBasic!.finalDetail);
console.log("detailFinalIncident_2", detailFinalIncident!.finalDetail);
console.log("detailFinalOther_2", detailFinalOther!.finalDetail);
I have two interface, one is cropFilter which is for checkbox filter and second interface is holding my data called Crop.
let me share my code for better understanding.
1. crop.model.ts
export class Crop { // Interface 1
name: string;
district: string
subCategory: Subcategory[];
}
export class Subcategory {
id: number;
name: string;
}
export class CropFilter { // Interface 2
name: string
checked: boolean
}
2. cropFilter.ts
import { CropFilter } from "./crop.model";
export const CROPSFILTER: CropFilter[] = [
{
name: "Rice",
checked: false
}, {
name: "Wheat",
checked: false
}, {
name: "Barley",
checked: false
}
]
The above interface is for checkbox filtration.
3. crop.data.ts
import { Crop } from "./crop.model";
export const CROPS: Crop[] = [
{
name: "Rice",
district: "Thane",
subCategory: [
{
id: 1,
name: "Basmati",
},
{
id: 2,
name: "Ammamore",
}
]
},
{
name: "Rice",
district: "Nashik",
subCategory: [
{
id: 1,
name: "Basmati",
},
{
id: 2,
name: "Ammamore",
}
]
},
{
name: "Wheat",
district: "Nashik",
subCategory: [
{
id: 1,
name: "Durum",
},
{
id: 2,
name: "Emmer",
}
]
},
{
name: "Barley",
district: "Ratnagiri",
subCategory: [
{
id: 1,
name: "Hulless Barley",
},
{
id: 2,
name: "Barley Flakes",
}
]
},
{
name: "Barley",
district: "Thane",
subCategory: [
{
id: 1,
name: "Hulless Barley",
},
{
id: 2,
name: "Barley Flakes",
}
]
}
];
This is the actual data. All I want to fetch data from crop.data.ts based on crop.filter.ts
for better clearance let me show you the html part as well :
1. all-trade.html
<div class="container" *ngIf="crops$ | async">
<div *ngFor="let item of cropFilterCheckbox$ | async; let i = index">
<mat-checkbox [checked]="item.checked" (change)="onChange($event, i, item)">
{{ item.name }}
</mat-checkbox>
</div>
<br />
<h4>JSON data:</h4>
<pre>
{{ cropFilterCheckbox$ | async | json }}
<div *ngFor="let crop of cropFilterCheckbox$ | async"
[hidden]="!crop.checked"
>{{ crop.name }}
</div>
<button type="button" class="btn">Basic</button>
</pre>
</div>
2. crop.service.ts
import { Injectable } from "#angular/core";
import { Observable, of } from "rxjs";
import { Crop, CropFilter, DistrictFilter } from "../shared/crop.model";
import { CROPS } from "../shared/crop.data";
import { CROPSFILTER } from '../shared/cropFilter';
#Injectable({
providedIn: "root"
})
export class CropService {
constructor() { }
crops: Crop[] = CROPS;
cropFilterCheckbox: CropFilter[] = CROPSFILTER;
getAllCrops(): Observable<Crop[]> {
return of(this.crops);
}
getCropFilter(): Observable<CropFilter[]> {
return of(this.cropFilterCheckbox)
}
getCrop(name: string): Observable<any> {
const crop = this.crops.filter(crop => crop.name === name)[0];
return of(crop);
}
}
The final output looks like this :
Now please guide me how to fetch data from crop.data.ts based on crop.filter.ts
Like when user check Rice checkbox, its should fetch all the details of Rice present in crop.data.ts file and display on the screen.
On checkbox change write an event handle like below. Maintain which are the checkbox user has checked in a variable "AppliedFilter" and then pass that array list to your service method.
onChange(status, name) {
if (status && this.appliedFilter.indexOf(name) === -1) {
this.appliedFilter.push(name);
} else {
this.appliedFilter = this.appliedFilter.filter((x) => x !== name);
}
this.crops$ = this.cropService.getCrop(this.appliedFilter);
}
In your service method based on that array filter your records like below.
getCrop(names: string[]): Observable<any> {
const crop = this.crops.filter((crop) => names.includes(crop.name));
return of(crop);
}
Here is the working sandbox.
https://codesandbox.io/s/filter-data-x2p0w?file=/src/app/app.component.ts:289-294
I am trying to display a selected index value in ionic 2 alert box. But I am not getting proper way how to display in ionic prompt.
This is home.ts
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import {AlertController} from 'ionic-angular';
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
companies: Array< {name: string, code: number}>
constructor(public navCtrl: NavController, public alertController:
AlertController) {
this.companies = [
{name: 'Microsoft', code: 1},
{name: 'Apple', code: 2},
{name: 'Google', code: 3},
{name: 'Oracle', code: 4},
{name: 'IBM', code: 5},
];
}
delete(no) {
let alert = this.alertController.create({
title: "Example",
subTitle: "Example SubTitle" + {{no}};
buttons: ["OK"]
});
alert.present();
(this.companies).splice(no, 1);
}
}
In the above delete function delete(no) I am passing no as parameter for delete function the same value I need to show in the alert box.
Good to create shared provider for this.
shared.provider.ts:
public Alert = {
confirm: (msg?, title?, no?) => {
return new Promise((resolve, reject) => {
let alert = this._alert.create({
title: title || 'Confirm', // `Example SubTitle ${no}`
message: msg || 'Do you want continue?',
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: () => {
reject(false);
}
},
{
text: 'Ok',
handler: () => {
resolve(true);
}
}
]
});
alert.present();
});
},
alert: (msg, title?) => {
let alert = this._alert.create({
title: title || 'Alert',
subTitle: msg,
buttons: ['Dismiss']
});
alert.present();
}
}
Invoke alert for confirmation:
import { SharedProvider } from '../../providers/shared.provider';
this.shared.Alert.confirm('Would you like to delete?', 'Confirm', 2).then((response) => {
console.log('confirmed');
}, err => {
console.error('rejected');
});
I want to use mutation in Relay to change an array (not connection). The array is typed GraphQLList in the GraphQL side. The graphql side worked perfectly, but relay side needs dataID for each item in an array. And when I am inserting new item or modifying existing item in the array, there are no dataID provided? What is the right way to do this? By the way, I am using redux to maintain the list, and submit changes via relay at the end.
The schema:
let widgetType = new GraphQLInputObjectType({
name: 'Widget',
fields: () => ({
label: {
type: GraphQLString
},
type: {
type: GraphQLString
},
list: {
type: new GraphQLList(GraphQLString)
},
description: {
type: GraphQLString
},
required: {
type: GraphQLBoolean
}
})
});
let modifyFormMutation = mutationWithClientMutationId({
name: 'ModifyForm',
inputFields: {
id: {
type: new GraphQLNonNull(GraphQLString)
},
name: {
type: new GraphQLNonNull(GraphQLString)
},
userId: {
type: new GraphQLNonNull(GraphQLString)
},
widgets: {
type: new GraphQLList(widgetType)
}
},
outputFields: {
formEdge: {
type: formConnection.edgeType,
resolve: (obj) => {
return {
node: {
id: obj.id,
name: obj.name,
userId: obj.userId,
widgets: obj.widgets
},
cursor: obj.id
};
}
},
app: {
type: appType,
resolve: () => app
}
},
mutateAndGetPayload: ({
id, name, userId, widgets
}) => {
db.collection('forms').findOneAndUpdate({
_id: new ObjectID(id)
}, {
name, userId, widgets, createAt: Date.now()
});
return {
id, name, userId, widgets
};
}
})
Relay mutation:
export default class ModifyFormMutation extends Mutation {
getMutation () {
return Relay.QL`mutation{modifyForm}`;
}
getFatQuery() {
return Relay.QL`
fragment on ModifyFormPayload {
formEdge
app { forms }
}
`;
}
getCollisionKey() {
return `check_${this.props.app.id}`;
}
getConfigs() {
return [{
type: 'FIELDS_CHANGE',
fieldIDs: {
formEdge: {node: this.props.node},
app: this.props.app.id
}
}];
}
getVariables() {
return {
name: this.props.node.name,
id: this.props.node.id,
userId: this.props.node.userId,
widgets: this.props.node.widgets
};
}
getOptimisticResponse() {
return {
formEdge: {
name: this.props.node.name,
id: this.props.node.id,
userId: this.props.node.userId,
widgets: this.props.node.widgets
}
};
}
}
And error message from browser:
"Variable "$input_0" got invalid value
{"name":"asdfasdfsa","id":"57e790cec252f32aa805e38d","userId":"57e10a02da7e1116c0906e40","widgets":[{"dataID":"client:618507132","label":"sdfas","type":"text","list":[],"description":"","required":true},{"label":"sfasdfasaaa","list":[],"type":"number","description":"","required":"false"}],"clientMutationId":"0"}.↵In
field "widgets": In element #0: In field "dataID": Unknown field."
I'd like to import a helper class rather than inlining the logic inside my component. I get the following error:
http://eslint.org/docs/rules/no-unused-vars 'NavbarService' is defined but never used
/services/NavbarService.js
class NavbarService {
constructor (init) {
this.init = init;
}
static applications () {
return [
{ name: 'Administration' },
{ name: 'Standard' }
];
}
static views () {
return [
{ name: 'Providers', path: '/providers' },
{ name: 'Authorities', path: '/authorities' },
{ name: 'Services', path: '/services' },
{ name: 'Codes', path: '/codes' }
];
}
}
/components/Navbar.vue
import NavbarService from '../services/NavbarService.js';
export default {
data () {
return {
versionIsVisible: false,
version: '2.0.0',
applications: NavbarService.applications(),
views: NavbarService.views()
};
},
methods: {
showApplications: function () {
this.applications = NavbarService.applications();
this.views = [];
return;
}
}
};
Following Roy J's suggestion, I changed /services/NavbarService.js to:
export default {
applications: function () {
return [
{ name: 'Administration' },
{ name: 'Standard' }
];
},
views: function () {
return [
{ name: 'Providers', path: '/providers' },
{ name: 'Authorities', path: '/authorities' },
{ name: 'Services', path: '/services' },
{ name: 'Codes', path: '/codes' }
];
}
};