Axios ReactJs Laravel: How to retrieve the multiple request Api - javascript

I create new laravel project and I combine the React Javascript Framework.
I watch youtube and read some website, how to use the Axios HTTP Requests API.
however they not discuss how to use the axios in retrieving the multiple request api.
Question:
How to retrieve multiple request api and also I want to display it to my render function
I create two public function (Mission,Store) in my Home Controller.
public function mission() {
$content_mission = DB::table('content_structure')
->where('content_pages','=','Home')
->where('content_section','=','Mission-Vision')
->where('status','=','Active')
->orderBy('content_id','Desc')
->limit(1)
->get();
return response()->json($content_mission);
}
public function store() {
$content_store = DB::table('content_structure')
->leftJoin('content_upload_assets','content_structure.content_id','=','content_upload_assets.cid')
->where('content_pages','=','Home')
->where('content_section','=','Store')
->where('status','=','Active')
->orderBy('content_id','Desc')
->limit(1)
->get();
return response()->json($content_store);
}
I also create Index.js to my component folder.
constructor() {
super();
this.state = {
}
}
componentWillMount() {
this.setState({
missionsApiRes: []
});
Promise.all([
axios.get('/api/mission'),
axios.get('/api/store')
]).then(response => {
const [storesApiRes, missionsApiRes] = response;
this.setState({storesApiRes,missionsApiRes}, () => {
});
})
}
renderMission() {
return this.state.missionsApiRes.map(mission =>
<div>{mission.content}</div>
)
}
My Render Function :
<div className="container">
{this.renderMission()}
</div>
Error:
Uncaught TypeError: this.state.missionsApiRes.map is not a function

From quick glance I would say that your this.state.missionsApiRes is not defined at the time of calling render. Try to move it to the constructor:
constructor() {
super();
this.state = {
missionsApiRes: []
}
}

