Angular 7, Asp.net web api, assigning roles to users - html

I have an ASP.NET Web API with Angular 7 material design client side. I want to assign a role to a current user,
I have a user that I'm trying to assign a role to. I've got the .Net part working, but I can not figure out how to implement it on the angular side. I'm not sure if I'm getting the select dropdown correctly implemented.
In my console.log(), the role is coming up as 'undefined'. Any help is appreciated.
The below is the current code.
ASP.NET Web API
[HttpPost]
public async Task<IActionResult> Role(Roles role)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
var assignRole = await _contextFile.AddRoles(role.Email, role.UserRoles);
}
catch (Exception ex)
{
throw ex;
}
return Ok();
}
Angular 7
Service
userRole(email: string, role: string){
return this.http.post(this.Url + {email, role});
}
populateDropdownRoles(){
return this.http.get(this.roleUrl);
}
.ts file
assignRole() {
this.roleService.userRole(this.user.email, this.role);
}
dropdownRole() {
this.roleService.populateDropdownRoles();
}
.html file
<form (ngSubmit)="assignRole()">
<mat-form-field>
<input
matInput
placeholder="Email"
name="email"
value="{{ user.email}}"
/>
</mat-form-field>
<mat-form-field>
<mat-select placeholder="Role">
<mat-option *ngFor="let role of roles" name ="rolename" [(ngModel)]="role.rolename" value="{{role.rolename}}">
{{role.rolename}}
</mat-option>
</mat-select>
</mat-form-field>
<button
mat-raised-button
type="submit"
>
Assign
</button>
</form>

I just mocked this up really quickly which is working. You may have to modify some of the variable names.
Moved [(ngModel)] and name to the <mat-select> element.
Declared the variable that is being bound to [(ngModel)] inside component class.
Printed out selected value in assignRole() method.
NOTE: This is assuming your are successfully receiving role values back from your API. I added a mock roles array inside the component class.
component.ts
#Component({
selector: 'app-roles-form',
templateUrl: './roles-form.component.html',
styleUrls: ['./roles-form.component.css']
})
export class RolesFormComponent implements OnInit {
public roles = [{ rolename: 'user' }, { rolename: 'admin' }];
public selectedRole: any;
constructor() { }
ngOnInit() {
}
assignRole() {
console.log('selectedRole: ', this.selectedRole);
}
}
component.html
<form (ngSubmit)="assignRole()">
<mat-form-field>
<mat-select placeholder="Role" [(ngModel)]="selectedRole" name="selectedRole">
<mat-option *ngFor="let role of roles" value="{{role.rolename}}">
{{role.rolename}}
</mat-option>
</mat-select>
</mat-form-field>
<button mat-raised-button type="submit">
Assign
</button>
</form>
DOCS
https://stackblitz.com/angular/gokjjqynqvo?file=main.ts
https://material.angular.io/components/select/overview

A few things I noticed: The name of you model in the controller is role and it is a string according to the parameters received in the service method, so you should use [(ngModel)]="role" and also I think you should put it at the mat-select tag not the mat-option. So your html should look something like this.
<mat-form-field>
<mat-select [(ngModel)]="role" name ="role" placeholder="Role">
<mat-option *ngFor="let role of roles" value="{{role.rolename}}">
{{role.rolename}}
</mat-option>
</mat-select>
</mat-form-field>
PS: I would suggest you change the name of the model to roleName. like this.roleName and [(ngModel)]="roleName" name ="roleName" it would be less confusing for you.
You could check the example here

You have the ngModel wrong, ngModel goes on the control, not the options.
<mat-select [(ngModel)]="role" name ="rolename" placeholder="Role">
<mat-option *ngFor="let role of roles" [value]="role.rolename">
{{role.rolename}}
</mat-option>
</mat-select>
After that you need to check on the .Net part the structure of the class Role because, if my memory is not failing, the UserRoles property is a list of selected roles, if that is correct you need to send an array of roles, not just one role.

Related

Angular set focus to select element on page load

