0

I have a person component that looks something like this (very simplified):

person.component.html

<div *ngfor='let current of users'>
  <p>{{current?.name}} <a (click)='more(current?.id)'>Click for more</a>
   <span [id]='current?.id + "_info"'></span>
  </p>
</div>

person.component.ts

export class Person {
  constructor() { }
  more(id: any){
    let response = `more info <button class='btn btn-primary btn-sm' (click)="changeCurrent()">`;
    $('#' + id + '_info').html(response);
  }
  change() {
    console.log('hello world', this);
  }
}

When I click the more button, more(id) fires and it shows more information about the person. So that's working.

But when I click the change button, change() never fires. I know there's a better way and jquery needs to go, but I'm just not finding an example yet.

Thanks,


Heh. I knew jQuery was the wrong way to go, but sometimes I get frustrated and just want to get something to work.

Duncan gave me the clue to what I finally came up with. I think it is close to the Angular way of thinking, so I'm adding it here.

A better description of the example I'm playing with: A veterinary clinic has multiple vets, and each vet has multiple patients. A web page displays a list of vets. Clicking on a vet's name displays a list of the patients, with a button next to the patient name to toggle the patient's status, Current /Not Current.

all-vets.component.ts

export class AllVetsComponent implements OnInit {
  vets: any = [];
  patients: any = [];
  displayPatients: any = [];
  patientsRetrieved: boolean = false;
  constructor(private http:  Httppatient) { }

  ngOnInit() {
    // hit a web api, parse the data into this.vets and this.patients
  }

  getAllPatients(vetID: number): any[] {
    // hit web api and get just the patients for the vet id
  }
  getPatients(vetID: number): void {
    this.patientsRetrieved = false;
    this.displayPatients[vetID] = [];
    this.displayPatients[vetID] = getAllPatients(vetID);
    this.patientsRetrieved = true;
    $('#' + vetID + '_patients').toggle(500); // yes, still jQuery, but used for animations
  }

  showPatients(): boolean{
    return this.patientsRetrieved;
  }

  changeStatus(patientID: number): void{
    // find the patient with that id and toggle the currentPatient value
    // post the change back to the api so it can go in the db
  }
}

all-vets.component.html

  <div class='card' *ngFor='let vet of vets'>
    <div class='card-header' role='tab' [id]='vet?.vet_id'>
      <a (click)='getAllPatients(vet?.vet_id)'
         class="collapsed"
         data-toggle="collapse"
         (href)='"#" + vet?.vet_id'
         aria-expanded="false"
      >
        {{vet?.vet_first_name}} {{vet?.vet_last_name}}
      </a>
    </div>

    <div [id]='vet?.vet_id + "_patients"'
       class="collapse hide" role="tabpanel"
       [attr.aria-labelledby]='vet?.vet_id' [attr.data-parent]='"#" + vet?.vet_id'>
    <div class="card-body patient-display">
      <div class='container-fluid'>
        <div class='row patient-listing patient-listing-header'>
          <span class='patient-name col'>Name</span>
          <span class='current-patient col'>Change Status</span>
        </div>
        <ng-template ngIf='showPatients()'>
          <div class='row patient-listing' *ngFor='let patient of patients'>
            <span class='patient-name col ' [ngClass]="{'currentpatient': patient.current_patient === 1, 'notpatient': patient.current_patient !== 1}">
              {{patient?.name}}
            </span>
            <span class='patient-owner col'>
              {{patient?.owner}}
            </span>
              <button class='btn btn-primary btn-sm' (click)='changeStatus(patient?.name)'>
                  <span *ngIf='patient.current_patient'>
                    Current
                  </span>
                  <span *ngIf='!patient.current_patient'>
                    Not Current
                  </span>
              </button>
            </span>
          </div>
        </ng-template>
      </div>
    </div>
  </div>
</div>

I've removed extraneous details and changed names to protect the innocent.

This works for me. The only non-angular piece is the jquery that displays the card body. I could use angular to just toggle classes, but I wanted to add the delay so it wasn't as abrupt. There's probably a way to do that as well; I just haven't learned it yet. Clicking the button correctly updates the button text and changes the style of the patient's name, and that's the general behavior I was after.

Todo:

  • replace the remaining jQuery toggle function
  • add aria-live and aria-announce to the right elements
  • add aria-role and other aria as appropriate
  • use all of the complicated real world data instead of the proof of concept stuff

Thanks Duncan!

1
  • Stop trying to mix jQuery into your angular code. You cannot use any angular directives inside hmtl that you just inserted into the DOM yourself, you must ensure everything goes through angular. Commented Nov 1, 2017 at 16:19

2 Answers 2

2

You need to use angular to update the DOM. HTML inserted using jQuery won't have been compiled to use any of your angular directives.

<div *ngfor='let current of users'>
  <p>{{current?.name}} <a (click)='more(current?.id)'>Click for more</a>
   <span *ngIf="wantMore[current?.id]">
       more info
       <button class='btn btn-primary btn-sm' (click)="changeCurrent()">
   </span>
  </p>
</div>

Then in your code:

export class Person {
  public wantMore = {};
  constructor() { }
  more(id: any){
    this.wantMore[id] = true;
  }
  changeCurrent() {
    console.log('hello world', this);
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Makes sense. I did forget to add the loop around the line because there would be multiple <button> tags built in the code. If I just had one line, your response would be copy/paste. As it was, it led me to the right answer. I'll post it once I'm back at work and have the syntax right. Or close to right. :) I think I can use ng-template or container in the parent combined with your example, or I can use the ngIf to set the value of an array and loop through that way. I didn't explain that well. But I'll know what I mean when I read this later. :)
-1

try this:

let response = `more info <button id='myIid' class='btn btn-primary btn-sm'>`;
$('#' + id + '_info').html(response);
var el = this.elementRef.nativeElement.querySelector('#myIid');
if(el) {
    el.addEventListener('click', this.changeCurrent.bind(this));
}

Besides, i saw you invoke changeCurrent function, but called your function change...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.