vue: access a specific array from an object inside an array - arrays

I want to show only one of the questions array in a single page, depending on which category the user picks.
faqData = [
{
name: "first-category",
questions: [
{
id: 1,
question: "First question?",
answer: "First Answer"
},
{
id: 2,
question: "Second question?",
answer: "blablablabla"
}
]
},
{
name: "second-category",
questions: [
{
id: 1,
question: "First question?",
answer: "First Answer"
},
{
id: 2,
question: "Second question?",
answer: "blablablabla"
}
]
},
{
name: "third-category",
questions: [
{
id: 1,
question: "First question?",
answer: "First Answer"
}
]
}
];
vue file
<div class="accordion" role="tablist" v-for="eachQuestion in questionList.questions" :key="eachQuestion.id">
<FAQCollapses v-bind:eachQuestion="eachQuestion" />
</div>
//script
data () {
return {
questionList: faqData
}
}
My template code shows a blank space and there's nothing in the console so I'm confused where the mistake is. The problem is I don't know how to specifically get only one category from the faqData array, depending on what category the user clicks. Can someone tell me what is the best practice to achieve my goal? I have read all the similar questions in StackOverflow but it didn't work in my case. Thank you so much.

Best way (and a best practice, I guess), it to implement computed prop with name like, for example selectedFaqQuestion:
computed: {
selectedFaqQuestion () {
return selectedCategory ? this.faqData.find(category => category.name === this.selectedCategory).questions : []
}
}
and use it into v-for:
<div v-for="eachQuestion in selectedFaqQuestion" :key="eachQuestion.id">
<FAQCollapses v-bind:eachQuestion="eachQuestion" />
</div>
Of course, to do so, you need to implement new data prop selectedCategory, where you are going to store selected category, on user's click:
data () {
return {
questionList: faqData,
selectedCategory: null
}
}
So, as you mentioned, you need to handle user click, when going to see any questions based on selected category. To handle its click, you need to use v-on:click. To pass new value of selected category you need to update it: selectedCategory = 'somecategoryname'
'somecategoryname' means something from your faqData prop 'name', for example first-category:
<div> Please, peek FAQ category to show answers:
<span v-on:click="selectedCategory = 'first-category'"> First category </span>
<span v-on:click="selectedCategory = 'second-category'"> Second category </span>
</div>

Related

Making reusable component for array data in reactjs

I want to make a footer component in react with a heading and several links under it like in the image.
Also I want to pass only an array for the links like the following.
const FooterLinksData = [
{
link1: "Insolvers",
link2: "How it works?",
link3: "I'm looking for job",
link4: "I'm looking to hire",
},
{
link1: "Features",
link2: "Pricing",
link3: "Schedule a Demo",
},
];
My code:
return (
<>
<li>
<a href="/" className="footer_link">
{props.data[`link${props.num + 1}`]}
</a>
</li>
// I have written this li tag for times in my code
// props.data is the array I passed using the parent
// props.num is the number (0 in this case) to get link1 from
// the object
</>
);
My problem is that first obj under the array has 4 items while the second one only have 3. Is there any way to make a reusable component for it?
You can add the title inside the json and use it to bracke. Something like
const FooterLinksData = [
{
"title":"some title",
"links":[
"Insolvers",
"How it works"
]
},
{
"title":"some other title",
"links":[
"other Insolvers",
"other How it works"
}]
];
so then you will do something like:
FooterLinksData.map(({title, links}) => ({
<div>
<div>{title}</div>
<ul>{links.map(link => <li>{link}</li>)}</ul>
</div>
})
})
If you ask this question, I assume you use FooterLinksData[x].link1, FooterLinksData[x].link2, FooterLinksData[x].link3 ..... to display your links.
You could change your code to use the map() function instead, not having to deal with a fixed number of links.
Can't really help with code since you didn't posted how you render your links.

Angular2: How do you properly bind to nested data?