You need custom resolve data of axios promise
Promise.all([
axios.get('/api/mission'), => axios.get('/api/mission').then(data => data)

Related

react-query within a class component where we have send/receive functions

I want to use React Query with a specific code structure, and I'd like to know if this is possible for me.
I have a React class component where sending the request and receiving the response are done in separate places
example:
class Engine{
sendMessage(from, message){
...
// for each subscriber
subscriber.receive(from, message)
}
listenFor(subscriber, chanel){
...
}
}
class ComponentA extends React.Component{
messageId;
constructor(props){
...
Engine.listenFor(this, 'APIResponse');
this.state = { data: {} }
}
componentDidMount(){
fetchData();
}
fetchData(){
let msg = createAPIRequestMsg({...});
this.messageId = msg.id;
Engine.send(msg);
}
recieve(from, message){
if(message.id == this.messageId){
let respones = message.response;
this.setState({ data: response });
}
}
}
Now I want someway to use react-query within this structure, I know we can use it within a class in different ways, but my question is how to use it when the function that is supposed to return a response is not returning the response, for example fetchData will not return the response, it will just send a message, and the receive function is the one responsible to get the response back, so in this structure we can't use the fetchData with useQuery, is there a workaround that?

Cannot put subscribed observable into collection as a string

I am kind of new to typescript and javascript, and I am having a real hard time figuring out how collections and file io works. I am trying to get data from a json file, which that I am successful in although when I put it into a collection the collection does not have the data.
Here is the code:
In my service class:
private configuration = "../../assets/Configurations/testConfiguration.json";
constructor(private http: HttpClient) {}
getBlogs(blog: string): Observable<IBlogPost[]> {
return this.http.get<IBlogPost[]>(blog);
}
getConfigurations() {
var configurationsData = [];
this.http.get(this.configuration).subscribe(data => {
configurationsData.push(data);
console.log(data);
// This will work and will print all the paths
});
//This will not work and will not print them, like if the collection is empty
configurationsData.forEach(x => console.log(x));
return configurationsData;
}
Where I get my service class injected:
blogs: IBlogPost[] = [];
private blogsPaths: string[] = [];
errorMessage = "";
constructor(private appUtilityService: AppUtilityServices) {}
ngOnInit() {
//This will not work, blogsPaths will not received the full collection as it should
this.blogsPaths = this.appUtilityService.getConfigurations();
this.blogsPaths.forEach(blogPath =>
this.appUtilityService.getBlogs(blogPath).subscribe(
b =>
b.forEach(blog => {
this.blogs.push(blog);
}),
error => (this.errorMessage = <any>error)
)
);
}
testConfiguration.json:
[
"../../assets/BlogPosts/testBlog1.json",
"../../assets/BlogPosts/testBlog2.json"
]
Bonus if you include a good tutorial on how collections work in javascript and how to return them properly
Turns out to be this, yes really...
Here I made it nice:
OnInit:
this.appUtilityService.getConfigurations().subscribe(
b =>
b.forEach(x => {
this.appUtilityService.getBlogs(x).subscribe(
b =>
b.forEach(blog => {
this.blogs.push(blog);
}),
error => (this.errorMessage = <any>error)
);
}),
error => (this.errorMessage = <any>error)
);
AppUtilityService:
getBlogs(blog: string): Observable<IBlogPost[]> {
return this.http.get<IBlogPost[]>(blog);
}
getConfigurations(): Observable<string[]> {
return this.http.get<string[]>(this.configuration);
}
Make it really nice:
ngOnInit() {
this.initializeBlog();
}
private initializeBlog(): void {
this.appUtilityService.getConfigurations().subscribe(
b =>
b.forEach(x => {
this.appUtilityService.getBlogs(x).subscribe(
b =>
b.forEach(blog => {
this.blogs.push(blog);
}),
error => (this.errorMessage = <any>error)
);
}),
error => (this.errorMessage = <any>error)
);
}
Inside getConfigurations, you're invoking an asynchronous http request. So it's normal, that configurationsData is empty outside the subscribe method.
Here, I'm using the powerful of RxJS to simplify the code and avoid to use
Array.push and nested subscribe().
1) Basic async service method to get data
So getConfigurations should return an Observable:
getConfigurations(): Observable<string[]> {
return this.http.get<string[]>(this.configuration);
}
And also getBlogs should return an Observable:
getBlogs(blogsPath: string): Observable<BlogPost[]> {
return this.http.get<BlogPost[]>(blogsPath);
}
Here, to respect Angular best practice, I remove I at the beginning of each interface, so BlogPost.
2) Here is the magic of RxJS
Then, you have another method inside your service to retrieve an Observable of blog posts, which should look like this:
getBlogPosts(): Observable<BlogPost[]> {
return this.getConfigurations().pipe(
mergeAll(),
concatMap(blogsPath => this.getBlogs(blogsPath)),
concatAll(),
toArray()
)
}
Which means, retrieve all collection of blog paths, for each array of paths, for each path, get blog posts, and then return an unique array of all posts.
Finally, inside your component, you call getBlogPosts with a subscription:
ngOnInit() {
this.service.getBlogPosts().subscribe(blogs => {
...do something with your blogs array.
});
}
or even better with async pipe inside your template :
.ts file:
blogs$: Observable<BlogPost[]>;
...
this.blogs$ = this.appService.getBlogPosts();
.html file:
<ul *ngIf="blogs$ | async as blogs">
<li *ngFor="let blog of blogs">
#{{ blog.id }} - {{ blog.title }}
</li>
</ul>
Check my full demonstration of this RxJS alternative on Stackbliz.
Recommended read on medium from Tomas Trajan

Delete properties from object that are not in a class

