Angular JS Support Tool - Make File Attachment Option - Not Required

I was handed over a support tool by our contractor that is based on Angular JS. I have some basic understanding of how it functions, but I am having trouble turning off a check on a particular module that is required that an attachment is included before the form can be submitted to a SharePoint list. I have included the code for the HTML as well as the associated code files. Please advise what need to be changed to allow for an attachment to be included but NOT have it be required.

HTML
<!--removed this from submit button [disabled]='!trgIssueForm.valid'-->
<div [@routerTransition] class="panel panel-primary">
    <!--<h2>Request Management- <small>{{_requestService?._selReq?.Title}}</small></h2>-->
    <div class="panel-heading">
        {{_requestService?._selReq?.Title}}
    </div>
    <!--<hr>-->
    <div class="panel-body">
        <form class="form-horizontal"
              novalidate
              (ngSubmit)="saveRequest()"
              [formGroup]="trgIssueForm" >
            <fieldset>
                <div class="checkbox">
                    <label class="formyself">
                        <input type="checkbox" formControlName="MyRequest">This request is for myself
                    </label>
                </div>
                <div class="form-group">
                    <mat-form-field class="col-md-6">
                        <input matInput class="form-control" 
                                id="requesterWWIDId" 
                                type="text" 
                                placeholder="WWID" 
                                value={{_impUser?.WWID}}
                                formControlName="RequesterWWID" />
                                <mat-hint align="start">{{_impUser?.RequesterName}}</mat-hint>
                    </mat-form-field>
                </div>
                <!--<div class="form-group">
                    <mat-form-field class="col-md-6">
                        <input matInput class="form-control" 
                                id="RequesterName" 
                                type="text" 
                                placeholder="Requester Name" 
                                value={{_impUser?.RequesterName}}
                                formControlName="RequesterName" />
                    </mat-form-field>
                </div>-->
                <div class="form-group">
                    <mat-form-field class="col-md-6">
                        <mat-select class="form-control"
                            id="ReqActSelect" 
                            placeholder="Select Issue Type"
                            formControlName="ReqActSelect">
                            <mat-option value="TrainingIssue">Completion issue</mat-option>
                            <mat-option value="TrainingIssue">Training not working</mat-option>
                            <mat-option value="TrainingIssue">Other</mat-option>
                            <!--<mat-option value="WhyTrg">Why did I get this assigned?</mat-option>-->
                        </mat-select>
                    </mat-form-field>
                </div>
                <div class="form-group">
                    <mat-form-field class="col-md-6">
                        <input matInput [matAutocomplete]="auto"
                                class="form-control" 
                                id="trainingId" 
                                type="text" 
                                placeholder="Training"
                                formControlName="Training" />
                                <mat-autocomplete #auto="matAutocomplete">
                                    <mat-option *ngFor="let trg of filteredTrainings | async" [value]="trg.TrainingCode">
                                        <span>Title: {{trg.Title}}</span> | Code: <span>{{ trg.TrainingCode }}</span>
                                    </mat-option>
                                </mat-autocomplete>
                    </mat-form-field>
                </div>
                <div class="form-group" [hidden]="_hideScreenshot">
                    <div class="col-md-6">
                        <input class="form-control file-attach" 
                            id="Screenshot" 
                            type="file" 
                            aria-describedby="fileHelp"
                            placeholder="Attache Screenshot" 
                            accept=".jpeg, .png"
                            formControlName="Screenshot" />
                        <!--<p *ngIf="trgIssueForm.controls.Screenshot.errors?.required" style="color: red">Screenshot is required!</p>-->
                        <label class="file-attach-label">
                            Attach a Screenshot
                        </label>
                    </div>
                </div>
                <div class="form-group">
                    <mat-form-field class="col-md-6">
                        <textarea matInput class="form-control" 
                                id="requestDescriptionId" 
                                placeholder="Description"
                                rows=3
                                formControlName="RequestDescription"></textarea>
                    </mat-form-field>
                </div>
                <div class="form-group">
                    <div class="col-md-4 col-md-offset-2">
                        <span>
                            <button mat-raised-button class="btn btn-primary"
                                    type="submit"
                                    style="width:80px;margin-right:10px"
                                    >
                                Submit
                            </button>
                        </span>
                        <span>
                            <a class="btn btn-default"
                               style="width:80px"
                               (click)="cancelRequest()">
                                Cancel
                            </a>
                        </span>
                     </div>
                </div>
            </fieldset>
        </form>
        <div class='has-error' style="color: red" *ngIf='errorMessage'>{{errorMessage}}</div>
    </div>
    <!--<ngb-alert [type]="alert.type" (close)="closeAlert(alert)" *ngFor="let alert of alerts">{{ alert.message }}</ngb-alert>-->