I have a component I'm going to use as a shell for multiple choice questions to load into. Here's how the component is set up so far
component
import { Component } from '#angular/core';
export class Answers{
id: string;
answer: string;
}
const answers: Answers[] = [
{
id: 'exp01q',
answer: 'Its fine as is.'
},
{
id: 'exp02q',
answer: 'I want to make minor adjustments.'
},
{
id: 'exp03q',
answer: 'I want to change my image'
},
{
id: 'exp04q',
answer: 'Ive never wanted to use a particular image until now.'
}
];
#Component({
moduleId: module.id,
selector: 'multi-radio-btn',
templateUrl: 'multi-rad-btn.component.html',
styleUrls: ['multi-rad-btn.component.css']
})
export class MultiRadioBtnShell {
question = 'How do you feel about your current image?';
id = 'exp-img-q';
name = 'exp-ques1';
ans = answers;
}
HTML Template
<h3>radio button shell</h3>
<div class="row justify-content-center">
<fieldset [attr.id]='id' class="card col-8 justify-content-center">
<label class="ques-title">
{{question}}
</label>
<div class="row answer-row-section justify-content-center">
<div *ngFor="let answers of ans" class="col col-12 answer-row justify-content-center">
<div class="col justify-content-center">
<input type="radio"
[attr.id]="answers.id"
[attr.name]="name"
[attr.value]="answers.answer" hidden />
<label [attr.for]="answers.id" class="col ques-ans-title" style="background-color: #4b73a0;">
{{answers.answer}}
</label>
</div>
</div>
</div>
</fieldset>
</div>
The reason it's set up like this now is because the way I was trying to do it at first wasn't working so I went to the Tour of Heroes tutorial to follow along with how they loaded all the heroes. The problem was coming from answer not being defined. So I rigged that part up the same way they did the heroes just for the sake of doing something I'm able to follow just to be sure I get the mechanics of how things load.
The original way I tried to do it was with this
// I had this right above the component
export class ExpQ{
question: string;
id: string;
name: string;
answers:[
{
id: string;
answer: string;
}
]
}
// I had this in the component's class
export const expq: ExpQ[] = [
{
question: 'How do you feel about your current image?',
id: 'exp-img-q',
name: 'exp-ques1',
answers:[
{
id: 'exp01q',
answer: 'Its fine as is.'
},
{
id: 'exp02q',
answer: 'I want to make minor adjustments.'
},
{
id: 'exp03q',
answer: 'I want to change my image'
},
{
id: 'exp04q',
answer: 'Ive never wanted to use a particular image until now.'
}
]
}
]
I was calling it in the html with
{{expq.question}}, {{expq.name}}, {{expq.answers.id}}, {{expq.answers.answer}}, etc.
at first with just loading the question it worked fine, but as I got to the answers: part it started breaking. I came across this https://scotch.io/tutorials/using-angular-2s-model-driven-forms-with-formgroup-and-formcontrol and seen the syntax for the addresses: part was pretty much the same as how I needed to structure my data. So I remade everything to resemble that. I still had no luck getting it to work.
Ultimately I'm going to be sending the questions through the parent component with #input and #output as well as a couple other tricks I came across. But before I can even think about that I need to get a handle on how to put the data all into one source so that it properly reads the nested bits of data. All the examples I come across are simple single tier bits of data, so I'm not sure on the syntax I need to use. How can I make this work?
You can define your model like so:
export interface Answer {
id: string;
answer: string;
}
export interface Question {
id: string;
name: string;
question: string;
answers: Answer[];
}
Then your component could have this to test
question1: Question = {
id: 'q1',
name: 'q1',
question: 'Does TypeScript rule?',
answers: [
{ id: 'a1', answer: 'Yes' },
{ id: 'a2', answer: 'Of Course' },
{ id: 'a3', answer: 'Duh' }
]
};
Of course the names don't have to be the same but I think this gives you a better idea of how to model nested data.
Then to display it you will need to iterate over nested structures. Look up the *ngFor directive. You will want to iterate over your answers in this case. Ex:
<div *ngFor="let answer of question1.answers">
{{answer.id}} - {{answer.answer}}
</div>
Need to flatten the objects,
Params :
Objects : at least n>0 array off JSON objects (dose not matter is circular)
target : {}
path : ""
Note : make sure the Objects Array passed in is n>0 at least
flatten(objects, target, path) {
let me = this;
let retArray = [];
for(let x=0; x < objects.length; x++) {
let object = objects[x];
path = path || '';
target={};
target = me.flattenHelper(object, target, path);
retArray.push(target);
}
return retArray;}
..
flattenHelper(object, target, path){
let me = this;
Object.keys(object).forEach(function (key) {
console.log("key : "+ key + " : object : " + (object[key] && typeof object[key] === 'object') + " path : " + path);
if (object[key] && typeof object[key] === 'object') {
me.flattenHelper(object[key], target, path + key);
}
target[path + key] = object[key];
console.log(target);
});
return target;}

How to choose only one radio button using AngularJS