I'm using adonisjs, and am trying to sanitize a post request on the server. The data I get back has extra properties that are not mapped to the table/model so it is erroring when I try to save. Here is the update method
async update ({ params, request, response }) {
const contract = await Contract.find(params.id);
contract.merge(request.post());
return await contract.save();
}
The problem is that when I returned the contract earlier on a get request, I added some computed properties. I could do something along the lines of
const { prop1, prop2 } = request.post();
but that doesn't feel like a future proof or clean solution. Ideally I want the object to only have the properties defined on the table/model. I have a validator setup as described in the validator docs, but it still lets other properties bypass it.
I resolved this by adding a beforeSave hook in the model class that filter's properties on the object, which allows us to keep a thin controller.
const FIELDS = ['id', 'description'];
class Contract extends Model {
static boot() {
super.boot();
this.addHook('beforeSave', async contractInstance => {
Object.keys(contractInstance.$attributes).forEach(key => {
if (!CONTRACT_FIELDS.includes(key)) {
delete contractInstance.$attribute[key];
}
});
});
}
}
What about an helper class?
class RequestContractExtractor{
static desiredProps = [
"prop1",
"prop2",
]; // you could replace this with a list you get from your model class
constructor(requestData){
desiredProps.forEach(prop => this[prop] = requestData[prop]);
}
static from(...args){ return new this(...args); }
}
Then you'd be able to do:
async update ({ params, request, response }) {
const contract = await Contract.find(params.id);
contract.merge(RequestContractExtractor.from(request.post()));
return await contract.save();
}
I resolved this by adding a beforeSave hook that filter's properties on the object.
Create a file at /app/Model/Hooks/ContractHook.js
'use strict'
const ContractHook = module.exports = {}
const CONTRACT_FIELDS = ["id", "description"];
ContractHook.removeDynamicFields = async (contractInstance) => {
if (contractInstance) {
Object.keys(contractInstance.$attributes).forEach((key) => {
if (!CONTRACT_FIELDS.includes(key) && contractInstance.$attributes[key]) {
delete contractInstance.$attributes[key];
}
});
}
};
Use it in your model like so...
class Contract extends Model {
static boot() {
super.boot();
this.addHook("beforeCreate", [
"ContractHook.removeDynamicFields",
]);
}

angular load local file then make http calls to return observables

I am new to Angular, JS, and observables. I have a typescript class called DataService. I want it to load a list of URLs from a JSON formatted local file, and then have some way to call those URLs (to a handful of REST APIs) and return observables. The problem I am having is my code is not waiting for the config file to be loaded before the REST API functions get called.
I thought I could have the DataService constructor load the configuration file, and then have unique functions for each REST API call, but that isn't working
my code:
export class DataService {
configFile
constructor(private http: HttpClient) {
this.http.get('/assets/restApiUrlListConfig.json').subscribe(config => {
this.configFile = config;
});
}
getUrlFromConfigFile(name: string): string {
...
this returns the URL from the config file
...
}
getUrlAData(): Observable {
return this.http.get( getUrlFromConfigFile('A') )
}
}
My other components have code like this:
export class SomeComponent implements OnInit {
someComponentAData
constructor(private data: DataService) { }
ngOnInit() {
this.data.getUrlAData().subscribe(
data => {
this.someComponentAData = data
}
)
}
I am getting an error that the observable returned from the dataservice is undefined. Which I believe is because the constructor hasn't finished loading the config file, which I think is why the function getUrlAData isn't returning anything.
I feel like I'm not correctly handling these async calls, but I'm at a loss for how to tell my code to :
create the data service object
load the data file before anything else can be done
allow the other functions to be called asyncronously AFTER the config file is loaded
Angular CLI: 6.2.3
Node: 8.12.0
OS: win32 x64
Angular: 6.1.8
Edit 1: attempting to implement suggested solution
My DataService
configFile
configObservable: Observable<any>;
someSubscribeObj
constructor(private http: HttpClient) {
this.someSubscribeObj = this.http.get('/assets/restApiUrlListConfig.json').subscribe(config => {
this.someSubscribeObj = undefined;
this.configFile = config;
});
}
getObsFromConfigFile(name: string): Observable<any> {
//...
if (this.configFile != undefined) {
console.log('this.restApiUrlListConfig[name]',this.configFile[name])
return of(this.configFile[name])
}
else
return of(this.someSubscribeObj.pipe(map(c => c[name])))
//this.configObservable
//...
}
getUrlAData(): Observable<any> {
return this.getObsFromConfigFile('A').pipe(mergeMap(url => this.http.get(url)))
}
My other component:
constructor( private data: DataService ) { }
ngOnInit() {
//this.data.loggedIn.pipe((p) => p);
this.data.getUrlAData().subscribe(
data => {
this.urlAData = data
}
)
}
I was unable to store the "subscribe" into the observable, so I created a generic Any type varable, but at runtime I get a problem with the pipe command:
TypeError: this.someSubscribeObj.pipe is not a function
at DataService.push../src/app/services/data.service.ts.DataService.getObsFromConfigFile
(data.service.ts:67)
at DataService.push../src/app/services/data.service.ts.DataService.getUrlAData
(data.service.ts:74)
Edit 2: the unfortunate workaround
I am currently using two nested subscriptions to get the job done basically
http.get(config_file_url).subscribe(
config => {
http.get( config['A'] ).subscribe( adata => { do things };
http.get config['B'].subscribe( bdata => {do things };
}
)
I feel like I should be able to use a mergeMap of some sort, but I couldn't get them to work as I thought they would.
You need to wait on that async call, I would use a flatmap to get the value out of an observable.
export class DataService {
configFile
configObservable: Observable<any>;
constructor(private http: HttpClient) {
this.configObservable = this.http.get('/assets/restApiUrlListConfig.json').pipe(
map(config => {
this.configObservable = undefined;
this.configFile = config;
return configFile;
})
);
}
getUrlFromConfigFile(name: string): Observable<string> {
...
return of(configFile[name]) if configFile is set else return configObservable.pipe(map(c => c[name]));
...
}
getUrlAData(): Observable<string> {
return this.getUrlFromConfigFile('A').pipe(map(url => this.http.get(url)))
}
}
Basically you want to store the observable and keep using it till it completes, after it completes you can just wrap the config in an observable. The reason for wrapping it is to make the interface consistent, otherwise you have to have an if before every get.

Modifying Angular Contentful API response

I have a contentful service like so..
import { Injectable } from '#angular/core';
import { createClient, Entry } from 'contentful';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
const CONFIG = {
space: '<spaceid>',
accessToken: '<accesstoken>',
contentTypeIds: {
programItems: 'programItem'
}
};
#Injectable()
export class ContentfulService {
private cdaClient = createClient({
space: CONFIG.space,
accessToken: CONFIG.accessToken
});
public weekNumber = new BehaviorSubject<any>(1);
constructor() { }
// Get all the program items
getProgramItems(query?: object): Promise<Entry<any>[]> {
return this.cdaClient.getEntries(Object.assign({
content_type: CONFIG.contentTypeIds.programItems
}, query))
.then(res => res.items);
}
}
but I only want to bring in the programItems sys.ids in the contentful documentation.. you can modify api calls and return only certain values like this modify api calls
https://cdn.contentful.com/spaces/<space_id>/entries/
?select=fields.productName,fields.price
&content_type=<content_type_id>
but Im not sure how I would implement the same thing, the way they do angular calls.. I could just do a http request but I would prefer to keep it the same way as I have done above
any help would be appreciated
You add a select property to your getEntries call.
// Get all the program items
getProgramItems(query?: object): Promise<Entry<any>[]> {
return this.cdaClient.getEntries(Object.assign({
content_type: CONFIG.contentTypeIds.programItems,
select: 'sys.id'
}, query))
.then(res => res.items);
}
You can read the full documentation, including javascript snippets, here: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/select-operator/query-entries/console/js

Categories