I have 3 users Admin, Supervisor and Student. What I want to do is, Admin adn supervisor can edit and delete students data while student can only delete and edit his own data. He can only view other's data.
I get roles for user in json like below:
Admin: ["Administrator"]
Supervisor: ["Supervisor", "Guest"]
Student: ["Student", "Guest"]
Below is what I am trying to do:
Exhibits.component.ts
getCurrentUser() {
this.userService.getCurrent()
.then(
(response) => {
this.currentUserId = response.id;
for (let role of response.roles) {
if (role === 'Administrator') {
this.canEdit = true;
} else if (role === 'Supervisor') {
this.canEdit = true;
} else if (role === 'Student') {
this.canEdit = false;
}
}
}
).catch(
(error) => console.log(error)
);
}
Exhibits.component.html
<div *ngIf="canEdit && this.currentUserId === exhibit.userId">
<button md-icon-button click-stop-propagation color="primary" [routerLink]="['/mobile-content/exhibits/edit', exhibit.id]"
title="{{ 'edit' | translate }}">
<md-icon>{{ !inDeletedPage ? 'edit' : 'remove_red_eye'}}</md-icon>
</button>
<button md-icon-button click-stop-propagation color="warn" (click)="deleteExhibit(exhibit)" *ngIf="!exhibit.used && !inDeletedPage"
title="{{ 'delete' | translate }}">
<md-icon>delete_forever</md-icon>
</button>
</div>
I am trying to show Exhibits which i got in array according to userId. It means, in exhibits json response, I am getting "userId" which i am trying to match with current user's userId. Oly thing is student can only see delete and edit option for his created exhibit but admin and supervisor can see edit and delete option for all users created exhibits.
Can anyone help me to figure this out?
First, I would suggest to converting this to an enum on both your front and back end, as opposed to relying on string matching.
But judging from your code, if I'm reading correctly, no student would ever be able to have an edit and delete button because you're always setting to false on that user type.
Your second problem is going to be in your *ngIf that states the following:
*ngIf="canEdit && this.currentUserId === exhibit.userId"
This is going to result in these buttons always being hidden at unwanted times because even on administrators and other users you need the conditional of the user ids matching to evaluate to true. You also should not need to specify this in a template.
Personally, I would do something more like this.
getCurrentUser() {
this.userService.getCurrent()
.then(
(response) => {
this.currentUserId = response.id;
for (let role of response.roles) {
if (role === 'Administrator') {
this.canEdit = true;
} else if (role === 'Supervisor') {
this.canEdit = true;
} else if (role === 'Student') {
if (this.currentUserId === this.exhibit.userId) {
this.canEdit = true;
} else {
this.canEdit = false;
}
}
}
}
).catch(
(error) => console.log(error)
);
}
You would then be able to just change your template *ngIf to:
*ngIf="canEdit"
As an aside, you may also want to change your checking of the role to a switch statement, it is more performant and will make your code cleaner.
OR you could do this, which would accomplish the same thing.
getCurrentUser() {
this.userService.getCurrent()
.then(
(response) => {
this.currentUserId = response.id;
for (let role of response.roles) {
if (role === 'Administrator') {
this.canEdit = true;
} else if (role === 'Supervisor') {
this.canEdit = true;
}
}
}
).catch(
(error) => console.log(error)
);
}
Template code would be:
*ngIf="canEdit || this.currentUserId === exhibit.userId"
Related
I just start working on existing code and my task is to place a spinner in auto completion but not sure where exactly to put isLoading = true and isLoading = false in my Typescript. I tried to put all over the place but some reason the spinner icon is still not showing when I try to search some data that store in the backend.
It kinda look like this project https://stackblitz.com/edit/angular-material-autocomplete-async2 and I tried to copy but the spinner icon is still not showing in my project when I start typing. any suggestion or help? thanks
isLoading = false;
#Input() set workspace(ws: Workspace) {
this._workspace = ws;
if (ws && ws.tags) {
this.superTags = ws.tags.filter(tag => {
return tag.type == 1;
});
}
}
constructor(private tagService: TagService) {
this.mapper();
}
ngOnInit(): void {
this.tagService.getAllTagsByType('super').subscribe((superTags) => {
if (superTags)
this.allsuperTags = superTags;
this.allsuperTags.forEach(superTag => {
this.allSuperTagNames.push(superTag.tag);
});
})
}
private _filter(value: string): String[] {
if (value.length > 0) {
const filterValue = value.toLowerCase();
return this.allSuperTagNames.filter(tag => tag.toLowerCase().indexOf(filterValue) === 0);
}
}
add(event: MatChipInputEvent, event1: MatAutocompleteSelectedEvent): void {
const input = event.input;
const value = event.value;
if (event1 === null) {
input == event.input;
value == event.value;
}
else {
input == event1.option.value;
value == event1.option.value;
}
if ((value || '').trim()) {
if (this.allSuperTagNames.find((f) => f.toLowerCase() === value.toLowerCase()) && !this.superTags.find((f) => f.tag.toLowerCase() === value.toLowerCase()))
{
this.superTags.push({ tag: value.trim().toLowerCase(), type: TagType.super });
this.tagService.addTag(this._workspace.guid, 'workspace', value).subscribe((tag) => console.log("added", tag));
this.snackbar.open(input.value + " has been added as super tag.", " ", { duration: 2500 });
}
}
// Reset the input value
if (input) {
input.value = '';
}
this.tagCtrl.setValue(null);
}
mapper() {
this.filteredSuperTags = this.tagCtrl.valueChanges.pipe(
startWith(null),
map((tag: string | null) => tag ? this._filter(tag) : this.allSuperTagNames.slice()));
}
<mat-autocomplete #auto="matAutocomplete" (optionSelected)="selected($event)">
<mat-option *ngIf="isLoading" class="is-Loading">
<mat-spinner diameter="20"></mat-spinner>
</mat-option>
<ng-container *ngIf="!isLoading">
<mat-option *ngFor="let tag of filteredSuperTags | async" [value]="tag">
{{tag}}
</mat-option>
</ng-container>
</mat-autocomplete>
It seems like the code which you added only doing the synchronous operation. Even though you subscribed to the form-control, the tags are being filtered locally from pre-loaded data, and the time taken will be very little. To really show the spinner, you may either need to call an API or add some delay to mock the filter method as observable as shown in this example
How can I create an observable with a delay
This way you can show the spinner during that delay.
I am getting a warning on the following function
function currencySubmenuTitle(ctx) {
let id = Object.keys(currencies).find(element => {
if (currencies[element].id === ctx.match[1]) {
return element
}
})
if (typeof id === 'undefined' || id === null) {
return "No match found"
} else {
return `💰 ${toTitleCase(id)} : ${currencies[id].current}`
}
}
Note: My id and element are different, so I can't just take the element and use that as the string return.
The warning is:
2:51 warning Expected to return a value at the end of arrow function array-callback-return
2:51 warning Expected to return a value at the end of arrow function consistent-return
How do I return my value in this function in a compliant way (aka not how I am doing it)
Can I thenify this? Run the if statement based on the return of the array-evaluation?
The evaluation of the statement can happen in the return line, so no specific if-statement is needed here. Simply do:
function currencySubmenuTitle(ctx) {
let id = Object.keys(currencies).find(element => {
return currencies[element].id === ctx.match[1]
})
if (typeof id === 'undefined' || id === null) {
return "No match found"
} else {
return `💰 ${toTitleCase(id)} : ${currencies[id].current}`
}
}
I have a requirement that after typing certain content in an tag, pressing enter will do search function.
It running well normally like:
<input
onChange={this.onInputChange}
onKeyPress={this.onSearch}
/>
onInputChange = (e) => {
console.log(2);
this.setState({
searchText: e.target.value
})
}
onSearch = (e) => {
console.log(1);
if (e.which === 13) {
search(this.state.searchText); // some search api ...
}
}
But if user Enter really quickly, like 0.1s, the this.state.searchText is not get updated properly.
This is not just caused by setState is async method, but the onKeyPress is trigger before onChange.
is there any idea to deal with this issue?
So I can't really understand why you use two separate functions.
First of all, if you only use searchText for the two functions you could just do:
HTML
<input
onKeyPress={this.onKeyPress} />
JS
onKeyPress = e => {
if(e.which === 13) {
// Send Query
search(e.target.value);
}
}
And even if you needed searchText somewhere else you could just do:
onKeyPress = e => {
let value = e.target.value;
if(e.which === 13) {
// Send Query
search(value);
} else this.setState({searchText: value});
}
If I missed something please tell me ^^
<input
onChange={this.onInputChange}
onKeyDown={this.onSearch}
/>
onInputChange = (e) => {
this.setState({
searchText: e.target.value
})
}
onSearch = (e) => {
if (e.keyCode === 13) {
search(this.state.searchText); // some search api ...
}
}
<input
ref={(input) => this.selectVal = input}
onKeyPress={(e) => e.which === 13 ?this.onSearch():''}
/>
onSearch = () => {
console.log("value",this.selectVal.value);
// search(this.input.current.value); // some search api ...
}
try this way
My server is supposed to send me back some data (stored as json) read when asked. To avoid blocking communications, I set-up 2 promises: one to read a file:
function readingfile(survey) {
return new Promise(
function (data_read, err) {
fs.stat(`./data/${survey}.json`, function (err, stat) {
if (err == null) {
fs.readFile(`./data/${survey}.json`, 'utf8', (err, data) => {
data_read((data))
})
} else
console.error(`./data/${survey}.json doesnt exist`)
})
})
}
and one to read all files from a user:
function readingusersurveys(user) {
let questionnaires = [];
let count = 0;
return new Promise(
function (data_read, err) {
user.surveys.forEach((survey) => {
readingfile(survey).then(function (all_surveys) {
count++;
//console.log((all_surveys)) //ok here
questionnaires.push((all_surveys))
if (count == user.surveys.length) {
console.log((questionnaires)) //not ok here (wtf)
data_read((questionnaires))
}
})
})
})
}
and the code snippet that send the data:
[...]
readingusersurveys(req.user).then(function (all_surveys) {
//console.log(all_surveys)
questionnaires.push((all_surveys))
console.log(questionnaires)
if (questionnaires != null) {
res.status(200).json({
questionnaires
});
} else {
res.status(500).json({});
}
})
but when readingusersurveys() return the data read, it get filled with tons of \r\n making the file unreadable. If I try to place a JSON.parse somewhere, I either: enter a infinite loop or the data become unreadable/undefined (eg: {"asset": ["value"]} become {"asset": [Object]}).
I have tried to place a JSON.parse pretty much everywhere to change comportement but no luck. Any idea how to get rid of \r\n and/or what's missing in my code ? :/
After many tries, I found out that it wasn't the JSON.parse the problem but questionnaire.push. It wasn't doing what I though it was doing(adding 2 json array together).
Added the JSON.parse here
function readingusersurveys(user) {
let questionnaires = [];
let count = 0;
return new Promise(
function (data_read, err) {
user.surveys.forEach((survey) => {
readingfile(survey).then(function (all_surveys) {
count++;
questionnaires.push(JSON.parse(all_surveys)) // <-- HERE
if (count == user.surveys.length) {
data_read((questionnaires)) //<-- array of JSON at this point
}
})
})
})
}
[...]
readingusersurveys(req.user).then(function (all_surveys) {
questionnaires = (all_surveys) //<-- pushing an array of JSON into another array was what created problems
if (questionnaires != null) {
res.status(200).json({
questionnaires
});
} else {
res.status(500).json({});
}
})
If I wanted to do a loop there and add more surveys, I needed to use concat() instead
if (questionnaires[0] == null)
questionnaires = all_surveys
else
questionnaires = questionnaires.concat(all_surveys)
So I have a table of people's information and I used codes that look something like below (people's location in this case):
<!-- ko foreach: filteredApplications -->
<div class="col-md-6">
<span>Location:</span>
<span data-bind="text: application.candidateLocation"></span>
</div>
to display location information for all people.
And when I click the "Preview Application" link, it is supposed to give me a modal like the attached image and show the information of the corresponding person.
I tried to remove the foreach knockout for the modal, but then it gives me an error saying that it cannot find the application.candidateLocation variable.
How do I have to approach this issue?
Please help!
EDIT (viewmodel):
public partial class Application
{
public dynamic JsonForm => new
{
CandidateLocation,
Job = this.Job.JsonForm,
CandidateStatus = this.CurrentStatuse.CandidateStatus,
};
public string CandidateLocation
{
get
{
switch (ApplicationCandidateType)
{
case ApplicationCandidateType.Standard:
return InsideApplication.CandidateApplication.Candidate.Location;
case ApplicationCandidateType.Guest:
return null;
case ApplicationCandidateType.Manual:
return null;
default:
throw new Exception("Unhandled ApplicationCandidateType");
}
}
}
function ViewModel() {
var self = this;
self.invite = ko.observable(false);
self.changeStylesInvite = function () {
self.invite(true);
}
self.notifications = ko.observableArray(#Html.Json(Model.Notifications.Select(o => o.JsonForm)) || []);
self.applications = ko.observableArray(#Html.Json(Model.ApplicationCompatibilities.Select(o => o.JsonForm)) || []);
self.applicationInvitations = ko.observableArray(#Html.Json(Model.ApplicationInvitations.Select(o => o.JsonForm)) || []);
self.applicationsFilter = ko.observable("new");
self.showHiddenApplications = ko.observable(false);
self.newApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return !i.application.isShortlisted && !i.application.isContactInfoSent && (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});
self.shortlistedApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return i.application.isShortlisted && (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});
self.connectedApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return i.application.isContactInfoSent && (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});
self.allApplicationsCount = ko.computed(function() {
return ko.utils.arrayFilter(self.applications(), function(i) {
return (self.showHiddenApplications() || !i.application.isHidden);
}).length;
});