I am trying to build a student-teacher result view portal in which teachers can login and see all of the students result and add to that list or edit that list. Students can login to search using their credentials and see their own result.
I have a list of students (Array). I want to use that list in my student-view to search the list and return the student with the matching credentials. I want to use that list to provide the teachers with the CRUD functionality for complete list.
I could not figure out the correct way to get access to the list of students in different parts of my project.
I tried using #Input and Service but I couldnt do it in the correct way and I am getting empty array in both of the methods.
What is the correct way to acheive this? I have data in sibling component. Should I store data in parent component? Please help me find the correct way to do this.
This is the component where I have the data, students component. You can also see the component and project structure in this photo. Currently I am trying to transfer data between siblings using service and failed. I am setting the data in constructor using :
this.studentTransferData.setData(this.students)
I am trying to get data in my StudentView Component using :
export class StudentViewComponent implements OnInit {
name: string = ""
rollno: number = 0
studentList = this.studentDataTransferService.getData();
#Input() students: Student[] = []
#Output() studentSearch: EventEmitter<Student> = new EventEmitter();
constructor(private studentDataTransferService: StudentDataTransferService) {
console.log(this.students)
console.log(this.studentList)
}
It consoles empty array.
How to get the data in my different components. I have it in students component.
Thanks.
Edit: I tried doing inside on ngInit as suggested in different post and it does not work as expected and I want it in complete project (siblings and parent to child) That is not what I want at the moment.
When you need to share data between several components, you usually use a service.
To be truly reactive, you should also use what are called proxies : those are RxJS elements that can act both as observables and observers.
private _students = new BehaviorSubject<Student[]>([]);
public students$ = this._students.asObservable();
get students() { return this._students.value; }
loadStudents() {
this.http.get<Student[]>(...).subscribe(students => this._students.next(students));
}
Then in other components
students$ = this.service.students$;
constructor(private service: StudentsService) {}
<ng-container *ngFor="let student of students$ | async">
...
</ng-container>
EDIT : using it in services
Yes, you can use it in services. If you need to make an HTTP call at some point, this is the best solution :
constructor(private service: StudentsService) {}
rewardStudent() {
const bestStudent = this.service.students[0];
this.http.post(...).subscribe();
}
Otherwise, you have access to students$ the same way you do with components.
You can create and store the student list inside a student service class and also create a public function: getStudentsList inside your service and then call it from your different views.
Related
I'm trying to implement multiple checkbox filtering using Angular but can't seem to be able to figure out how to proceed exactly. I've looked over multiple similar questions here, but failed to figure out how to use any of the answers.
Ideally, I'd like to filter my data using event listeners.
My main two issues are:
Figuring out how to actually filter things, I can't figure out what the right approach would be for my goal
Actually displaying updated data
Stackblitz
Any push in the right direction would be greatly appreciated!
Basically, you need to share the data(filters) between two components because filtered-users and filters are two components in your project.
so, to share data between two components (which are not having parent-child relationship) we can use observables.
You can create a service called FilterService and in that, you can have an observable (filters)
#Injectable()
export class FilterService {
private filters = new Subject<{}>(); // creating a subject
filters$ = this.filters.asObservable(); // creating an observable
alertFilter(key: string, value: string) {
this.filters.next({ key, value }); // publishing the new fliter to the subscribers
}
}
and add this service to filtered-users, filters components through dependency-injection. and call this alertFilter() method of FilterService from filters component whenever the user checks the filter checkbox.
in filter.component.html
<input (change)="onCheck('gender', opt)"
in filter.component.ts
onCheck(key: string, value: string) {
this.filterService.alertFilter(key, value);
}
after this, subscribe to the observable(filters) of FilterService in filtered-users-component.
in filtered-users.component.ts
constructor(
private sortingService: SortingService,
private userService: UserService,
private filterService: FilterService
) {
this.filterService.filters$.subscribe({
next: filter => {
this.filteredUsers = this.filteredUsers.filter(user => {
return user[filter['key']] === filter['value'];
});
}
});
}
this.filterService.filters$.subscribe() will execute whenever a new filter has been added so, using filter variable you can filter the users accordingly.
I have this situation:
<app-component-test [dataSource]="dataSource"></app-component-test>
dataSource is a service inited in component's constructor. Now I change this service property by:
this.dataSource.list = [1, 2, 3];
but app-component-test doesn't have any changes. I need to have in component this service with the updated list.
Rather than passing through the service, instead try passing through the list on it's own.
#Input() list: number[];
<app-component-test [list]="dataSource.list"></app-component-test>
Even better, if your list comes from an Observable, you can just pass the observable through with the async pipe:
#Input() list: number[];
<app-component-test [list]="list$ | async"></app-component-test>
You can choose between
Send in the list rather than the service.
Inject the service in the app-component-test constructor and get the list inside the app-component-test component instead.
There is already some similar questions on this.
You can check this answer it is about variables not service, but it is in the same principle.
Click
Bascaly you make a local equivalent of your input object and modify it as you wish.
The objective behind using BehaviouSubject was to use a single API call and pass the same data to multiple components in the same route.
I am able to do that. I am not able to filter the received data
Heres a stackblitz fiddle that i have created
https://stackblitz.com/edit/angular-xtne5y
In one component, i am displaying the table, in the other, i need to extract some info out of it based on the individual object key values. Like how many todos are complete / incomplete.
Since I am required to use the async pipe everywhere in the template, performing operations like filter are not possible.
Is there a better way to implement this?
I need to keep the data extracted as reusable
You're currently using the async pipe. To get the desired result, you can use (or chain) another custom pipe with your data to fetch specific properties.
I've forked your stackblitz example and modified the code with the solution.
Here's my working solution.
Essentially, all you need to do is use a custom pipe.
{{ todos$ | async | myCustomFilter }}
In my example (stackblitz), I'm doing:
<p>
No. of Completed Todos: {{ (todos$ | async | filterByCondition: {property: 'completed', value: true}).length }}
</p>
<p>
No. of Incomplete Todos: {{ (todos$ | async | filterByCondition: {property: 'completed', value: false}).length }}
</p>
Edit after your comments
There are two approaches to your desired result:
1) Use a custom pipe. You can parameterize the pipe the same as I've done or even create your own conditions and evaluate by passing a parameter to your pipe (as I've done in the example for the args property).
I.e
<div>{{$todos | async | filterTodo: 'byDate'}}</div>
Now, you can put the handler in your pipe filterTodo for this byDate value.
2) Use different observables for different data.
class MyComponent {
todos$;
completedTodos$;
constructor() {
this.todos$ = this.someService.getTodos(); // from behavior subject
this.completedTodos$ = this.todos$.pipe(
filter((item) => {
// your filter code here
})
)
}
}
So I've have worked on projects where when we create TODO's section, My approach would be when you subscribe to the URL to fetch the data, create a Redux Store
(Here's [a link] https://medium.com/supercharges-mobile-product-guide/angular-redux-the-lesson-weve-learned-for-you-93bc94391958), which has an interface like
interface ProductTodos {
product: {title:string,iscompleted:boolean,id:string/number},
todos?: any[]
}
When you fetch your Todos, you will dispatch an action like "PUSH" and every TODO will be an object of type product and will appended on to the todos array in the interface.
when you create an instance of the redux Store in the Component Class you will loop through the "todos" array and check for the id and isCompleted flag.
Based on this you can loop through each object and fill your tables based on their completed status.
Here is a link to my ReduxDemo in Angular 4, check it out https://github.com/nerdySingh/ReduxDemoAngular
Overview:
I have a UI that allows a user to select one or more employees based on various search criteria. When they select them, I need to store the selected employees in an array, within my shared service.
Before any of this data is sent to the server, the array could be modified by adding more employees or removing some that exist in the array.
I need to be able to create and subscribe to an array of data in this shared service.
My Approach:
My initial approach was to use a BehaviorSubject so that I could call next and pass the data along when needed. This became an issue though because I didn't have a way to see all of the stored/selected users, only the last one that was passed through the BehaviorSubject.
Psuedo Code:
shared.service.ts
public selectedUsers = []; //<- How do I store stuff in here?
private selectedUsersSub = new BehaviorSubject<any>(null);
selectedUsers$ = this.selectedUsersSub.asObservable();
setSelectedUsers(data) {
this.selectedUsersSub.next(data);
}
get selectedUsers(){
return this.selectedUsers;
}
component.ts:
this._reqService.selectedUsers$.subscribe(
data => {
if (data) {
console.log('Observable Stream', data)
}
}
)
My goal here is to be able to store my selected employees in this selectedUsers array. My other components need to be able to subscribe so that they are always up-to-date with the current value of selectedUsers.
I also need to be able to access the current array of selected users at any time, not just the last value.
Delete public selectedUsers = [];
delete get selectedUsers(){
return this.selectedUsers;
}
And in any component you want to fetch the selectedUsers just subscribe to the public observable selectedUsers$
in a component
this.subscription = this.yourService.selectedUser$.subscribe((users)=>//do stuff here like push theusersto the users array of the component)
The service needs to be inject to a shared module in order all the components to get the same state (data).
More details: https://angular.io/guide/component-interaction#parent-and-children-communicate-via-a-service
Your approach is wrong here. You have 2 basic options in a shared service pattern. 1 is to use a store pattern where you have a predefined set of data manipulations and use the scan operator, this is more complex, the simpler is to pass the entire list every time you want to update the list.
So your components will not only send the update, they'll first get the entire list and then manipulate and then send it.
Currently, I am using angular 4 for my school project. I have an array, each item is a child component which can be updated and deleted, which means I should know the index and the data.
parent.ts:
updOne(i:number,stc:string):void{
this.myarray[i]=stc
}
delete(edu:string):void{
this.myarray=this.myarray.filter(x=>x!==edu)
}
parent.html:
<child-com [edu]=x [num]=i (updstr)="updOne($event)" (delstr)="delete($event)"></child-com>
child-com.ts:
#Input() edu:string
#Input() num:number
#Output() updstr: EventEmitter<string> = new EventEmitter<string>()
#Output() delstr: EventEmitter<string> = new EventEmitter<string>()
//some other code here
save():void{
this.updstr.emit(this.edu)
this.updating=false
}
del():void{
this.delstr.emit(this.edu)
}
delete works well, without a doubt. The problem is updating. Actually, using *ngFor, trackBy, and printing it all manually, this problem can be solved. But I wanna try using child component, as in React. When I play around with react, I can just use javascript closure, i.e. myfunc.bind(this,i,stc).
I've tried using bind here, no results
code when using bind:
parent.ts:
#Output() updstr: EventEmitter<number,string> = new EventEmitter<number,string>()
parent.html:
//I've tried some order
//this,i,$event
//$event,this,i
<child-com [edu]=x (updstr)="updOne.bind(this,$event,i)" (delstr)="delete($event)"></child-com>
And generics in typescript doesn't allow multiple data, so I cant emit more than one data
So my question is, how can I pass some data at once from child to parent, using emit or bind?
Thanks to Alex, using an object can substitute multiple data passing. Just to make sure that the data is correct, an interface is used, kind of like this
export interface Interview{
num:number
payload:{
dt:string
seeker:string
}
}
and used it like
#Output() updstr: EventEmitter<Interview> = new EventEmitter<Interview>()