I can set focus to an input field, but not a select. What am I missing?
This works:
<input matInput formControlName="name" required maxlength="100" id="elementFocus" appElementFocus="true" />
This does not
<mat-select formControlName="countryId" required id="elementFocus" appElementFocus="true">
Here's the entire section that exists now, and when the page loads, the 2nd form element (Name) has focus. I need the select to have focus.
<mat-card-content>
<div fxLayout="column" fxLayoutGap="25px" class="container">
<mat-form-field appearance="standard">
<mat-label>Countries</mat-label>
<mat-select formControlName="countryId" #countrySelectRef required>
<mat-option *ngFor="let c of countries" [value]="c.id"
>{{ c.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="hasError(f.countryId)">{{
getErrorMessage(f.countryId)
}}</mat-error>
</mat-form-field>
<mat-form-field appearance="standard">
<mat-label>Name</mat-label>
<input matInput formControlName="name" required maxlength="100" />
<mat-hint align="end">{{ f.name.value.length }} / 100</mat-hint>
<mat-error *ngIf="hasError(f.name)">{{
getErrorMessage(f.name)
}}</mat-error>
</mat-form-field>
</div>
</mat-card-content>
.ts code (narrowed down to all that is relevant, I think).
#ViewChild(MatSelect) countrySelectRef: MatSelect;
constructor(
private formBuilder: FormBuilder,
private activatedRoute: ActivatedRoute,
private router: Router,
private matDialog: MatDialog,
private readonly countryService: CountryService,
private readonly stateService: StateService,
private messageService: UIMessageService) { }
ngOnInit(): void {
const paramId = this.getParam('id');
this.isAdd = !paramId
this.id = !paramId ? uuid() : paramId
this.heading = this.isAdd ? `Add ${this.headingName}` : `Edit ${this.headingName}`
this.initForm();
if (!this.isAdd) {
this.patchForm()
}
}
ngAfterViewInit(): void {
if (this.countrySelectRef) {
this.countrySelectRef.focus();
}
}
Your code has a problem, in the sense that it can trigger a ExpressionChangedAfterItHasBeenCheckedError error, but even with that error, it should work fine. Your code and the error message, described in your comment, don't match up and may indicate that the issue is elsewhere.
I do provide an alternative, which hopefully, will solve your problem. You need to add the A11yModule to the imports array of the module that declare the component that use that template.
Template
<mat-form-field appearance="standard" cdkMonitorSubtreeFocus>
<mat-label>Countries</mat-label>
<mat-select formControlName="countryId" #countrySelectRef required
cdkTrapFocusAutoCapture
cdkTrapFocus="false">
<mat-option *ngFor="let c of countries" [value]="c.id">
{{ c.name }}
</mat-option>
</mat-select>
<mat-error *ngIf="hasError(f.countryId)">
{{ getErrorMessage(f.countryId) }}
</mat-error>
</mat-form-field>
Note the 2 (with 3 inputs in total) directives added:
cdkMonitorSubtreeFocus: considers an element focused if it or any of its children are focused
cdkTrapFocusAutoCapture: Whether the directive should automatically move focus into the trapped region upon initialization and return focus to the previous activeElement upon destruction.
cdkTrapFocus="false": If this value is true, it will trap the focus on that directive indefinitely.
Ref: Material documentation

Routing via MatAutocomplette Angular

I am trying to implement the autocomplete component of Angular Material:
It works fine, it at least displays and I see the data from my json. Is there a way to route on click of the event, that is when something is selected from the autocomplete to a page?
My HTML currently looks like this:
<div class="example-form">
<div class="container">
<form [formGroup]="schoolForm">
<mat-form-field class="example-form">
<input type="text" matInput placeholder="Gib den Namen deiner Schule ein" formControlName="schoolGroup" required
[matAutocomplete]="autoGroup">
<mat-autocomplete #autoGroup="matAutocomplete">
<mat-optgroup *ngFor="let group of schoolGroupOptions | async" [label]="group.letter">
<mat-option *ngFor="let name of group.names" [value]="name">
{{name}}
</mat-option>
</mat-optgroup>
</mat-autocomplete>
</mat-form-field>
</form>
<form [formGroup]="schoolForm">
<mat-form-field class="example-form1">
<input type="text" matInput placeholder="Gib den Nachnamen deines Lehrers ein">
</mat-form-field>
</form>
</div>
<button mat-raised-button color="primary" routerLink="/teachers">Ergebnisse anzeigen</button>
</div>
how should the TS. look like?
Thx for your help
Looking at the documentation we see that MatAutoComplete exposes an event when an option is selected.
#Output() optionSelected
Reading further, we need to add [matAutocomplete] as a directive onto our text input, and pass it a reference to the auto-complete in our template.
To do this, I create a mat-autocomplete and give it the template literal name of #navigable which we pass into the [matAutocomplete] directive.
This gives us the following html template
<mat-form-field>
<input type="text" matInput [matAutocomplete]="navigable">
<mat-autocomplete #navigable (optionSelected)="chooseOption($event)">
<mat-option *ngFor="let group of groups" [value]="group.route">{{group.name}}</mat-option>
</mat-autocomplete>
</mat-form-field>
Note we can bind to (optionSelected) on the mat-autocomplete as it is the event stated in the API to be called when the value is selected for our attached input.
in our .ts file
#Component()
export class MyExampleComponent {
// group dummy object, hard-coded for demonstration purposes
// I've chosen to store the route directly on the group.
// You should choose whichever strategy suits your needs
groups = [
{
name: "One",
route: 'one'
},
{
name: "Two",
route: 'two'
},
{
name: "Three",
route: 'three'
}
];
// inject router for navigation
constructor(private router: Router) {}
chooseOption(event: MatAutocompleteSelectedEvent) {
// destructure the value and navigate.
const {option: {value}} = event
this.router.navigate([value]);
}
}
and that is it. We will now navigate as soon as an option is selected in the dropdown list.

How to use angular material drop down to to see on each select an the "the company name" from the sql server

How to fix it so it will work, want to use angular angular material drop down to to see on each select an the "the company name" from the sql server
I would like to bind the dropdown select and it will let me choose company name that i already imported
TS-https://prnt.sc/p2eg21
HTML-https://prnt.sc/p2eguq
Component:
import { Component, OnInit } from '#angular/core';
import {MatPaginator, MatTableDataSource,MatSort} from '#angular/material';
import { User } from 'src/app/services/user.model';
import { UserService } from 'src/app/services/user.service';
#Component({
selector: 'app-companyinteraction',
templateUrl: './companyinteraction.component.html',
styleUrls: ['./companyinteraction.component.css']
})
export class CompanyinteractionComponent implements OnInit {
// displayedColumns: string[] = ['CompanyName'];
// dataSource:MatTableDataSource<User>;
public companiesNames:User[];
constructor(private prodService:UserService) { }
ngOnInit() {
const ob = this.prodService.getUsers();
ob.subscribe(users => {
this.companiesNames = users;
console.log(this.companiesNames);
console.log('Test from CompanyInteraction');
});
//...
}
Template:
<h4>Basic mat-select</h4>
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select>
<mat-option *ngFor="let User of companiesNames" [value]="companyName">
{{companyName}}
</mat-option>
</mat-select>
</mat-form-field>
You could do it like this:
<mat-form-field>
<mat-label>Favorite food</mat-label>
<mat-select>
<mat-option *ngFor="let user of companiesNames" [value]="user.id">
{{user.name}}
</mat-option>
</mat-select>
</mat-form-field>
Then the mat-select will give you the id of the selected one. Read the documentation to get the selected value here: https://material.angular.io/components/select/api
<h4 class="valuePicker">Select Company</h4>
<mat-form-field class="valuePicker">
<mat-label>Select Company</mat-label>
<mat-select>
<mat-option *ngFor="let p of companies" [value]="p.CompanyName">
{{p.CompanyName}}
</mat-option>
</mat-select>
</mat-form-field>
Thanks.... figured it out

How to get index of selected option outside the select in Angular?

I have a simple <select> in Angular (with Material) as follows:
<mat-form-field>
<mat-label>Type</mat-label>
<mat-select placeholder="Type" formControlName="type" name="type" id="name">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types" [value]="t">
{{t}} <-- it is enum -->
</mat-option>
</mat-select>
</mat-form-field>
I would like to use index of selected type in the other part of code.
To be more precise: in *ngFor of another select. Therefore, I can't use documentById.
In addition, I don't want to install jQuery just for do this.
Is it possible?
As suggested by this Angular Material Documentation example, you can bind to [(value)] two ways:
<mat-form-field>
<mat-select [(value)]="selected">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types; let i = index" [value]="i">{{t}}</mat-option>
</mat-select>
</mat-form-field>
<p>You selected: {{selected}}</p>
selected will be a property on your class.
Here's a Working StackBlitz from the Angular Team for your reference.
You can simply set [(ngModel)] variable and use the variable to get your index
<mat-select placeholder="Type" [(ngModel)]="selected" formControlName="type" name="type" id="name">
<mat-option>None</mat-option>
<mat-option *ngFor="let t of types" [value]="t">
{{t}} <-- it is enum -->
</mat-option>
</mat-select>
and then in the component, use
this.index = this.types.findIndex(item => item === selected);
Per Sajeetharans answer, using ngModel together with reactive form is not recommended, and is also deprecated. Instead watch for the changes of the form control and find the index and store it in a variable for later use. Here's a sample:
myForm: FormGroup;
idx: number;
foods = ['Steak', 'pizza-1'];
constructor(private fb: FormBuilder) { }
ngOnInit() {
this.myForm = this.fb.group({
mySelect: ['']
});
this.myForm.get('mySelect').valueChanges.subscribe((value) => {
this.idx = this.foods.findIndex(val => val === value);
console.log(this.idx)
});
}

Angular - Building form based on user input

I'm building a web form that is supposed to be dynamic.
When a user selects an option from a list, the next form inputs are generated based on his input.
For instance:
<mat-form-field>
<mat-select placeholder="Type" [(ngModel)]="row.Type" (change)="TypeChosen(row.Type, row)">
<mat-option [value]="0">Treatment</mat-option>
<mat-option [value]="1">Travel</mat-option>
<mat-option [value]="2">Medication</mat-option>
<mat-option [value]="3">Equipment</mat-option>
</mat-select>
</mat-form-field>
If he selects Type 'Treatment', he gets another selection input with some options with a few other inputs, and if he selects a different Type, he gets different options and other inputs.
I understand that I need to dynamically generate HTML content, and maybe a dynamic component.
What is the best approach to do this in an easy way?
I'd suggest creating a component for each sub-form and then *ngIf them based on the selected option, like so:
<!-- component.html -->
<mat-form-field>
<mat-select placeholder="Type" [(ngModel)]="row.Type" (change)="onTypeChosen(row.Type, row)">
<mat-option [value]="0">Treatment</mat-option>
<mat-option [value]="1">Travel</mat-option>
<mat-option [value]="2">Medication</mat-option>
<mat-option [value]="3">Equipment</mat-option>
</mat-select>
</mat-form-field>
<my-treatment-component *ngIf="type === 0" [someInput]="'some value'"></my-treatment-component>
<my-travel-component *ngIf="type === 1" [somethingElse]="true"></my-travel-component>
<my-medication-component *ngIf="type === 2" (someOutput)="onMedicationOutput($event)"></my-medication-component>
<my-equipment-component *ngIf="type === 3"></my-equipment-component>
If you already have something holding your Type selection, you can bind that to the *ngIfs instead. If not, create a field in your controller class and hold the selected type in there.
// component.ts
public type: number | null = null;
public onTypeChosen(type: number, row): void {
this.type = type;
}
If your sub-forms have parts that are re-usable (or are basically the same, sans configuration), it's generally a good practice to extract the re-usable code into components in and of themselves and compose them together.
Hope this helps a little :-)
To Add the options dynamically, angular provide ( *ngFor ).
<mat-form-field>
<mat-select placeholder="Type" [(ngModel)]="row.Type" (change)="TypeChosen(row.Type, row)" *ngFor="let option of options; let i = index">
<mat-option (click)="updateOptions(option)" [value]="{{i}}">option.text</mat-option>
</mat-select>
</mat-form-field>
in your controller .ts
private options = [];
private initOptions(){
this.options = [
{text:'Treatment' , possibleOptionsRelates:[text:'possible1']},
{text:'Travel' , possibleOptionsRelates:[text:'possible12']},
{text:'Medication' , possibleOptionsRelates:[text:'possible13']}];
}
private updateOptions(option){
this.options.push(...option.possibleOptionsRelates);
}