WP API and JS : Cannot read property 'wp:featuredmedia' of undefined - wordpress

I write a component to display a list WP posts on a page build with nuxt.js and I just can not display the featured image.
The Vue Component
<template>
<div class="references__grid">
<div class="references__item" v-for="item in references">
<h3><nuxt-link :to="slugToUrl(item.slug)"><h2 class="is-title">{{ item.title }}</h2></nuxt-link></h3>
<div v-html="item.excerpt"></div>
<div>{{ item.image }}</div>
<strong class="more"><nuxt-link :to="slugToUrl(item.slug)">Lire la suite</nuxt-link></strong>
</div>
</div>
</template>
The request
getReferences() {
return new Promise((resolve, reject) => {
request.defaults.baseURL = this.baseUrl;
request.get(`posts?categories=46&per_page=6&_embedded`).then(response => {
const data = [...response.data];
if (response.status === 200 && response.data.length > 0) {
const filtered = {
total: response.headers["x-wp-total"],
totalPages: response.headers["x-wp-totalpages"],
data: data.map(item => ({
id: item.id,
title: item.title.rendered,
content: item.content.rendered,
excerpt: item.excerpt.rendered,
slug: item.slug,
image: item._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url
}))
};
resolve(filtered);
} else {
reject(response);
}
});
});
},
The WP Api seems ok: https://www.agencedebord.com/wp-json/wp/v2/posts?categories=46&per_page=61&_embed
The error message:
ERROR
TypeError: Cannot read property '0' of undefined
server-bundle.js:1525 filtered.data.data.map.item
server-bundle.js:1525:56
Array.map
server-bundle.js:1519 >__WEBPACK_IMPORTED_MODULE_1_axios___default.a.get.then .response
server-bundle.js:1519:24
next_tick.js:160 process._tickCallback
internal/process/next_tick.js:160:7
So why item._embedded is undefined?
There is no problem for item.id or item.slug... any clarification is appreciated.

Finally, I did not use "_embed" but I add a new endpoint following this answer : Get Image URL instead of Attachment Id in Rest API

I think that the request url is not correct, you should use _embed not _embedded
So it will be request.get(posts?categories=46&per_page=6&_embed)
Otherwise, the _embedded part will be missing in the json response.

The problem is sometimes item._embedded['wp:featuredmedia'] is returning undefined, you can confirm you get that by console.log(item._embedded['wp:featuredmedia']), the solution for that by wrapping it in if condition, if it not undefined to proceed if(item._embedded['wp:featuredmedia']){ return let imageUrl = item._embedded["wp:featuredmedia"][0].media_details.sizes.full.source_url
} else { return null}

I had one post that was giving this error. I had the if/else setup properly but it was still erroring. I went to the problem post and resaved the featured image and also changed to the ajax to load .full.source_url instead of .medium.source_url and that fixed the error.

I imported some photos from another wordpress. some of them had not featured images yet they somehow acting like they have. (has_post_thumbnail() was acting same way and returns true for that posts)
My solution is checking if they 'really' have thumbnail:
if(post.featured_media == 0 || post._embedded['wp:featuredmedia'][0].media_details == undefined){
imageSource = null;
}
else{
imageSource = post._embedded['wp:featuredmedia'][0].media_details.sizes.full.source_url;
}

Related

Value rendered by Controller not getting displayed

