{
title: "My Title",
entryID: 1,
url: "#",
author: "William Pears, Andrew Cutcher",
comment_count: 2
}
I got this object and I am trying to figure out how to display this in my React component.
I want to display like a blog entry where when clicking on title I could hopen the link (url) and show author also on the blog entry..
Please help!!
Map overs the keys of object
{Object.keys(yourObject).map(function(key) { return <div>Key: {key}, Value: {yourObject[key]}</div>; })}
Two things here:
where will you store the data?
is it in state?
then use this.state.{propertyName}
#dotnetdev4president explained how to access this.state.property, except, you should wrap the reference in curly braces
is it in a constant?
then check my example below with JavaScript’s .map method
for rendering/displaying the data to React Components
you might want to check this for blog-like entries: https://reactstrap.github.io/components/card/
Loop through your object, with the above mentioned .map like so:
const yourExampleDataConstant = {
title: "My Title",
entryID: 1,
url: "#",
author: "William Pears, Andrew Cutcher",
comment_count: 2
};
return <div>
{yourExampleDataConstant.map(element => (
return <ul key={element.entryID} >
<a href={element.url}><li>{element.title}</li></a>
<li>{element.author}</li>
<li>{element.comment_count}</li>
</ul>
))}
</div>;
}
However, context of your code would be useful!
Hope this helps!
PS.
Looks while I was writing an answer, #nikhilkarkra already replied with what I had in mind :)
If you saved your data into the state with this.setState({your_object}) you can output easyly in jsx:
<p>this.state.title</p>
<p>this.state.author</p>
And so on...
Related
I have stucked with an issue using refinement list widget of algolia.
First of all my resulting data structure is like that:
[
{
objectID: 999,
title: 'some title',
categories: [
{
id: 444,
name: 'some name',
},
{...},
]
}
]
I have that type of structure on my page:
<ais-instant-search
:search-client="searchClient"
:initial-ui-state="{
index_Name: { query: searchedValue },
}"
index-name="index_Name"
>
<ais-index index-name="index_Name">
<ais-configure
:filters="facetsFilters"
:facets="['categories.id']"
:hits-per-page.camel="items"
/>
<ais-refinement-list attribute="categories.id" />
<div> ...Some other widgets nested in divs as ais-search-box, ais-sort-by, etc </div>
</ais-index>
</ais-instant-search>
Within ais-configure I have passed to filters a facetsFilters variable which contains string with such content:
"categories.id:1 OR categories.id:5"
and it works ok, I'm getting results within selected categories,
problems starts, when i try to get refinement-list here:
<ais-refinement-list attribute="categories.id" />
I have an empty list despite that on dashboard this attribute is added as an attributesForFacetings and in ais-configure filters parameters with categories.id in it also works well.
Any suggestions is highly appreciated !
Problem was in Dashboard of Algolia.
When we clicked on 'searchable' instead of 'filter only' radiobutton for chosen attributeForFaceting - everything starts working good.
I am accessing an online API and want to use the text value to populate a ngb-typeahead dropdown. There is a working example on the Angular Bootstrap website using Wikipedia, but the returned data from the Wikipedia API is different to the data I am getting from a geocoding API. The data I get is returned in this format:
{
"suggestions": [
{
"text": "23 Queen Charlotte Drive, Aotea, Porirua, Wellington, 5024, NZL",
"magicKey": "dHA9MCNsb2M9NDMwNzcyNzQjbG5nPTMzI2huPTIzI2xicz0xMDk6NDg1NDQwMzU=",
"isCollection": false
},
{
"text": "23 Queen Mary Avenue, Epsom, Auckland, 1023, NZL",
"magicKey": "dHA9MCNsb2M9NDMwNDY4MjUjbG5nPTMzI2ZhPTE0NDE3OTIjaG49MjMjbGJzPTEwOTo0ODU0NDMyNA==",
"isCollection": false
},
I have been trying to access text in response data with the following:
return this.http
.get<any>(GIS_URL, {params: GIS_PARAMS.set('text', term)}).pipe(
map(response => response.suggestions)
);
I have also read the Angular tutorial here on dealing with response data, but the difference in the example is that they are getting an array of Hero's whereas I am getting an object containing an array of suggestions.
The typeahead looks like:
HTML
<fieldset class="form-inline">
<div class="form-group">
<label for="typeahead-http">Search for a wiki page:</label>
<input id="typeahead-http" type="text" class="form-control mx-sm-3" [class.is-invalid]="searchFailed" [(ngModel)]="model" [ngbTypeahead]="search" placeholder="Wikipedia search" />
<small *ngIf="searching" class="form-text text-muted">searching...</small>
<div class="invalid-feedback" *ngIf="searchFailed">Sorry, suggestions could not be loaded.</div>
</div>
</fieldset>
<hr>
<pre>Model: {{ model | json }}</pre>
Full code on StackBlitz is here.
I am new to Angular, so a verbose answer would be great.
You need to specify resultFormatter and inputFormatter on the typeahead input (refer to Typeahead).
Explanation
Your search method in the service returns a list of suggestion Objects which each look like:
{
isCollection: ...
magicKey: ...
text: ...
}
However by default the typeahead control expects a list of strings, hence it displays your objects as [Object object].
You need to tell the typeahead control how to determine a string value from your object, you do this via resultFormatter and inputFormatter.
These inputs take a function, which has the object as an input and the string display value as its output.
formatter below is that function, it will be called for each item displayed in the list. If you expand it to a normal function you can put a breakpoint in it and see it being called in this manner.
Solution
<input id="typeahead-http" ... [inputFormatter]="formatter" [resultFormatter]="formatter"/>
TypeScript file:
formatter = (item:any) => item.text as string;
Updated StackBlitz
https://stackblitz.com/edit/so-typeahead?file=src%2Fapp%2Ftypeahead-http.ts
Follow-up questions
item in the formatter:
Consider:
formatter = (item:any) => item.text as string;
is shorthand for:
function format(item: any){
return item.text as string;
}
They typeahead control/directive iterates the items returned by search(..) and calls this method which each one. The results are displayed in the select list.
map(response => response.suggestions)
The response from the service is an object like:
{ // object
suggestions:
[
{ ..., text: 'Place 1' },
{ ..., text: 'Place 2' }
]
}
That is an object containing a list named suggestions. The typeahead expects a list only, so the map transforms the object containing list => list only.
Does the formatter that you have defined do both input and result?
Yes, as it is assigned to both [inputFormatter] and [resultFormatter] in the template.
Alternative answer
The mapping is done entirely in the service:
return this.http
.get<any>(GIS_URL, {params: GIS_PARAMS.set('text', term)}).pipe(
map(response => response.suggestions.map(suggestion => suggestion.text)),
);
Each response object is mapped to the list of suggestions. Each suggestion is mapped (using JavaScript map) to its text value.
You can use this solution provided you don't need access to any of the other suggestion properties outside of the service.
I am building a site with Gatsby and so far I find programmatically creating pages a very handy feature. You can have a category template and have gatsby-node create pages for each content category very easily.
However, what is the recommended way to add differentiated content to that same template?
Right now I'm using ternary operators to check which category it is and add specific content based on that precise category (a few examples of content that needs to be differentiated accordingly: category intro title, SEO title and SEO description inside the SEO component based on Helmet)
inside categoryTemplate.js
const booksTitle = "Quote on books"
const songsTitle = "Quote on songs"
const travelsTitle = "Quote on travels"
const booksSeoTitle = "Books Title for SEO"
...
<CategoryIntro
title={<>
{category === "books"
? booksTitle
: category === "songs"
? songsTitle
: category === "travels"
? travelsTitle
: null
}
</>}
/>
This approach actually works, but I wonder if there's a more convenient practice that I could use? Should I store information about categories elsewhere in a json format instead of having them declared in the template file?
Thanks a lot.
I think the approach you suggested about storing that information elsewhere would make the code cleaner and easier to maintain. The template component should only be a generic template as it is intended. You shouldn't mix it with content.
How about a categories.js file?
export const categories = {
book: {
title: "Quote on books",
seoTitle: "",
},
songs: {
title: "Quote on songs",
seoTitle: "",
},
}
Import your categories.js in your template file and let it decide which category to choose via a prop:
import { categories } from "./categories.js"
// This function returns your mediaType object
function getObject(obj, mediaType) {
return obj[mediaType];
}
function MediaTemplate({ mediaType }) {
const category = getObject(mediaType);
// See this answer for how to access the object key dynamically: https://stackoverflow.com/questions/4255472/javascript-object-access-variable-property-by-name-as-string
console.log("category:");
console.log(category);
// It is only one amazing line now!
return (
<CategoryIntro title={category.title} />
);
}
I may have gone about this completely the wrong way from the beginning so all advise is welcomed.
I am trying to create a basic page with inputs on the right and hints for the inputs on the left and when you focus on the inputs the appropriate hint is highlighted on the left.
There is a JSFiddle here: https://jsfiddle.net/eywraw8t/210693/
This will not work as I do not know how to find the appropriate hint to highlight (and set isHighlighted to false on all the other hints).
I managed to get a working example by adding a highlighted prop on the field object and not using a hint component. However in reality the fields data will come from the database so it won't have a highlighted parameter so a hint component seemed more sensible.
To put my question in simple terms: How can I find the relevant hint component when focused on an input?
JS Fiddle showing functionality without a component: https://jsfiddle.net/as2vxy79/
Broken JS Fiddle trying to use a component: https://jsfiddle.net/eywraw8t/210693/
Here is the JS outside JS Fiddle:
Vue.component('hint', {
template: `
<div class="hint" :class="{'highlight-hint': isHighlighted }">
<slot></slot>
</div>
`,
data() {
return {
isHighlighted: false
}
}
});
new Vue({
el: "#app",
data: {
fields: [
{
'id': 'name',
'hint': 'Put the name here'
},
{
'id': 'description',
'hint': 'Put the description here'
},
{
'id': 'more',
'hint': 'Put more here'
}
]
},
methods: {
onFocus(focusedField) {
// Somehow loop through the hints
// I am aware there is no "hints" property this is an example
this.hints.forEach(function(field) {
field.isHighlighted = (focusedField == field.id)
})
}
}
})
Short answer: you don't need a direct reference to the components displaying the hint because you can solve this problem using reactivity.
Only thing you need to do is add a data field which stores the field id you want to highlight and then conditionally add a class to the hint you want to highlight based on the selected id (or render the components conditionally).
new Vue({
el: "#app",
data: {
highlightedFieldId: '',
fields: [
{
'id': 'name',
'hint': 'Put the name here' },
{
'id': 'description',
'hint': 'Put the description here' },
{
'id': 'more',
'hint': 'Put more here' }
]
},
methods: {
onFocus(focusedFieldId) {
this.highlightedFieldId = focusedFieldId;
}
}
})
Here's an update to your Fiddle.
NOTES:
If you really need direct references you can use the ref directive (this also works in a list of components generated by v-for).
You should probably think about using tooltips for the hints. E.g. using Element UI's Tooltip.
UPDATE:
So, here's a solution using the ref directive to obtain a reference to the highlighted hint component. You can see that I used the field id in :ref but you still get an array from this.$refs[focusedFieldId] since there is a surrounding v-for. Other than that, it's pretty simple.
I also updated the hint component to accept the is-highlighted prop so it can change its class on its own (you previously used a data property for this which does not result in a component update).
I’ve been experimenting with this GitHub repo via a course on Lynda.com (https://github.com/planetoftheweb/learnangular) by Ray Villalobos -- it functions similarly to a basic web app that I’m hoping to build, but I’ve recently hit a bit of a road block.
In that repo linked above, in app/component.app.ts, is the following array:
var ARTISTS: Artist[] = [
{
"name": "Barot Bellingham",
"shortname": "Barot_Bellingham",
"reknown": "Royal Academy of Painting and Sculpture",
"bio": "Some bio here."
},
// etc...
]
This array is filtered by a pipe as seen in app/pipe.search.ts:
export class SearchPipe implements PipeTransform {
transform(pipeData, pipeModifier) {
return pipeData.filter((eachItem) => {
return eachItem['name'].toLowerCase().includes(pipeModifier.toLowerCase()) ||
eachItem['reknown'].toLowerCase().includes(pipeModifier.toLowerCase());
});
}
}
Here's the filter input:
<input class="search-input" [(ngModel)]="field1Filter" placeholder="type in search term here" (click)="showArtist(item); field1Filter=''">
And the code for the filter results:
<ul class="artistlist cf" *ngIf="field1Filter">
<li class="artistlist-item cf"
(click)="showArtist(item);"
*ngFor="let item of (artists | search: field1Filter)">
<artist-item class="content" [artist]=item></artist-item>
</li>
</ul>
<artist-details *ngIf="currentArtist" [artist]="currentArtist"></artist-details>
This all works perfectly, however, in my project, I would need to include three nested arrays, and have the ability to filter based upon the values in those arrays. A sample of the kind of array I need will look something like this:
var ARTISTS: Artist[] = [
{
"name": "Barot Bellingham",
"shortname": "Barot_Bellingham",
"reknown": "Royal Academy of Painting and Sculpture",
"bio": "Some bio here...",
"friends": [
"James",
"Harry",
"Bob",
"Liz",
"Kate",
"Jesse"
],
"emails": [
"bb#this.com",
"aa#this.com"
],
"car": [
"honda",
"scion",
"aston martin"
]
},
// etc...
]
Therefore, I hope to filter by “Harry,” and only display objects that contain “harry” in either “name,” “reknown,” “friends,” "emails," or "cars." Is this possible, and if so, how can I edit the pipe filter to do this? Thank you!!
(I'm pretty green at angular and JS in general, so I want to apologize in advance if I’ve used incorrect terminology or overlooked/misunderstood something basic.)
I deleted my prior answer because it was more confusing than helpful. I pasted example code without applying it to your variables/properties/objects and it was misleading. Let's try again:
export class SearchPipe implements PipeTransform {
transform(pipeData, pipeModifier) {
pipeModifier = pipeModifier ? pipeModifier.toLowerCase() : null;
return pipeModifier ? pipeData.filter(eachItem => {
eachItem['name'].toLowerCase().indexOf(pipeModifier) !== -1 ||
eachItem['reknown'].toLowerCase().indexOf(pipeModifier !== -1) : pipeData;
});
}
}
The first line of code in the transform method ensures that the modifier passed in is also lowercase so that the compare always compares lower case values. It also has a null check to ensure it does not try to lowercase it if it is null.
The second line of code also uses the "?" syntax to handle the case of a null pipeModifier.
I changed includes to indexOf. Includes checks arrays. Are these items, such as eachItem['name'], an array?
That should be closer.
NOTE: Without a provided plunker ... I did not check the syntax or correct execution of this code.