Access an API from Angular - node.js

I'm trying to use an API for the first time. Here is how I currently call it :
url = 'https://data.economie.gouv.fr/api/records/1.0/search/?dataset=prix-carburants-fichier-instantane-test-ods-copie&q=aire+sur+l%27adour&lang=fr&facet=id&facet=adresse&facet=ville&facet=prix_maj&facet=prix_nom&facet=com_arm_name&facet=epci_name&facet=dep_name&facet=reg_name&facet=services_service&facet=horaires_automate_24_24&refine.ville=Aire-sur-l%27Adour';
datas = [];
constructor(private http: HttpClient){
this.http.get(this.url).toPromise().then((data: any) => {
this.datas = data
})
}
And HTML :
<pre class="text-white">
{{ datas | json }}
</pre>
The result shows a JSON like this :
Now, how do I access it? I already tried things like :
let data of datas :
data.records
data[0][records]
etc

Here an example, where json property rappresents your entire object:
<span *ngFor="let element of json.records">
{{element.datasetid }}
{{element.geometry }}
{{element.recordid }}
</span>

Here, Please try to create a new method and use async/await.
Create a new method something like this -
public async getData()
{
await this.http.get(this.URL)
.toPromise()
.then(response => this.datas = response['records'])
.catch(err => { console.log ('error');
});
}
Now, You can call this method from your constructor something like this -
constructor(private http: HttpClient) {
this.getData()
}
Now, You should use *ngFor directive to iterate over the datas as it is an array so you can use this data to develop HTML.
<div *ngFor="let data of datas">
{{data?.fields?.id}}
</div>
In this way, you can use this .
let me know if you need any further help on this .
please find the working link of stackblitz- https://stackblitz.com/edit/angular-dukmlc?file=src/app/app.component.ts
Thank you.

Related

Angular suscribed observable don't showing information on the view