</div>

Open in new window


TS Components
import { Component, OnInit, AfterViewInit, OnDestroy, ViewChildren, ElementRef, Input } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, FormArray, Validators, FormControlName } from '@angular/forms';
import { routerTransition } from '../../router.animations';
import { RequestService, FileValidator } from 'app/core';
import { Router, ActivatedRoute, NavigationEnd } from '@angular/router';
import { GenericValidator } from '../../shared/generic-validator';
import { TrgIssueService } from './trgissue.service';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import {startWith} from 'rxjs/operators/startWith';

import { DISABLED } from '@angular/forms/src/model';
import { HttpClient } from '@angular/common/http';
import { IImpUser } from 'app/reqmgmt/dtos';
import { map, filter, debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';

@Component({
  selector: 'app-req-whytrg',
  templateUrl: './trgissue.component.html',
  animations: [routerTransition()]
})
export class TrgIssueComponent implements OnInit {

  currentUrl: string;
  @ViewChildren(FormControlName, { read: ElementRef }) formInputElements: ElementRef[];

  // pageTitle = 'Product Edit';
  // errorMessage: string;
  trgIssueForm: FormGroup;
  selTraining: any = null;
  public errorMessage = null;
  public _hideScreenshot = true;
  public _impUser: IImpUser = <IImpUser>{};
  // private sub: Subscription;
  MyRequest: FormControl = new FormControl();
  private isMyrequest = true;
  // MyRerequesterWWIDId: FormControl = new FormControl();
  // Use with the generic validation message class
  // displayMessage: { [key: string]: string } = {};
  // private validationMessages: { [key: string]: { [key: string]: string } };
  private genericValidator: GenericValidator;
  filteredTrainings: Observable<any[]>;
  constructor(private _router: Router, public _requestService: RequestService,
    private fb: FormBuilder,
    private _http: HttpClient,
    private route: ActivatedRoute,
    private router: Router,
    private _trgIssueService: TrgIssueService ) {
      _router.events.subscribe((event) => {
        if (event instanceof NavigationEnd ) {
            this.currentUrl = event.url;
            console.log('TrgIssueComponent- constructor - this.currentUrl', this.currentUrl);
        }
    });
    // this.genericValidator = new GenericValidator(this.validationMessages);
  }
  filterTrainings(TrainingCode: string) {
    console.log('TrgIssueComponent -> filterTrainings: TrainingCode', TrainingCode);
      return this._requestService._distTrgs.filter(trg =>
        trg.TrainingCode.toLowerCase().indexOf(TrainingCode.toLowerCase()) === 0);
  }
  buildForm() {
    this.trgIssueForm = this.fb.group({
      MyRequest: true,
      RequesterWWID: new FormControl(this._impUser.WWID, {
        validators: [Validators.required], updateOn: 'blur'}),
      // RequesterName: new FormControl(this._impUser.RequesterName, {
      //  validators: [Validators.required], updateOn: 'blur'}),
      ReqActSelect: ['', Validators.required],
      Screenshot: ['', FileValidator.validate],
      Training: ['', Validators.required],
      RequestDescription: ''
    });
  }
  ngOnInit() {
    console.log('TrgIssueComponent Component OnInit');
    this.setImpUserToDefault();
    // If user directly came to thispage, we need to get Selected Request Item
    if ( !this._requestService._selReq) {
      // filter this request type from reqTypes collection
      this._requestService._selReq = Object.assign({}, this._requestService._requestTypes.filter(req =>
        req.ReqLink === this.currentUrl)[0]);
        console.log('TrgIssueComponent->ngOnInit-_selReq',  this._requestService._selReq);
    }
    // get all the data
    // this._requestService.getTrainings();
    this.buildForm();
    this.trgIssueForm.get('RequesterWWID').disable();
    // this.trgIssueForm.get('RequesterName').disable();
    this.filteredTrainings = this.trgIssueForm.get('Training').valueChanges
      .pipe(
        startWith(''),
        map(trg => trg ? this.filterTrainings(trg) : this._requestService._distTrgs)
      );
    this.trgIssueForm.get('MyRequest').valueChanges
        .subscribe(value => this.onChangeMyRequest(value));
    this.trgIssueForm.get('ReqActSelect').valueChanges
    .subscribe(value => this.onReqActionChange(value));
    this.trgIssueForm.get('RequesterWWID').valueChanges.
      subscribe(inputval => {
        console.log('TrgIssueComponent->ngOnInit- getWWIDDataByWWID -> inputval', inputval );
        if (!this.trgIssueForm.get('MyRequest').value) {
          this._requestService.getWWIDDataByWWID(inputval).then(res => {
            console.log('TrgIssueComponent->ngOnInit- getWWIDDataByWWID -> res', res);
            if (res && res.length === 1 ) {
              const tUser: IImpUser = <IImpUser>{};
              tUser.WWID = res[0].Title;
              tUser.RequesterName = res[0].FirstName + ' ' + res[0].LastName ;
              tUser.Email = res[0].Email;
              this._impUser = tUser;
              // this.trgIssueForm.get('RequesterName').setValue(this._impUser.RequesterName);
              console.log('TrgIssueComponent->ngOnInit- getWWIDDataByWWID -> this._impUser', this._impUser);
              // this.trgIssueForm.get('RequesterWWID').setValue(this._impUser.RequesterName);
            } else {
              this._impUser = null;
              this.trgIssueForm.get('RequesterWWID').setErrors({
                'required': true });
            }
        });
      }
    });
  }
  setImpUserToDefault() {
    const tUser: IImpUser = <IImpUser>{};
    tUser.WWID = this._requestService._userProfile.WWID;
    tUser.RequesterName = this._requestService._userProfile.DisplayName;
    tUser.Email = this._requestService._userProfile.Email;
    this._impUser = tUser;
  }
onReqActionChange(action: String) {
  console.log('TrgIssueComponent -> onReqActionChange: action', action);
  if (action && action.toLowerCase() === 'whytrg') {
    this.trgIssueForm.get('Screenshot').setValidators([]);
    this.trgIssueForm.get('Screenshot').updateValueAndValidity();
    this._hideScreenshot = true;
  } else if (action && action.toLowerCase() === 'trainingissue') {
    this.trgIssueForm.get('Screenshot').setValidators([FileValidator.validate]);
    this.trgIssueForm.get('Screenshot').updateValueAndValidity();
    this._hideScreenshot = false;
  }
}

  // (change)="onChangeMyRequest($event.target.checked)"
  onChangeMyRequest(isChecked: boolean): void {
    console.log('TrgIssueComponent -> onChangeMyRequest: _impUser', isChecked, this._impUser);
    if ( isChecked ) {
      // Set ImpUser to default
      this.setImpUserToDefault();
      this.trgIssueForm.get('RequesterWWID').setValue(this._impUser.WWID, {emitEvent: false});
      this.trgIssueForm.get('RequesterWWID').disable();
    } else {
      this.trgIssueForm.get('RequesterWWID').enable();
      this._impUser = null;
    }

  }
  saveRequest(): void {
    console.log('TrgIssueComponent -> saveRequest -');
    if (this.trgIssueForm.dirty && this.trgIssueForm.valid) {
        // Copy the form values over the product object values
        const p = Object.assign({}, this.trgIssueForm.value);
        // get selected trainng detail
        this.selTraining = this._requestService._trgUgInv.filter(trg =>
          trg.TrainingCode.toLowerCase().indexOf(p.Training.toLowerCase()) === 0)[0];
          console.log('TrgIssueComponent -> saveRequest-> this.selTraining', this.selTraining);
        p['TrainingName'] = this.selTraining.TrainingName;
        p['TrainingCode'] = this.selTraining.TrainingCode;
        p['Title'] = this._requestService._selReq.Title + ' - ' + p['ReqActSelect'];
        p['ReqSupportMailbox'] = this._requestService._selReq.ReqSupportMailbox;
        p['RequesterWWID'] = this._impUser.WWID;
        p['RequesterName'] = this._impUser.RequesterName;
        p['RequesterEmail'] = this._impUser.Email;
        p['ReqContentTypeId'] = this._requestService._selReq.ReqContentTypeId;
        console.log('TrgIssueComponent -> saveRequest-> p', p);
        this._trgIssueService.saveRequest(p).subscribe(
          result => {
            console.log('TrgIssueComponent ->saveRequest- > result', result);
            this.trgIssueForm.reset();
            this.router.navigate(['/reqmgmt/confirm']);
        },
        error => {
          this.errorMessage = error;
          console.log('TrgIssueComponent ->saveRequest- > error', error);
        }
      );
    }
}
cancelRequest(): void {
  this.router.navigate(['/home']);
}
  /*ngAfterViewInit(): void {
    // Watch for the blur event from any input element on the form.
    const controlBlurs: Observable<any>[] = this.formInputElements
        .map((formControl: ElementRef) => Observable.fromEvent(formControl.nativeElement, 'blur'));

    // Merge the blur event observable with the valueChanges observable
    Observable.merge(this.trgIssueForm.valueChanges, ...controlBlurs).debounceTime(800).subscribe(value => {
        this.displayMessage = this.genericValidator.processMessages(this.trgIssueForm);
    });
  }*/
}

Open in new window


TS Services
import {Injectable, OnInit} from '@angular/core';
import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
import { sp, Web } from "@pnp/sp";
import { environment } from '../../../environments/environment';
import { RequestService } from 'app/core';
import { Response } from '@angular/http';
import { fromPromise } from 'rxjs/observable/fromPromise';

@Injectable()
export class TrgIssueService {
    constructor(private _reqService: RequestService) {
    }
    // public _reqTypes;
    saveRequest(req): Observable<any> {
        console.log('TrgIssueService -> saveRequest->req', req);
        if (!req.ID) {
            return this.createItem(req);
        }
        // return this.updateRequest(reqWhyTrg);
    }
    private createItem(_req):  Observable<any> {
        console.log('TrgIssueService -> createRequest->_req', _req);
        let _result = null;
        return fromPromise(sp.web.lists.getByTitle('Requests').items.add({
            Title: _req.Title,
            Action: _req.ReqActSelect,
            TrainingCode: _req.Training,
            TrainingName: _req.TrainingName,
            ReqSupportMailbox: _req.ReqSupportMailbox,
            ContentTypeId: _req.ReqContentTypeId,
            RequestDescription: _req.RequestDescription,
            RequesterName: _req.RequesterName,
            RequesterEmail: _req.RequesterEmail,
            RequesterWWID: _req.RequesterWWID,
        }).then((iar: any) => {
            console.log('TrgIssueService -> createItem-> iar', iar);
            if (_req.Screenshot[0]) {
            return sp.web.lists.getByTitle('Requests').items.getById(iar.data.ID).
                attachmentFiles.add(_req.Screenshot[0].name, _req.Screenshot[0]).then(file => {
                    _result = file;
                    return _result;
                });
            } else {
                //return iar;
            }
        }));
    }
    private updateRequest(_req) {
        let _result = null;
        sp.web.lists.getByTitle('Requests').items.add({
            Title: _req.AssignedTrg + ' - ' + _req.RequesterName,
            Training: _req.AssignedTrg,
            RequestDescription: _req.RequestDescription,
            RequesterName: _req.RequesterName,
            RequesterEmail: _req.RequestrEmail
        }).then((iar: any) => {
            console.log('TrgIssueService -> cre qateRequest-> iar', iar);
            _result = iar;
        });
    }
    private extractData(response: Response) {
        const body = response.json();
        return body.data || {};
    }
    private handleError(error: Response): Observable<any> {
        // in a real world app, we may send the server to some remote logging infrastructure
        // instead of just logging it to the console
        console.error(error);
        return Observable.throw(error.json().error || 'Server error');
    }
}

Open in new window


Thanks for your help.
Adam EhrenworthLead Technology AnalystAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Julian HansenCommented:
Firstup Angular JS is 1.x of Angular - the code above is Angular 2,4 or 5 (We leave the JS off).

Secondly the file attachement input (I am assuming it is the file input you are referring to) has this validator specified

 Screenshot: ['', FileValidator.validate],

Open in new window

Which is defined here
import { RequestService, FileValidator } from 'app/core';

Open in new window

So unless this validator does something other than check for existance you can simply remove it from the FormControl

 Screenshot: [''],

Open in new window

0
Adam EhrenworthLead Technology AnalystAuthor Commented:
Thank you for the clarification.

I made this change to the Form Control and attempted to submit a request without an attachment and it still will not proceed. No error is being triggered in console (when I view with F12).

If I attach a file and submit it works fine.

Is there something else I am missing?
0
Julian HansenCommented:
What does your updated code look like.

Note on line 137 of your Component .ts you have some logic that resets the Validator based on the action value - make sure you are not being undone by that code.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Adam EhrenworthLead Technology AnalystAuthor Commented:
Yes, that was the issue. I noticed that when I added your first suggestion.

Thank you for the help! I am slowly learning Angular based on an urgent business need - but hopefully, it will help me out in the future as well.
0
Julian HansenCommented:
You are welcome.

Good luck with it Angular is a great framework but it does take some time to get to grips with the ins and outs of it - hang in there.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Microsoft SharePoint

From novice to tech pro — start learning today.