So I'm working with Symfony 4.4 and I'm facing an issue here.
I have the following script responsible for getting the value of an input tag and sending it back as json to the controller
{% if summoner is defined %}
let res = {{ summoner.summonerLevel }}
{% endif %}
$("#valid-summoner").click(function () {
let data = {'sumoner_name':$("#riot-summoner-input").val()}
let url = `/coach/`
let xhr = new XMLHttpRequest
xhr.open("GET", url)
xhr.send(JSON.stringify(data))
console.log(data)
function loadXMLDoc()
{
document.getElementById("display-summonerLevel").innerHTML = `Summoner Level: <h2>${res}</h2>`
}
loadXMLDoc();
});
I have a controller method responsible for rendering the index page that's receiving this json and extracting the content and using it to render the page again but with another option which is that value.
...
$data = json_decode($request->getContent());
if(isset($data->sumoner_name) && !empty($data->sumoner_name)){
// We return the code
return $this->render('coach/index.html.twig', [
'coaches' => $coaches, 'summoner'=> $this->showSummoner($data->sumoner_name),
'current_user'=>$currentUser, 'combined'=>$call->fetchCoachRating($coaches)
]);
}else{
return $this->render('coach/index.html.twig', [
'coaches' => $coaches,
'current_user'=>$currentUser, 'combined'=>$call->fetchCoachRating($coaches)
]);
}
I don't know if this is actually the way you're supposed to do it. But it certainly isn't working..
console.log(data) in the script give back the following output
(index):445 {sumoner_name: 'YàKûZa'}sumoner_name: "YàKûZa"[[Prototype]]: Object
I'm not sure what I can do here

Use setState to change the value of a grandchild object

