Tailwind (html) List from FM Records

Noob Question.

I’m practicing with some UI stuff.

I pulled a Stacked List code from the Tailwind website.

It’s obviously pre-filled with some data.

If I wanted to present data to the user using FileMaker record data, how is that done?

How does the Form List know how many records are coming in?

I realize this is a very involved question but I just wanna know the “basics” of how an HTML list gets its list data from FileMaker.

The quick answer is utilizing v-for to loop through the data from FileMaker and create your list.

Let’s take this Tailwind Stacked List for example (copied from here):

<template>
  <ul role="list" class="divide-y divide-gray-200">
    <li v-for="person in people" :key="person.email" class="py-4 flex">
      <img class="h-10 w-10 rounded-full" :src="person.image" alt="" />
      <div class="ml-3">
        <p class="text-sm font-medium text-gray-900">{{ person.name }}</p>
        <p class="text-sm text-gray-500">{{ person.email }}</p>
      </div>
    </li>
  </ul>
</template>

Lets break down what’s required to make this snippet work:
v-for="person in people"
This is looking for an array called “people”. In the case of BetterForms, we would need to change that to model.people, and have an array in our data model called “people”. person is a variable that gets set to a new element in the array for each iteration. In this example, each element in the array is an object with 3 fields: name, email, and image.

To summarize, this v-for loop will iterate through each element in the people array, set the element to person, and will stop when there are no more elements. It will “duplicate” the HTML element that it was applied to as many times as it iterates.

The array the example uses is:

people = [
  {
    name: 'Calvin Hawkins',
    email: 'calvin.hawkins@example.com',
    image:
      'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80',
  },

 ...
]

To make this work with your v-for loop, it must be valid JSON in your data model, which should look like:

"people": [{
        "name": "Calvin Hawkins",
        "email": "calvin.hawkins@example.com",
        "image": "https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
    }, {
        "name": "Kristen Ramos",
        "email": "kristen.ramos@example.com",
        "image": "https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
    }, {
        "name": "Ted Fox",
        "email": "ted.fox@example.com",
        "image": "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80"
    }]

And that’s it. The only change needed in the actual code is changing people to model.people. The end result looks like:

That is the gist of how v-for works and how to modify Tailwind Vue components into your own solutions.

To answer your question about FM Records, all you need is to populate your data model with an array which you can use a v-for to loop through.

1 Like

<sarcasm>I coulda figured that out on my own</sarcasm>

Thank you. I’ll get to work on this.

My JSON skills are lacking.

Once I locate my Attorney, he’s related to my Case Table.

In my Case table, I have a calc field called caseAsJSON

JSONSetElement ( "{}" 
  ; ["id_case" ; id_case ; JSONString ] 
  ; ["dateOfInjury" ; dateOfInjury ; JSONString ]
  ; ["caseManager" ; caseManager ; JSONString ]
  ; ["caseManagerEmail" ; caseManagerEmail ; JSONString ]
  ; ["caseManagerPhone" ; caseManagerPhone ; JSONString ]
  ; ["injuryType" ; injuryType ; JSONString ]
)

That sets my case fields that I need to send to BF in a JSON format.

Now that I’ve located my Attorney, would I set ALL the related Cases (related records) in some sort of JSON Array?
I tried JSONSetElement ( $$BF_Model ;"case" ; Case::caseAsJSON ; JSONArray ) but it only returns the first related record (understandably).

Is there some sort of JSON syntax that will get all the found related Case records and set into a JSON (Array?)?

I’m guessing I’d need to do it in some sort of LOOP or WHILE calculation.

So Attorney’s have multiple related Case records? In your Attorney asJSON, you can add an element that gets all related Case records using JSON.ArrayFromRelated, something similar to:

["case"; JSON.ArrayFromRelated ( Case::caseAsJSON) ; JSONArray]

Here’s an example of an Invoice asJSON that grabs an array of related InvoiceLineItems:

JSONSetElement ( ""; 

["id"; id; JSONString];
["date";date; JSONString];
["description"; description; JSONString];
["address"; address; JSONString];
["notes"; notes; JSONString];
["lineItems"; JSON.ArrayFromRelated ( InvoiceLineItems::asJSON) ; JSONArray]
)

FWIW, I did some searching and found a custom function that creates dynamic JSON for Related Records.

I’m testing it now.

@Andrew
In this Dev Tools data model … am I correct in thinking that in order to show, say, patient email, the syntax would be
model.patient.patient.email?

I’ve tried that and nothing shows.

Well since the second patient is an array, you would have to specify which index of the array you wanted to show. For example, if you wanted to show the first index, it would be model.patient.patient[0].email