I've tried searching for questions similar to mine, only to find out that our circumstances aren't the same. So I'm posting this here.
I'm trying to create a list of questions where the methods of selection depends on the question, so question #1 can have radio buttons, question #2 can have checkboxes, and so on. One set lets the user choose the answer using radio buttons, however they're behaving like checkboxes (minus the unchecking). Am I missing something?
Below is my HTML:
<ul style="padding-left:30px;">
<li ng-repeat="query in carousel.currentQuestionObject.choices" style="padding-bottom:5px;" ng-init="carousel.checkboxCollection[query.id] = carousel.currentQuestionObject.init">
<input type="{{carousel.currentQuestionObject.inputType}}" id="{{query.id}}" ng-model="carousel.checkboxCollection[query.id]">
<label for="{{query.id}}" style="font-family:'MetricWeb-Regular';font-size:17px;cursor:pointer"> {{query.question}}</label>
</li>
</ul>
And below is my AngularJS that deals with the radio button answers:
{ inputType: "radio", init:false, question: "Tell us about your needs...",
choices: [
{ question: "Foo1", id: "qs1q1"},
{ question: "Foo2", id: "qs1q2"},
{ question: "Foo3", id: "qs1q3"},
{ question: "Foo4", id: "qs1q4"}
]},
And this is what I'm currently getting:
Please help.
Thank you.
Sharing my solution below. Thank you Simon Pertersen for the guidance! Adding name really did the trick.
HTML:
<ul style="padding-left:30px;">
<li ng-repeat="query in carousel.currentQuestionObject.choices" style="padding-bottom:5px;" ng-init="carousel.checkboxCollection[query.id] = carousel.currentQuestionObject.init">
<input name= "carousel.currentQuestionObject.name" type="{{carousel.currentQuestionObject.inputType}}" id="{{query.id}}" ng-model="carousel.checkboxCollection[query.id]">
<label for="{{query.id}}" style="font-family:'MetricWeb-Regular';font-size:17px;cursor:pointer"> {{query.question}}</label>
</li>
</ul>
AngularJS:
{ inputType: "radio", name: 'qs1needs', init:false, question: "Tell us about your needs...",
choices: [
{ question: "Foo1", id: "qs1q1"},
{ question: "Foo2", id: "qs1q2"},
{ question: "Foo3", id: "qs1q3"},
{ question: "Foo4", id: "qs1q4"}
]},

How do perform an "except" filter in Angular?

Suppose that I have an Angular view that allows a user to check books out of a library. My data model consists of two arrays of Book entities which each have a unique ID field plus a title field. The first array contains an entity for every book in the library and the second array contains an entity for every book that the user has checked out.
libraryBooks = [{
id: 0,
title: "The Adventure of Tom Sawyer"}, {
id: 1,
title: "Moby Dick" }, {
id: 2,
title: "To Kill a Mockingbird" }, {
id: 3,
title: "The Three Little Pigs" }];
checkedOutBooks = [{
id: 0,
title: "The Adventure of Tom Sawyer"}, {
id: 3,
title: "The Three Little Pigs" }];
In short, the library has four books and the user has checked out two. If I want to list the books from both arrays, I can write this:
<h1>Library Books</h1>
<div ng-repeat="book in libraryBooks">
{{ book.title }}
</div>
<h1>Checked out Books</h1>
<div ng-repeat="book in checkedOutBooks">
{{ book.title }}
</div>
Suppose I want to display a third list: the subset of library books that the user has not checked out.
I have seen examples where the Angular "filter" is used to specify one particular value that should not be matched in order to narrow down a list, but in this case, I want to exclude multiple values, so how do I go about doing this?
I have seen examples where a custom filter is added to an Angular module, but I think that in this case, any custom filter should be scoped to this controller.
I've got this figured out. The solution is to write a filter function and attach it to $scope like so:
function filter_notCheckedOut(book) {
var i;
for (i = 0; i < that.libraryBooks.length; i += 1) {
if (that.libraryBooks[i].id === page.id) {
return false;
}
}
return true;
}
In the view, it can then be referenced like this:
<h1>Books not checked out</h1>
<div ng-repeat="book in libraryBooks | filter:filter_notCheckedOut">
{{ book.title }}
</div>

AngularJS checkboxes checking from pivot table?

I created the following with the plugin: http://vitalets.github.io/checklist-model/
<section ng-repeat="owner in lord.owners">
<form ng-submit="foobar(owner)" name="update_location_form">
<input type="text" ng-model="owner.name">
<ul>
<li ng-repeat="sheep in sheeps">
<input checklist-model="owner.sheeps" checklist-value="sheep.id" type="checkbox">
<label class="checkbox">{{ sheep.name }}</label>
</li>
</ul>
<button type="submit">Submit</button>
</form>
</section>
All sheeps are shown in the list. And saving to my pivot table (manytomany-relation) also works.
But when I refresh the page, all checks are gone of course. How can I access them?
They're stored in:
{
id: 1,
name: "Obama",
farms: [
{
id: 10,
name: "VirtualFarm",
sheeps: [
{
id: 1,
name: "Foo",
},
{
id: 2,
name: "Bar",
},
{
id: 10,
name: "Cow",
},
{
id: 13,
name: "Hey",
},
]
}
]
}
But I really have now clue how I can check the checkboxes by default that are in the Pivot table.
Someone?
The checklist-model directive automatically checks the appropriate checkboxes based on the value of the checklist-model. You don't have to do anything else, your code looks fine. But ...
First of all, I suspect that owner in lord.owners must be something like owner in x.farms where x is the object that you pasted above.
And this is on the client side and even though you check this boxes, you still need to save them on the server-side. On a refresh, every data not persisted on the server side is lost.

Resources