image of the detail view with console to see the console.log()
I'm having troubles making the Tour Of Heroes Angular tutorial work, i'm in the 6 step of the tutorial, getting the data from a server but instead of getting the data from a simulated data server i have a api with nodejs express and mysql.
The problem cames when i try to show the detail of the hero (fetching one by id), all seems to work but the information don't show on the view.
template:
<div *ngIf="hero">
<h2>{{ hero.name }} Details</h2>
<div>id: {{hero.id}}</div>
<div>
<label for="name">Hero name: </label>
<input id="name" [(ngModel)]="hero.name" placeholder="name">
</div>
<button type="button" (click)="goBack()">go back</button>
</div>
component:
ngOnInit(): void {
this.getHero();
}
getHero(){
const id = Number(this.route.snapshot.paramMap.get("id"));
this.heroService.getHero(id).subscribe(hero => {
this.hero = hero;
console.log("hero", hero)
})
}
service:
private heroesUrl = 'http://localhost:3300/api/';
constructor(private MessageService: MessageService, private http: HttpClient) {
}
private log(message: string) {
this.MessageService.add(`HeroService: ${message}`);
}
getHeroes(): Observable<Hero[]>{
this.log('HeroService: fetched heroes');
return this.http.get<Hero[]>(this.heroesUrl);
}
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}${id}`;
return this.http.get<Hero>(url);
}
I don't know what's the problem, im learning angular but the observable is well suscribed, in the attached image you can see in the console that at least the api is working.
you received an array with an unique element, see the [``] in console. So
Or in subscribe your write hero[0]
this.heroService.getHero(id).subscribe(hero => {
this.hero = hero[0];
})
Or in your service return the first element of the array. For this use rxjs/operator map
getHero(id: number): Observable<Hero> {
const url = `${this.heroesUrl}${id}`;
return this.http.get<Hero[]>(url).pipe(
map((res:Hero[])=>res[0])
);
}
See that although you say to Angular that getHero return an Observable<Hero> really you got an Observable<Hero[]>. Yes, when we indicate the return of a function this not make "magically" we get the result, only help us to write the code and the editor advise us about it

how can injection dynamic html element to page with next.js?

how can dynamic injection html element to page with next.js? that these elements Unknown type like(input, checkbox, img,...). this element specified with api that return json type like this:
[{
"id":"rooms",
"title":"Rooms",
"order":1,
"type":"string",
"widget":"select",
"data":[{
"Id":18,
"ParentId":null,
"Title":"One",
"Level":null,
"Childrens":[]
},
{"Id":19,
"ParentId":null,
"Title":"Two",
"Level":null,
"Childrens":[]
},
{"Id":20,
"ParentId":null,
"Title":"Three",
"Level":null,
"Childrens":[]
}]
},
{
"id":"exchange",
"title":"Exchange",
"order":0,
"type":"boolean",
"widget":"checkbox",
"data":[]
}]
my try is:
Index.getInitialProps = async function({req, query}) {
const res= await fetch('url api')
var elements= await res.json()
var test = () => (
<div>
{...... convert json to html elements.......}
</div>
)
return {
test
}
})
function Index(props) {
return(
<a>
{props.test}
</a>
)
}
result is null, mean nothing for presentation.
the question is, Do I do the right thing? Is there a better way?
What happens is that during the transfer of props from server to client in getInitialprops, JSON is serialized and so functions are not really serialized. See https://github.com/zeit/next.js/issues/3536
Your best bet is to convert the test data into a string of HTML data and inject it using dangerouslySetInnerHTML. An example will be:
class TestComponent extends React.Component {
static async getInitialProps() {
const text = '<div class="homepsage">This is the homepage data</div>';
return { text };
}
render() {
return (
<div>
<div className="text-container" dangerouslySetInnerHTML={{ __html: this.props.text }} />
<h1>Hello world</div>
</div>
);
}
}
The catch with this is that the string you return must be a valid HTML (not JSX). So notice I used class instead of className
You can read more about it here: https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml

Display single item with Vue.js

I have a list of items where the title is a link to display a detailed view of the item. Click the title and it correctly goes to url + Id. In the Vue tolls the detail page retrieves the item with matching ID but as and array not an object and the template does not display any properties - what am I missing?
<script>
import axios from "axios";
export default {
name: "Report",
data() {
return {
report: {}
};
},
mounted: function() {
this.getReport();
},
methods: {
getReport() {
let uri = "http://localhost:5000/api/reports/" + this.$route.params.id;
axios.get(uri).then(response => {
this.report = response.data;
});
}
}
};
</script>
The template is so
<template>
<v-content>
<h1>report detail page</h1>
<p>content will go here</p>-
<h3>{{ report.month }}</h3>
<pre>{{ report._id }}</pre>
</v-content>
</template>
any comments appreciated
url + Id
It sounds like your issue is that you are receiving an array not an object.
You can pull out objects encapsulated inside arrays easily.
For example, if we had the following data:
var bus1 = {passengers:10, shift:1}
var busArr = [bus1]
which we can assert: busArr === [{passengers:10, shift:1}]
We could then pull out bus1 by referencing the index 0:
var bus1New = busArr[0]
If you want to avoid the data transformation and just output the structure you can consider a v-for in your template.
<p v-for="val in report">
_id: {{val._id}}
<br>
month: {{val.month}}
</p>

search engine laravel and vue.js without scout

Hi am trying to make search engine with laravel and vue.js but i have no result:
this is my SearchController.php
namespace Amp\Http\Controllers;
use Amp\User;
use Illuminate\Http\Request;
class SearchController extends Controller
{
/**
* #param Request $request
* #return array
*/
public function search(Request $request)
{
$error = ['error' => 'No results found, please try with different keywords.'];
if ($request->has('q')) {
$users = User::search($request->get('q'))->get();
return $users->count() ? $users : $error;
}
return $error;
}
}
this my TopNavbar.vue:
<template>
<div>
<input type="text" v-model="keywords">
<ul v-if="results.length > 0">
<li v-for="result in results" :key="result.id" v-text="result.name"></li>
</ul>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
keywords: null,
results: []
};
},
watch: {
keywords(after, before) {
this.fetch();
}
},
methods: {
fetch() {
axios.get('api/search', { params: { keywords: this.keywords } })
.then(response => this.results = response.data)
.catch(error => {});
}
}
}
</script>
If i use only the api url then i have result and work proprely i mean if i make search with url on the browser something like this: api/search?q=XXXX then work pefect but only on browser wen i try to make search on then nothing
thank you for your help
To get the keywords sent from axios inside the controller, you would need to use
$keywords = $request->get('keywords');
In the code shared, you are looking for a request parameter named q. When you are entering the URL through the browser, you are entering the parameter with the name q. So the search works. I hope you are clear about the issue now.
So, assuming that you are handling the search method with eloquent, the controller action becomes:
public function search(Request $request)
{
$error = ['error' => 'No results found, please try with different keywords.'];
$keywords = $request->get('keywords')?? null;
if ($keywords) {
$users = User::search($keywords)->get();
return $users->count() ? $users : $error;
}
return $error;
}
For send Request as ajax you must use X-CSRF-Token or disable (exception) validate this token for this url.
For API url validate token disabled.
Read more:
https://laravel.com/docs/5.6/csrf

Using Fragment to insert HTML rendered on the back end via dangerouslySetInnerHTML

I used to compile and insert JSX components via
<div key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
which wrapped my HTML into a <div>:
<div>my html from the HTML object</div>
Now react > 16.2.0 has support for Fragments and I wonder if I can use that somehow to avoid wrapping my HTML in a <div> each time I get data from the back end.
Running
<Fragment key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
will throw a warning
Warning: Invalid prop `dangerouslySetInnerHTML` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.
in React.Fragment
Is this supported yet at all? Is there another way to solve this?
Update
Created an issue in the react repo for it if you want to upvote it.
Short Answer
Not possible:
key is the only attribute that can be passed to Fragment. In the
future, we may add support for additional attributes, such as event
handlers.
https://reactjs.org/docs/fragments.html
You may want to chime in and suggest this as a future addition.
https://github.com/facebook/react/issues
In the Meantime
You may want to consider using an HTML parsing library like:
https://github.com/remarkablemark/html-react-parser
Check out this example to see how it will accomplish your goal:
http://remarkablemark.org/blog/2016/10/07/dangerously-set-innerhtml-alternative/
In Short
You'll be able to do this:
<>
{require('html-react-parser')(
'<em>foo</em>'
)}
</>
Update December 2020
This issue (also mentioned by OP) was closed on Oct 2, 2019. - However, stemming from the original issue, it seems a RawHTML component has entered the RFC process but has not reached production, and has no set timeline for when a working solution may be available.
That being said, I would now like to allude to a solution I currently use to get around this issue.
In my case, dangerouslySetInnerHTML was utilized to render plain HTML for a user to download; it was not ideal to have additional wrapper tags included in the output.
After reading around the web and StackOverflow, it seemed most solutions mentioned using an external library like html-react-parser.
For this use-case, html-react-parser would not suffice because it converts HTML strings to React element(s). Meaning, it would strip all HTML that wasn't standard JSX.
Solution:
The code below is the no library solution I opted to use:
//HTML that will be set using dangerouslySetInnerHTML
const html = `<div>This is a div</div>`
The wrapper div within the RawHtml component is purposely named "unwanteddiv".
//Component that will return our dangerouslySetInnerHTML
//Note that we are using "unwanteddiv" as a wrapper
const RawHtml = () => {
return (
<unwanteddiv key={[]}
dangerouslySetInnerHTML={{
__html: html,
}}
/>
);
};
For the purpose of this example, we will use renderToStaticMarkup.
const staticHtml = ReactDomServer.renderToStaticMarkup(
<RawHtml/>
);
The ParseStaticHtml function is where the magic happens, here you will see why we named the wrapper div "unwanteddiv".
//The ParseStaticHtml function will check the staticHtml
//If the staticHtml type is 'string'
//We will remove "<unwanteddiv/>" leaving us with only the desired output
const ParseStaticHtml = (html) => {
if (typeof html === 'string') {
return html.replace(/<unwanteddiv>/g, '').replace(/<\/unwanteddiv>/g, '');
} else {
return html;
}
};
Now, if we pass the staticHtml through the ParseStaticHtml function you will see the desired output without the additional wrapper div:
console.log(ParseStaticHtml(staticHtml));
Additionally, I have created a codesandbox example that shows this in action.
Notice, the console log will throw a warning: "The tag <unwanteddiv> is unrecognized in this browser..." - However, this is fine because we intentionally gave it a unique name so we can easily differentiate and target the wrapper with our replace method and essentially remove it before output.
Besides, receiving a mild scolding from a code linter is not as bad as adding more dependencies for something that should be more simply implemented.
i found a workaround
by using react's ref
import React, { FC, useEffect, useRef } from 'react'
interface RawHtmlProps {
html: string
}
const RawHtml: FC<RawHtmlProps> = ({ html }) => {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!ref.current) return
// make a js fragment element
const fragment = document.createDocumentFragment()
// move every child from our div to new fragment
while (ref.current.childNodes[0]) {
fragment.appendChild(ref.current.childNodes[0])
}
// and after all replace the div with fragment
ref.current.replaceWith(fragment)
}, [ref])
return <div ref={ref} dangerouslySetInnerHTML={{ __html: html }}></div>
}
export { RawHtml }
Here's a solution that works for <td> elements only:
type DangerousHtml = {__html:string}
function isHtml(x: any): x is DangerousHtml {
if(!x) return false;
if(typeof x !== 'object') return false;
const keys = Object.keys(x)
if(keys.length !== 1) return false;
return keys[0] === '__html'
}
const DangerousTD = forwardRef<HTMLTableCellElement,Override<React.ComponentPropsWithoutRef<'td'>,{children: ReactNode|DangerousHtml}>>(({children,...props}, ref) => {
if(isHtml(children)) {
return <td dangerouslySetInnerHTML={children} {...props} ref={ref}/>
}
return <td {...props} ref={ref}>{children}</td>
})
With a bit of work you can make this more generic, but that should give the general idea.
Usage:
<DangerousTD>{{__html: "<span>foo</span>"}}</DangerousTD>

Resources