Ah. That’s gonna be troublesome since I won’t know how many iterations are present.

What’s the desired result? To show every email?

I’m setting my $$BF_Model as:
Set Variable [$$BF_Model ; Value: (JSONSetElement ( $$BF_Model ;"attorney" ; Case_Attorney::attorneyAsJSON ; JSONObject )

Here’s what I’m trying (my BF Html):

<template>
  <ul role="list" class="divide-y divide-gray-200">
    <li v-for="person in model.attorney.patient" :key="model.attorney.patient.email" class="py-4 flex">
      <img class="h-10 w-10 rounded-full" :src="person.image" alt="" />
      <div class="ml-3">
        <p class="text-sm font-medium text-gray-900">{{model.attorney.patient.nameFirstMiddleLast_c}}</p>
        <p class="text-sm text-gray-500">{{model.attorney.patient.email}}</p>
      </div>
    </li>
  </ul>
</template>

My incoming data model shows (sorry … I couldn’t think of a better way to show what’s coming into BF):

"attorney": {
        "case": [{
            "caseManager": "John Williams",
            "caseManagerEmail": "caseManager@attorney.com",
            "caseManagerPhone": "",
            "dateOfInjury": "12/08/2021",
            "id_case": "C5EC1864-57D0-4288-BA24-42770723D6E1",
            "injuryType": "Auto"
        }, {
            "caseManager": "Peter Piper",
            "caseManagerEmail": "peter@attorneyCaseManager.com",
            "caseManagerPhone": "",
            "dateOfInjury": "01/04/2022",
            "id_case": "293A0B51-5F02-4C1D-8EC0-32E5EC7CF8F9",
            "injuryType": ""
        }, {
            "caseManager": "Peter Piper",
            "caseManagerEmail": "peter@attorneyCaseManager.com",
            "caseManagerPhone": "",
            "dateOfInjury": "03/04/2022",
            "id_case": "BFAE29AB-DDCA-409A-B169-5F2D2C94F940",
            "injuryType": ""
        }, {
            "caseManager": "John Williams",
            "caseManagerEmail": "caseManager@attorney.com",
            "caseManagerPhone": "",
            "dateOfInjury": "02/27/2022",
            "id_case": "AD0DEA10-4689-4389-9A23-3A0C196CBD0D",
            "injuryType": ""
        }, {
            "caseManager": "Peter Piper",
            "caseManagerEmail": "peter@attorneyCaseManager.com",
            "caseManagerPhone": "",
            "dateOfInjury": "02/27/2022",
            "id_case": "E81B1B42-A747-4F6C-82B9-A186209F7D76",
            "injuryType": ""
        }],
        "company": "HutzHutzHike",
        "email": "redacted",
        "id_attorney": "1650C7B9-67D3-46AA-BD89-4D95005E577C",
        "nameFirst": "Lionel",
        "nameFirstMiddleLast_c": "Lionel Hutz",
        "nameLast": "Hutz",
        "patient": [{
            "addrCity": "Riverside",
            "addrDisplay_c": "redacted",
            "addrState": "CA",
            "addrStreet": "redacted",
            "addrZip": "92506",
            "age": "44",
            "contactMethod": "Email",
            "dateOfBirth": "04/29/1977",
            "email": "redacted",
            "emergencyContact": "redacted",
            "emergencyContactRelationship": "Spouse",
            "heightFeet": "6",
            "heightInches": "2",
            "id_patient": "E3D63C18-43EF-43B7-A70B-AE9EFD7C1B09",
            "insGroupNumber": "X238J9",
            "insName": "Carefirst Inc.",
            "insPolicyNumber": "123456789",
            "language": "Spanish",
            "maritalStatus": "Married",
            "nameFirst": "Waylan",
            "nameFirstMiddleLast_c": "Waylan Wyatt Kruse",
            "nameLast": "Kruse",
            "occupation": "CRNA",
            "pharmAddress": "redacted",
            "pharmName": "Rite Aid",
            "pharmPhone": "951-682-0177",
            "phoneEmergencyContact": "111-111-1111",
            "phoneHome": "000-000-0000",
            "phoneInsurance": "000-000-0000",
            "phoneMobile": "redacted",
            "race": "White, non-Hispanic",
            "sex": "M",
            "weightLbs": "240"
        }, {
            "addrCity": "Cypress",
            "addrDisplay_c": "redacted",
            "addrState": "TX",
            "addrStreet": "21026 Green Jewel Dr",
            "addrZip": "77433",
            "age": "47",
            "contactMethod": "",
            "dateOfBirth": "07/02/1974",
            "email": "redacted",
            "emergencyContact": "redacted",
            "emergencyContactRelationship": "Spouse",
            "heightFeet": "6",
            "heightInches": "0",
            "id_patient": "0C1CDD3A-F09D-49C3-B815-5A660F490362",
            "insGroupNumber": "654321",
            "insName": "Aetna",
            "insPolicyNumber": "E1234567",
            "language": "English",
            "maritalStatus": "Married",
            "nameFirst": "Daniel",
            "nameFirstMiddleLast_c": "Daniel Ray Kruse II",
            "nameLast": "Kruse II",
            "occupation": "Pilot",
            "pharmAddress": "123 anystreet here to 77377",
            "pharmName": "Cvs",
            "pharmPhone": "",
            "phoneEmergencyContact": "‭ redacted",
            "phoneHome": "222-222-2222",
            "phoneInsurance": "redacted",
            "phoneMobile": "redacted",
            "race": "White, non-Hispanic",
            "sex": "M",
            "weightLbs": "210"
        }, {
            "addrCity": "Santa Ana",
            "addrDisplay_c": "redacted",
            "addrState": "CA",
            "addrStreet": "redacted",
            "addrZip": "92704",
            "age": "43",
            "contactMethod": "Phone",
            "dateOfBirth": "redacted",
            "email": "redacted",
            "emergencyContact": "Yo Momma",
            "emergencyContactRelationship": "Lover",
            "heightFeet": "6",
            "heightInches": "2",
            "id_patient": "BCD9985A-1335-4163-944E-610A21F93047",
            "insGroupNumber": "",
            "insName": "",
            "insPolicyNumber": "",
            "language": "English",
            "maritalStatus": "Single",
            "nameFirst": "Dallas",
            "nameFirstMiddleLast_c": "redacted",
            "nameLast": "Kruse",
            "occupation": "Musician",
            "pharmAddress": "",
            "pharmName": "",
            "pharmPhone": "",
            "phoneEmergencyContact": "444-444-4444",
            "phoneHome": "333-333-4444",
            "phoneInsurance": "",
            "phoneMobile": "333-333-3333",
            "race": "White, non-Hispanic",
            "sex": "M",
            "weightLbs": "242"
        }, {
            "addrCity": "Anytown",
            "addrDisplay_c": "123 Main Street Anytown, CA 90000",
            "addrState": "CA",
            "addrStreet": "123 Main Street",
            "addrZip": "90000",
            "age": "33",
            "contactMethod": "",
            "dateOfBirth": "01/01/1989",
            "email": "redacted",
            "emergencyContact": "John Doe",
            "emergencyContactRelationship": "Friend",
            "heightFeet": "5",
            "heightInches": "5",
            "id_patient": "224EA0F9-9FC7-4BC8-B8C6-C7DF1A9FF41A",
            "insGroupNumber": "",
            "insName": "",
            "insPolicyNumber": "",
            "language": "English",
            "maritalStatus": "Married",
            "nameFirst": "Elsa",
            "nameFirstMiddleLast_c": "Elsa Munguia",
            "nameLast": "Munguia",
            "occupation": "Medical Billing",
            "pharmAddress": "",
            "pharmName": "",
            "pharmPhone": "",
            "phoneEmergencyContact": "888-888-8888",
            "phoneHome": "‭redacted",
            "phoneInsurance": "",
            "phoneMobile": "‭ redacted",
            "race": "Hispanic",
            "sex": "F",
            "weightLbs": "100"
        }, {
            "addrCity": "München",
            "addrDisplay_c": "redacted",
            "addrState": "",
            "addrStreet": "redacted",
            "addrZip": "81737",
            "age": "52",
            "contactMethod": "",
            "dateOfBirth": "redacted",
            "email": "redacted",
            "emergencyContact": "Sophia Lauren",
            "emergencyContactRelationship": "Lover",
            "heightFeet": "6",
            "heightInches": "1",
            "id_patient": "8E09A9E2-3623-49FC-946F-CC0CE00C7A30",
            "insGroupNumber": "",
            "insName": "",
            "insPolicyNumber": "",
            "language": "English",
            "maritalStatus": "Single",
            "nameFirst": "Benedick",
            "nameFirstMiddleLast_c": "redacted",
            "nameLast": "Miller",
            "occupation": "Developer",
            "pharmAddress": "",
            "pharmName": "",
            "pharmPhone": "",
            "phoneEmergencyContact": "444-444-4444",
            "phoneHome": "222-222-2222",
            "phoneInsurance": "",
            "phoneMobile": "111-111-1111",
            "race": "White, non-Hispanic",
            "sex": "M",
            "weightLbs": "190"
        }]
    },
    "case": [],
    "company": {
        "addrDisplay_c": "redacted",
        "addrState": "CA",
        "companyName": "Weston Medical",
        "email": "redacted",
        "facebook": "https://www.facebook.com",
        "id_company": "02848995-C537-4271-A9C9-766B61A5BC10",
        "instagram": "https://www.instagram.com",
        "phone": "‭ redacted",
        "phoneFax": "‭ redacted",
        "twitter": "https://www.twitter.com",
        "website": "https://www.westonmedical.org"
    },
    "lang": "English",
    "patient": []
}

The trick here is that to display the information, you no longer need to reference the model. The person variable declared in the v-for will have the information you need for each iteration.

Change {{model.attorney.patient.nameFirstMiddleLast_c}} to {{person.nameFirstMiddleLast_c}} and {{model.attorney.patient.email}} to {{person.email}}

1 Like

Great!

OK I’ll work on this and report back when I get stuck. So…probably in 12 seconds.

OK … so I have another riddle.

As of now, I’m pulling data from the model.attorney.patient

Is it possible at all to ALSO enter in data from model.attorney.case?

I’m trying to show Attorneys the status of their clients’ (patient) case.

Pt data is obviously stored in the Patient Table. The actual Case data is stored in the Case table.

So … the data I want to show is:

Patient::name
Patient::dateOfBirth
Case::casePhase
Case::{possibly some other data}}