Everything I have tried from what I can find doesn't seem to be working. I'm really curious how to access and edit grandchild objects located in the state with react. If anyone could tell me what I'm doing wrong, it would be very helpful.
https://codesandbox.io/s/0mo32q85pp
Take a look at the following code...
App.js
lines: 41-58
getHomeworld = URL => {
fetch(URL)
.then(res => {
return res.json();
})
.then(homeWorldObject => {
console.log(homeWorldObject);
// this.setState({ <- Why isn't this working????
// ...this.state.starwarsChars,
// ...this.state.nextPage,
// ...this.state.prevPage,
// ...this.state.starwarsChars.homeworld = homeWorldObject
// });
})
.catch(err => {
throw new Error(err);
});
};
lines: 86-89
<CharacterList
characters={this.state.starwarsChars}
getHomeworld={this.getHomeworld}
/>
CharacterList.js
lines: 8-12
<Character
key={character.name}
characterDetails={character}
getHomeworld={props.getHomeworld}
/>
Character.js
lines: 18-29
{Object.keys(props.characterDetails).includes("homeworld") ? (
<div className="character-homeworld">
<Homeworld
homeworld={props.getHomeworld(props.characterDetails.homeworld)}
/>
</div>
) : (
<div className="character-homeworld">
<h4>Homeworld</h4>
<p>None</p>
</div>
)}
Homeworld.js
lines: 7-10
<div className="homeworld-details">
<p>Name: {props.name}</p>
<p>Rotation Period: {props.rotation_period}</p>
</div>
Expected Output:
If you look on the sandbox webpage, the "Name" and "Rotation Period" (Under "Homeworld") should display the values from here: https://swapi.co/api/planets/1/
Is there anyone who can help me figure this out?
EDIT:
I got really close making these changes (using my local machine, the code on the sandbox is still the original)...
App.js
let temp = {...this.state.starwarsChars} // use spread operator to clone it, so you don't mutate state on next line;
for (let character in temp) {
if (temp[character].homeworld === URL) {
temp[character].homeworld = homeWorldObject;
}
}
// console.log(temp);
this.setState({
starwarsChars: temp
});
Character.js
const Character = props => {
props.getHomeworld(props.characterDetails.homeworld);
console.log(props.characterDetails); // returns object{homeworld: {object}}
console.log(props.characterDetails.homeworld); // returns url
and...
<div className="character-homeworld">
<Homeworld
homeworld={props.characterDetails.homeworld}/>
</div>
However, the issue now is if I do console.log(props.characterDetails.homeworld);, it logs homeworld: url
and...
if I do console.log(props.characterDetails);, it logs the property of the character object as homeworld: {object}
...
What I want is the 2nd one, and I'm not sure why it's not consistent.
Update
For some strange reason, codesandbox is console logging both urls, and when I run with yarn start, it logs the url for one, and the object for another. Because of this... I am adding the github link here -> https://github.com/jamespagedev/Sprint-Challenge-React-Wars (so the error can properly be reproduced)
Edit 2:
I changed the sandbox code to the following so we are now only worrying about the code in 1 file -> https://codesandbox.io/s/0mo32q85pp
Here is the issue I am now seeing, and I'm not sure how to solve it...
getHomeworld = URL => {
let home;
fetch(URL)
.then(res => res.json())
.then(homeWorldObject => {
home = homeWorldObject;
console.log(home); // home is an object
});
console.log(home); // why is home undefined?
return home;
};
I've tried doing return homeWorldObject, but the main function just returns undefined. After doing the console logging, that was what I found, and I'm not sure why that is happening...

VueJS rendering data from REST service

I've attempted to render data from a http request to a component which is working fine, the issue is that it's null while the data is being fetched. While the data is null the console is throwing a TypeError until all the data is loaded and committed to the Vuex store.
All is working how I'd suspect, I'm just trying to figure how I can prevent the errors being thrown and to wait until all the appropriate data is fetched. I've seen others using v-if to check if the data is null which will work. It just seems tedious and that there surly is a better way to achieve the same outcome, without an application riddled with v-if statements checking every single state.
I came across this solution but it's still not working how I thought it would, I'm still receiving the same console errors. Am I using these key words correctly and are they in the correct location? since nothing has changed with every variation I've tried.
Vuex Action:
const actions = {
getThread ({ commit }, payload) {
Vue.http
.get(`http://localhost:9000/threads/${payload.id}`)
.then(async response => {
commit(FETCH_THREAD, await response.data)
})
}
}
This is within my vue file calling upon the action:
created () {
this.$store.dispatch('getThread', {id: '59280ab5acbafb17af9da902'})
}
I assume you are trying to display something from your store in your template. The problem is, Vue cannot render something that does not exist yet. The solution is to check whether the data exists or not.
Let's take this component example:
<template>
<div>
{{ someObject.name }}
</div>
</template>
<script>
export default {
data () {
return {
someObject: null
}
},
methods: {
fetchTheObject () {
this.someObject = {
id: 1,
name: 'My object'
}
}
},
created () {
setTimeout( () => {
this.fetchTheObject()
}, 3000)
}
}
</script>
As you can see, you will get an error in your console because someObject.name does not exist until fetchTheObject() has been called.
The solution is to put some v-if attribute to control that:
<template>
<div>
<span v-if="someObject === null">Fetching the object</span>
<span v-else>{{ someObject.name }}</span>
</div>
</template>
In general, you would want to display some spinner to show the user that something is loading...
Hope this helps
EDIT: And forget about the async await in your code, you don't need that here

"This operation is insecure" when using class properties as key

I have a custom FileArgument class that I'm using to store information about an uploaded file:
export class FileArgument {
name: string;
path: string;
file: File;
}
My upload works fine and the server then returns the path where the file was uploaded. I then want to store this path in a dictionary using a previously set fileArgument.name as key. Below is a simplified overview of my component. onSubmit() is where the action is happening:
export class InputModuleComponent {
private vsmodule: InputModule;
private moduleArguments = {};
private fileArgument: FileArgument = new FileArgument();
#Input()
module: InputModule;
constructor(private inputModuleService: InputModuleService) {}
onSubmit(): void {
this.inputModuleService.postFile(this.fileArgument.file).subscribe(
response => {
this.moduleArguments[this.fileArgument.name] = response.filename;
console.log(this.moduleArguments);
},
error => {}
);
}
onFileChange(event): void {
this.fileArgument.file = event.originalTarget.files[0];
this.fileArgument.name = event.originalTarget.id;
}
}
Line 14 above (this.moduleArguments[this.fileArgument.name] = response.filename;) causes the following error in Firefox:
EXCEPTION: Uncaught (in promise): SecurityError: The operation is insecure.
and in Chrome:
core.umd.js:5995 EXCEPTION: Uncaught (in promise): InvalidStateError: Failed to set the 'value' property on 'HTMLInputElement': This input element accepts a filename, which may only be programmatically set to the empty string.
If I replace that line with, for example:
this.moduleArguments['hello'] = response.filename;
I don't get any errors. The error clearly comes from using fileArgument.name as a dict key, but I have no idea why.
EDIT: The postFile() method from my service is below:
postFile (file: File): Observable<any> {
console.log('input-module.service - postFile()');
var url = this.uploadURL;
return Observable.create(observer => {
var formData: FormData = new FormData()
var xhr: XMLHttpRequest = new XMLHttpRequest();
formData.append("upload", file, file.name);
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
observer.next(JSON.parse(xhr.response));
observer.complete();
} else {
observer.error(xhr.response);
}
}
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
The component HTML:
<a (click)="modal.open()">
{{vsmodule.displayName}}
</a>
<modal #modal>
<form (ngSubmit)="onSubmit()">
<modal-header [show-close]="true">
<h4 class="modal-title">Input Module - {{vsmodule.displayName}}</h4>
</modal-header>
<modal-body>
<p>{{vsmodule.description}}</p>
<hr>
<ul>
<li *ngFor="let arg of vsmodule.args; let i = index">
<fieldset *ngIf="arg.type == 'file'">
<label>{{ arg.displayName }}</label>
<input
name="{{arg.name}}"
id="{{ arg.name }}"
type="file"
[(ngModel)]="moduleArguments[arg.name]"
(change)="onFileChange($event)"
>
<p>{{ arg.description }}<p>
</fieldset>
</li>
</ul>
</modal-body>
<modal-footer>
<button type="button" class="btn btn-default" data-dismiss="modal" (click)="modal.dismiss()">Dismiss</button>
<button type="submit" class="btn btn-primary">Run</button>
</modal-footer>
</form>
</modal>
In onChange, fileArgument.name is set to the value of event.originalTarget.id - the id of an actual HTML element in the page
And chrome error is saying:
Failed to set the 'value' property on 'HTMLInputElement'
Edit since you added the html - you have bound the 'moduleArguements' property to the file input element's ngmodel - as a result, changing that value will cause angular to try and modify the value property on the file input which is not permitted.
What is the purpose of updating that value? Is it just to feedback to the user?
If you remove the ngModel binding from the input element it should work - you are using the onFileChange event to capture the new filename anyway (although in the controller it is just onChange?)
Short Answer: You cannot cannot actually change the value of the this.moduleArguments[this.fileArgument.name]as it would be a security issue.
Explaination: You would be changing the actual value of this.fileArgument.name to something else, which would allow other people with ill intent to do the same. Essentially, an attacker could change that name to redirect any attempts to use that file to another file. So, Java (or Flash, or any other programming language) could not programmatically change that due to security reasons.
With your work-around, you are not actually setting a File data member, therefore JS does not see this as a security hazard.
Remember, [almost] anything you can do involving website/server code (or code interacting with either), an attacker can do as well. That's why JS, in this case, blocks people from changing the content of this specific standard File object. Hope this helps!
have you tried this:
var fileName = this.fileArgument.name;
this.moduleArguments[fileName] = response.filename;
and also if you are somewhere in JS changing the 'value' of your
tag, you will get that error, please refer to:
https://stackoverflow.com/a/29721020/2071008

How to show correct profile picture Meteorjs

I am using CollectionFs to Upload profile pictures. Uploading and storing the image is successful. I can insert and show the image alright for one user but the
problem is:
For multiple users, when a user visit other users profiles, He sees his own picture rather than seeing the profile owner's picture!
I understand its the mongo query I have in my helper function thats causing the issue but can't just get it to work no matter how many "This._id" I Try.
Here is the javaScript
Router.route('show',{
path:'/list/:_id',
data: function(){
return Main_database.findOne({_id: this.params._id});
}
});
Template.upload.events({
'change #exampleInput':function(event, template){
var file = $('#exampleInput').get(0).files[0];
fsFile = new FS.File(file);
fsFile.metadata = {ownerId:Meteor.userId()}
Images.insert(fsFile,function(err,result){
if(!err){
console.log("New images inserted")
}
})
}
});
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId':Meteor.userId()});
}
});
And here is the html:
<template name="upload">
<div class="container">
<div class="row">
<input type="file"
id="exampleInput">
</div>
</div>
</template>
<template name="profile">
{{#each profilePic}}
<img src="{{this.url}}"
height="400" width="400"
class="img-circle">
{{/each}}
</template>
Thanks
B.s : after following the answer given, I attached the photo in the profile.xxx field. But its still showing the wrong picture. The mongo query is still showing the wrong picture.
here is the code,
Router.route('show',{
path:'/list/:_id',
data: function(){
return Main_database.findOne({_id: this.params._id});
}
});
Template.upload.events({
'change #exampleInput':function(event, template){
var file = $('#exampleInput').get(0).files[0];
newFile = new FS.File(file);
newFile.metadata = {'ownerId':Meteor.userId()};
Images.insert(newFile,function(err,result){
if(!err){
console.log(result._id);
Meteor.users.update(Meteor.userId(),{
$set: {
'profile.profilePic': result._id
}
});
}
});
}
})
// .....................profile pic.............
Template.profile.helpers({
profilePicture: function () {
return Images.find({'_id':Meteor.user().profile.profilePic});
}
});
Finally was able to do it. Being a beginner, I was stuck at uploading images and then showing them for my users for days. Tried almost each method out there, none worked. asked everywhere, none of the answer worked. Finally , a dead simple package from cosio55:autoform-cloudinary worked like magic!!! Just take a look at the problems I faced while using these packages:
1. cfs:ui
{{#with FS.GetFile "images" selectedImageId}}
// image url
{{/with}}
problem:
with this was I couldn't get the selectedImageId .
2. cfs:gridfs
problem :
grid fs stores image in a separate collection. My user list uses iron router to show the user list form another collection. Image was getting uploaded into the images collection. But For the love of my life, I couldn't show them correctly. Each user was seeing his own picture rather than the profile owner's picture. happened because of a wrong mongo query but I couldn't get the right query. Tried attaching the photo in the profile.profilePicture, but same problem of wrong image stayed.
And I had to put the upload photo in a separate page and NOT in the autoform.
3. lepozepo:cloudinary
Problem:
Image uploaded fine. But had problem getting /storing the image url. Couldn't get
And I had to put the upload photo in a separate page and NOT in the autoform.
public_id ????? Got lost there.
4. autoform-file by yogiben
same problem as GridFs.
Finally with this cosio55:autoform-cloudinarypackage took me just a minute to figure things out. A minute vs days of other big name packages!!!!
:smiley:
<div> <img src=" {{image}}" alt=""> Image </div>
just add {{image} in the img source and thats it. The image url is stored in the same collection autoform stores everything.
Cheers Mates.
For the profilePic it will return the same user profile image, instead you should do a query by _id and not metadata.ownerId
To do that you should have a reference for the image in users collection when you insert the image something like:
Images.insert(file, function (err, res) {
if (!err) {
Meteor.users.update(Meteor.userId(),{
$set: {
'profile.profilePic': res._id,
}
});
});
And when you need to display the image you can do something like:
Template.profile.helpers({
profilePic: function () {
return Images.find({'_id':Meteor.user().profile.profilePic});
}
});
First things first: Meteor.user() and Meteor.userId() always shows data for CURRENTLY logged-in user.
So when user wants to see his own profile, it is right to use your current Template helper like this:
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId':Meteor.userId()});
}
});
But when user goes to another user's profile, you should fetch that user's info like this: Meteor.user("another-user-id");
So how to do this? Let's suppose that you have set routing in you Meteor app with Iron Router or Flow Router and for user profile page you have set-up route path something like this: /user/:_id.
Now, you want to publish only this user's data like this in your publications on the server:
Meteor.publish("userData", function(userId){
return = Meteor.users.find({"_id": userId});
});
...and subscribe to this publication on client (we'll use Template-level subscriptions!)
With Iron Router:
var userId;
Template.profile.onCreated(function() {
var self = this;
self.autorun(function() {
userId = Router.current().params._id;
self.subscribe("userData", userId);
}
});
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId': userId});
}
});
With FlowRouter:
var userId;
Template.profile.onCreated(function() {
var self = this;
self.autorun(function() {
userId = FlowRouter.getParam("_id");
self.subscribe("userData", userId);
}
});
Template.profile.helpers({
profilePic: function () {
return Images.find({'metadata.ownerId': userId});
}
});

Resources