I think I understand that in the v-for, we’re declaring where to pull data from … but at this point, it’s only from the model.attorney.patient.

Is it possible to pull data from the JSON that is labeled as case?

If you want more v-for information you can check out the vue docs https://v2.vuejs.org/v2/guide/list.html

If im following, you have model.attorney.patient and model.attorney.case are they connected by an id?
I’ll assume they are. Check out lodash _.filter
Something similar to this would work.

<li v-for="person in model.attorney.patient" :key="model.attorney.patient.email" class="py-4 flex">
      <img class="h-10 w-10 rounded-full" :src="person.image" alt="" />
      <div class="ml-3">
        .....

        <div v-for="case in _.filter( model.attorney.case, function(o) {
                    return o.id == person.id;
                  }); >  
        {{case}}
       </div>
      </div>
    </li>

They are related.

Case::id = Case_Patient::idf_case

Case_Patient (my patient table) has a foreign key filled with the Case primary key.

Something like this?

<div v-for="case in _.filter( model.attorney.case, function(o) {
     return o.idf_patient == person.id;
     });">
    {{ case.casePhase }}
</div>

My full section code:

<div class="bg-white shadow overflow-hidden sm:rounded-md">
    <template>
        <ul role="list" class="divide-y divide-gray-200">
            <li v-for="person in model.attorney.patient" :key="model.attorney.patient.email" class="py-4 flex">
                <div class="min-w-0 flex-1 flex items-center">
                    <div class="flex-shrink-0">
                        <div class="ml-3">
                            <p class="text-sm  font-bold text-indigo-600 truncate">{{person.nameFirstMiddleLast_c}}</p>
                            <p class="text-sm text-gray-500">DOB: {{person.dateOfBirth}}</p>
                            <p class="text-sm text-gray-500">{{person.dateOfInjury}}</p>
                        </div>
                        <div class="d-block ml-5">
                            <div>
                                <p class="text-sm font-medium text-indigo-600 truncate">
                                    Phase:
                                </p>
                                <div v-for="case in _.filter( model.attorney.case, function(o) {
                    return o.idf_patient == person.id;
                  }); ">
                                    {{case.casePhase}}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </li>
        </ul>
    </template>
</div>

I must be missing something as this doesn’t return anything :frowning:

Your code looks pretty close to being correct. With the information we have, it’s hard to know where the issue is, but I would lean towards missing something in your data or wrong id names.

I guess you can also add a function in your onFormLoad.

In the function you would use a javascript for loop to go through the length of the model.attorney.patient . Use the same _.filter syntax you have there and update the model directly to now also include a casePhase

"attorney": {
    "patient": [{
     ....
     "casePhase": "...."
     }]
}

Use console.logs to debug because im just psedocoding this.

for (var i = 0; i < model.attorney.patient; i++) {
     var match = _.filter(model.attorney.case, function(o) {
        return o.id == model.attorney.patient[i].id;
    })
    console.log("match", match)
    console.log(model.attorney.patient[i], "model.attorney.patient")
    model.attorney.patient.casePhase=  match[0].casePhase
    
}