link1781 link1782 link1783 link1784 link1785 link1786 link1787 link1788 link1789 link1790 link1791 link1792 link1793 link1794 link1795 link1796 link1797 link1798 link1799 link1800 link1801 link1802 link1803 link1804 link1805 link1806 link1807 link1808 link1809 link1810 link1811 link1812 link1813 link1814 link1815 link1816 link1817 link1818 link1819 link1820 link1821 link1822 link1823 link1824 link1825 link1826 link1827 link1828 link1829 link1830 link1831 link1832 link1833 link1834 link1835 link1836 link1837 link1838 link1839 link1840 link1841 link1842 link1843 link1844 link1845 link1846 link1847 link1848 link1849 link1850 link1851 link1852 link1853 link1854 link1855 link1856 link1857 link1858 link1859 link1860 link1861 link1862 link1863 link1864 link1865 link1866 link1867 link1868 link1869 link1870 link1871 link1872 link1873 link1874 link1875 link1876 link1877 link1878 link1879 link1880 link1881 link1882 link1883 link1884 link1885 link1886 link1887 link1888 link1889 link1890 link1891 link1892 link1893 link1894 link1895 link1896 link1897 link1898 link1899 link1900 link1901 link1902 link1903 link1904 link1905 link1906 link1907 link1908 link1909 link1910 link1911 link1912 link1913 link1914 link1915 link1916 link1917

[Vue.js] When I try to detach() is returning the error local.ERROR Call to a member function programs() on integer0 App|Http|Controllers|CoursesController->update(Object(Illuminate\Http\Request), '2')

there is this Many to Many relationship between Courses and Programs. I can insert new courses and add as many programs as and it’s returning correctly. The problem is on Update. I can fecth all data in my Course update form and all Programs that was selected through checkboxes checked or unchecked.

My idea is when I update the Course, to delete all Programs related (detach()) and attach again according to Programs checked in the update form.

The problem happens when I call the update method and it calls the detach().

My models are:

Course (model):

class Course extends Model
{
protected \$fillable = [
‘name’, ‘title’, ‘description’, ‘is_active’
];

public function programs(){
return \$this->belongsToMany(Program::class)->withTimestamps();
}
}

Program (model):

class Program extends Model
{
protected \$fillable = [
‘name’, ‘description’, ‘is_active’
];

public function courses(){
return \$this->belongsToMany(Course::class)->withTimestamps();
}
}

My update on CoursesController:

public function update(Request $request, $id)
{
\$request->validate([
‘name’ => ‘required’,
‘title’ => ‘required’
]);

// $course = Course::whereId($id)->update($request->all());
$course = Course::whereId($id)->update([
‘name’ => $request->name,
‘title’ => $request->title,
‘description’ => $request->description,
‘is_active’ => \$request->is_active
]);

$course->programs()->detach();
$program = Program::find($request->programs);
$course->programs()->attach(\$program);

return response()->json([
‘course’ => \$course,
‘message’ => ‘Course has been Updated!’
]);
}

my Course Vuejs component:

<template>

</template>
<script>
export default {

data(){
return {
course:{
name: ‘’,
title: ‘’,
description: ‘’,
is_active: true,
programs: []
},
courses: [],
uri: ‘http://127.0.0.1:8000/courses/',
errors: [],
new_update_course: [],
toastr: toastr.options = {“positionClass”: “toast-top-full-width”},

programs: [],
uriPrograms: ‘http://127.0.0.1:8000/activePrograms/',
}
},

methods: {

loadUpdateCourseModal(index){
this.errors = [];
\$(“#update-course-modal”).modal(“show”);
this.new_update_course = this.courses[index];

this.new_update_course.programs.forEach(program => {
this.new_update_course.programs.push(program.id)
});
},

updateCourse(){
axios.patch(this.uri + this.new_update_course.id, {
name: this.new_update_course.name,
title: this.new_update_course.title,
description: this.new_update_course.description,
is_active: this.new_update_course.is_active,
programs: this.new_update_course.programs
}).then(response =>{
\$(“#update-course-modal”).modal(“hide”);
toastr.success(response.data.message);
}).catch(error=>{
if(error.response.data.errors.name){
this.errors.push(error.response.data.errors.name[0]);
}
if(error.response.data.errors.title){
this.errors.push(error.response.data.errors.title[0]);
}
if(error.response.data.errors.description){
this.errors.push(error.response.data.errors.description[0]);
}
});
},

},

mounted(){

}
}
</script>

If I try to update the course with id = 2, for example, in my laravel log, the error is:

local.ERROR: Call to a member function programs() on integer {“userId”:1,”exception”:”[object] (Symfony\Component\Debug\Exception\FatalThrowableError(code: 0): Call to a member function programs() on integer at /Users/GiselleTavares/Documents/…/app/Http/Controllers/CoursesController.php:120)
[stacktrace]

0 [internal function]: App\Http\Controllers\CoursesController->update(Object(Illuminate\Http\Request), ‘2’)

The line for CoursesController.php:120 is:

\$course->programs()->detach();

I don’t understand what this error means…

Call to a member function programs() on integer …

If I comment these 3 lines below, I can update the course data but, of course, without changes on Programs relationship…

$course->programs()->detach();
$program = Program::find($request->programs);
$course->programs()->attach(\$program);

If I comment just the detach() line, the attach() line show the same error above. However, I use exactly the same (without the detach()) on my store() and it’s working.

Any ideas? I tried to find solutions but nothing helped…

Solution :

The update() function returns an integer indicating whether or not the update (which is a bulk update, because Course::whereId(\$id) returns a collection of results, not a single one).

This should work:

$course = Course::findOrFail($id);

$course->update([
‘name’ => $request->name,
‘title’ => $request->title,
‘description’ => $request->description,
‘is_active’ => \$request->is_active
]);

Solution 2:

The update() method is going to return a boolean value so \$course is not going to be the model you want to update. As for why the error is saying integer instead of boolean I’m not 100% sure.

There are a couple of solutions to get the corse:

Just use find() or findOrFail()

$course = Course::findOrFail($id);

Use Route Model Binding

public function update(Request $request, Course $course)

Going with option 1 as an example the code would look something like:

public function update(Request $request, $id)
{
\$request->validate([
‘name’ => ‘required’,
‘title’ => ‘required’,
]);

$course = Course::findOrFail($id);

$course->update([
‘name’ => $request->name,
‘title’ => $request->title,
‘description’ => $request->description,
‘is_active’ => \$request->is_active,
]);

$course->programs()->sync($request->input(‘programs’, []));

return response()->json([
‘course’ => \$course,
‘message’ => ‘Course has been Updated!’,
]);
}

In the above example there is used sync() instead of detaching and the attaching the new ids.

[Vue.js] How to get Vue to remove whitespace in compiled output?

The preserve whitespace option in vue.js isn’t working. It is generating code with lots of whitespace:

_vm._v(
“\n “ +
_vm._s(_vm.object.created_by_full_name) +
“\n “
)

Solution :

Put the following in vue.config.js

chainWebpack: config => {
config.module
.rule(‘vue’)
.use(‘vue-loader’)
.loader(‘vue-loader’)
.tap(options => {
// modify the options…
options.compilerOptions = { whitespace: ‘condense’ };
return options;
});
},

[Vue.js] Is there a way to convert from a component with inputs to raw html?

there is a component in vue.js component-z.

I need to pass in certain parameters, so when I use this in my Vue’s html I do as follows:

<component-z id=”component” data1=”data” data2=”moreData”></component-z>

I need to take the html out of this and pass it as a message to a parent frame (from an iFrame). Is there a way to do something like:

<component-z id=”component” data1=”data” data2=”moreData”></component-z>.toHtml()

Solution :

First, add a ref attribute to the line, such as ref=’component-z’

If you are inside the <script> portion of the vue.js file, you can access the vue.js instance and its references like this:

this.$refs

To access the outer HTML (or rendered/raw HTML) of that component, use

this.$refs.component-z.$el.outerHTML

Note the “component-z” portion of that JavaScript, which corresponds to that ref attribute you set.

[Vue.js] How do I generate a list of items based on selected dropdown item?

I would like to filter a list of items based on the dropdown option the user has selected.

This is my dropdown:

<select class=”form-control” onchange=”loadFilteredList()” id=”conditionSelect”>
<option disabled value=”” selected=”selected”>Please select a filter</option>
<option>Under 18</option>
<option>Above 18</option>
</select>

This is the loop when using to generate the list with:

<table class=”table table-hover” id=”ageOverview”>
<tr>
<th>Col1</th>
<th>Col2</th>
<th>Col3</th>
</tr>
@foreach (var person in ViewBag.persons)
{
<tr>
<td>@person.FirstName</td>
<td>@person.MiddleName</td>
<td>@pperson.LastName</td>
</tr>
}
</table>

to load the correct list each time the user selects an option from the dropdown.

The ViewBag with the data is filled within the controller I have. At the moment there is a ViewBag for the first option from the dropdopdown, and a ViewBag for the second option from the dropdown. Don’t know if this is the right approach but I’m very new to this and would like to get some advice.

Thanks in advance!

Solution :

Without accounting for the vue.js tag you have here, my typical way to manage something with large (more than a couple hundred DOM elements, and you mentioned 1000+) is to hook it with small controller actions to avoid polluting the DOM, which slows things down a lot.

We have to look at options, though, because you’re going to take a hit somewhere. You have to decide where. Large number of DOM elements vs bandwidth of requests. Storing the data in Session so it’s available for multiple post requests or requerying whatever generated it.

So for purposes here, I’ll assume we do not want to have tons of items in the DOM and just show/hide, which is the simplest way, but not good as you start adding items and combinations or mobile users try to load a page, etc. I’m also colored by the name of the function, loadFilteredList, which to me just yells out “add another filter” requirement from someone soon.

So here’s the basic design:

A controller method returns the table content you have here as a partial view.

It (the controller method) optionally has an argument that is the filter option. (more of those can be added later; the point is, the controller is doing the filtering and sending data)

the javascript then for the onchange is just firing off a post request to the controller for new filtered data. You can use jquery or vanilla to set the html content as needed. the controller either gets fresh data for filtering or uses session. Whatever is best for the needs here. You don’t need ViewBag though, it’s not for this type of situation.

The downside of this type of design is that a fresh request is sent in the background to the server for each filter. The upside is that hopefully the payloads are small enough, so it’s not as expensive to do it that way as to return every possible row so that it can be filtered client side.

[Vue.js] How to pass parameters in get request using axios?

when trying to pass api_key in the get request parameter using axios

here is my code

const instance = axios.create({
baseURL: “https://api.themoviedb.org/3"
});
export function crudify(path) {
function get(id) {
return instance.get(`${path}/${id}`, {
params: {
api_key: “qwerasd”
}
});
}

return { get };
}

I expected to get the URL:

https://api.themoviedb.org/3/genre/movie/list?api\_key=qwerasd

but instead I get:

https://api.themoviedb.org/3/genre/movie/list

Solution :

You need to append query string params directly to url:

function get(id) {
return instance.get(`${path}/${id}?api_key=qwerasd`);
}

[Vue.js] Vue Do computed properties watch for internal data changes?

when passing in products as a prop to this component. when then setting a data value to those props because when going to be pushing products to it.
In my template, when using a computed property productsToShow because if a prop hideSoldOutProducts is passed in, they get filtered out.

My question is: I made productsData a data value because at the bottom of the page, their will be an API call to push more products to the productsData array. So although the productsData is being added to, the computed property productsToShow() which is using the productsData is not reflecting this.

<template>
<div v-for=”(product, index) in productsToShow”>
…..
</div>
</template>

data() {
return {
productsData: Vue.util.extend([], this.products.data)
}
},
props: {
products: {type: Object},
hideSoldOutProducts: {type: Boolean}
},
computed: {
productsToShow() {
if (!this.products.data) {
return []
}

if (this.hideSoldOutProducts) {
let filteredList = this.productsData.filter(x => x.product.inventory_quantity.quantity > 1)
return filteredList
} else {
return this.productsData
}
}
}

Solution :

It feels like there might be a better approach, since this looks quite complex. But perhaps the reactivity is not working because the data is only set on mount. You could set a watcher on the property and bind the new property data to productsData on a change.

But, there is the feeling that it might be a better approach to create an addProduct methods that will push a product into you data array. Something like this.

watcher: {
products(value) {
value.data.forEach(this.addProduct);
}
},
methods: {
addProduct(product) {
// here you could also check if the product already exists in the
// productsData
this.productsData.unshift(product);
},
fetchProducts() {
this.$http.get(‘/api/products’)
.then(resp => {
// Now we can loop through and add the products.
resp.data.forEach(this.addProduct);
});
}
}

I hope this helps.

[Vue.js] debounce function being called every time

I’m calling searchkeyword function on keyUp. to cancel/clearTimeout $emit when new letter is typed quickly, so that only few times $emit is called.
But console being called/debounce on every searchkeyword call.

methods: {
searchKeyword : function() {
var scope = this;
(this.debounce(function(){
scope.$emit(“search-keyword”, scope.keyword);
console.log(“Called”);
},350))();
},
debounce: function (func, delay) {
var timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), delay);
}
}
}

Solution :

You approach is good, setting a timeout then clearing it is a well known approach to debouncing. This answer describes it and uses the same method.

The problem is that you are creating a new debounced function in each call to searchKeayword, then executing it right away.

You instead need to pass the debounced function directly.

const debounce = (func, delay) => {
let timeout;

return function() {
const context = this;
const args = arguments;

clearTimeout(timeout);

timeout = setTimeout(function() {
func.apply(context, args), delay;
}, delay);
};
};

new Vue({
el: ‘#root’,
name: “App”,
data: _ => ({ called: 0 }),
methods: {
doSomething: debounce(function() {
this.called += 1;
}, 2000)
},
template: `
<div>
<button v-on:click=’doSomething’>Do Something</button>
I’ve been called { called } times
</div>
`
})
<script src=”https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id=’root’></div>

Note also that debounce does not need to be a method of the component.

[Vue.js] Vue + Laravel Still use old endpoint pagination?

when working on a Laravel app that has used Blade files for a while. We are switching part of the app to use vue.js now. One of the endpoints we had used pagination with a page parameter at the end of it.
For example: /store/api/users?page=1 and each page would show 20 users — sort of like a lazyLoad.
Does this make sense to keep it like this for Vue? With Vue, shouldn’t the endpoint just get me ALL the users and then I can do what with that data?

Solution :

No, you should not query all the data and return to vuejs. If the data is huge then you will be in big trouble with slower performance. So, it’s always a good idea to use Larave’s paginations even while you are responding json instead of view.

For example when you were using blade, you were doing something like:

$users = User::where(‘column’, $value)->paginate();

return view(‘user.index’, compact(‘user’));

Right? Now you are using Vuejs and still have you covered with it’s nice length aware paginator instance. So, now you can do something like:

$users = User::where(‘column’, $value)->paginate();
return $users;

This Will return all paginations meta data like, total page, current page etc.
So that, you can manipulate those data perfectly in vuejs.

[Vue.js] How to put the popup above the gridline and text?

Before popup:
https://i.imgur.com/qNrsTej.png

After popup:
https://i.imgur.com/EWrAY3E.png

The popup div is inside date 3.
You can see that the popup covers the grid before it but not those comes after it. Does anyone know how to fix this?

there is tried setting the z-index but it seems not to be the case.

Here’s the html and the CSS of my page.

<div class=”calendar”>
<span class=”day-name” v-for=”weekday in weekdayInfo”>{weekday.shorthand}</span>
<div class=”day” v-for=”calendarDay in calendarDays” @click=”focusOnDay(calendarDay)”>
{calendarDay.getDate()}
<div class=”popup_calendar light” v-if=”calendarDay == focusDay”>
<div class=”calendar_header”>…</div>
<div class=”calendar_events”>…</div>
</div>
</div>
</div>

.calendar {
display: grid;
width: 100%;
grid-auto-columns: minmax(120px, 1fr);
grid-template-rows: 50px;
grid-auto-rows: 120px;
overflow: auto;
}
.calendar-container {
width: 90%;
margin: auto;
overflow: hidden;
font-family: Montserrat, “sans-serif”;
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
border-radius: 10px;
background: #fff;
max-width: 1200px;
}
.day {
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
border-right: 1px solid rgba(166, 168, 179, 0.12);
text-align: right;
padding: 14px 20px;
letter-spacing: 1px;
font-size: 12px;
box-sizing: border-box;
color: #98a0a6;
position: relative;
z-index: 1;
}
.light {
background-color: #fff;
}

.popup_calendar {
text-align: left;
width: 500px;
height: 500pxcss;
box-shadow: 0px 0px 35px -16px rgba(0, 0, 0, 0.75);
font-family: ‘Roboto’, sans-serif;
padding: 20px;
color: #363b41;
background-color: #FFFFFF;
display: inline-block;
z-index: 200;
overflow: visible;
margin-top: -100px;
margin-left: -100px;
}

Solution :

remove z-index for .day, add position:relative; for .popup_calendar

.popup_calendar {
text-align: left;
width: 500px;
height: 500pxcss;
box-shadow: 0px 0px 35px -16px rgba(0, 0, 0, 0.75);
font-family: ‘Roboto’, sans-serif;
padding: 20px;
color: #363b41;
background-color: #FFFFFF;
display: inline-block;
z-index: 200;
overflow: visible;
margin-top: -100px;
margin-left: -100px;
position:relative;
}

.day {
border-bottom: 1px solid rgba(166, 168, 179, 0.12);
border-right: 1px solid rgba(166, 168, 179, 0.12);
text-align: right;
padding: 14px 20px;
letter-spacing: 1px;
font-size: 12px;
box-sizing: border-box;
color: #98a0a6;
position: relative;
/*z-index: 1;*//*Remove this*/
}

https://codepen.io/anon/pen/wZNjVe

[Vue.js] How to render contents based on the params received from vue-router?

when using vue.js 3.6.3 to build a film list page. The home page shows a list of films, only includes the brief information of each film, and the user can click the title of the header of each film to enter a page where there is detail information about the film. I use the vue-router to help me with the transition.

The root component is App.vue, actually it does nothing but showing its child components.

the important codes in App.vue:

<template>
<div id=”app”>
<router-view></router-view>
</div>
</template>

The router is mounted on App.vue, I created a routers.js file to define the routes, and then make a router in main.js.

codes from routers.js:

import App from ‘./App.vue’
import FilmList from ‘./components/FilmList.vue’
import DetailPage from ‘./components/DetailPage.vue’

const routers = [{
path: ‘/‘,
redirect: ‘/filmlist’,
component: App,
children: [{
path: ‘/filmlist’,
name: ‘filmlist’,
component: FilmList
},
{
path: ‘/detail/:id’,
name: ‘detail’,
component: DetailPage
}
]
}]
export default routers

codes from main.js:

import vue.js from ‘vue’
import VueRouter from ‘vue-router’
import App from ‘./App.vue’
import routes from ‘./routers’

Vue.use(VueRouter)
Vue.config.productionTip = false

const router = new VueRouter({
mode: ‘history’,
routes: routes
})

new Vue({
router: router,
render: h => h(App),
}).$mount(‘#app’)

The App.vue.js component has two children, FilmList and the DetailPage. At first, FilmList will be shown, after the user click one of them, it will jump to a detail page of a certain movie based on the id param in the router. After receiving the id param, the DetailPage.vue.js ought to select one of the movies from the film list and display its detailed contents.

codes from the FilmList.vue:

<template>
<div id=”filmlist”>
<film-brief
v-for=”(film, index) in filmList[pageIndex-1]“
v-bind:key=”film._id”
v-bind:index=”index + groupCount*(pageIndex-1)”
v-bind:film=”film”
\></film-brief>
<div class=”buttons”>
<jump-button
class=”button”
v-for=”index in buttonIndexList”
:key=”index.id”
v-bind:index=”index”
v-on:jump-page=”pageIndex = index.id”
\></jump-button>
</div>
</div>
</template>

<script>
import FilmBrief from “../components/FilmBrief.vue”;
import films from “../assets/films.json”;
import JumpButton from “../components/JumpButton.vue”;

const GroupCount = 10;

export default {
name: “FilmList”,
data: function() {
return {
films,
pageIndex: 1,
pageCount: Math.ceil(films.length / GroupCount),
groupCount: GroupCount
};
},
components: {
FilmBrief,
JumpButton
},

……

codes from FilmBrief.vue:

<template>
<div class=”film-brief”>
<div class=”poster”>
<img :src=”imgSrc”>
</div>

<div class=”info”>
<router-link
:to=”{
name: ‘detail’,
params: {
id: index
}
}”
\>
<div class=”heading” v-html=”title”></div>
</router-link>

<div class=”ratings”>{film.rating.average}</div>
<div class=”ontime”>{pubdate}</div>
<div class=”type”>{genres}</div>
</div>
</div>
</template>

<script>
import errorImg from “../assets/imgErr.jpg”;

export default {
name: “FilmBrief”,
props: [“film”, “index”],
data: function() {
return {
imgSrc: “”,
imgErrSrc: errorImg
};
},
methods: {
loadImg(resolve, reject) {
let originImgSrc = this.film.poster;
let img = new Image();
img.src = originImgSrc;

img.onload = function() {
resolve({
src: img.src
});
};

img.onerror = function(e) {
reject(e);
};
}
},
created: function() {
this.loadImg(
response => {
this.imgSrc = response.src;
},
reject => {
console.log(“”);
this.imgSrc = this.imgErrSrc;
}
);
},
……

codes from DetailPage.vue:

<template>
<div class=”detail-page”>
<div class=”poster”>
<img :src=”this.imgSrc”>
</div>
<div class=”details”>
<ul>
<li id=”title” v-html=”title”></li>
<li id=”director”>{directors}</li>
<li id=”writers”>{writers}</li>
<li id=”casts”>{casts}</li>
<li id=”genres”>{genres}</li>
<li id=”duration”>{duration}</li>
<li id=”pubdate”>{pubdate}</li>
<li id=”summary”></li>
</ul>
</div>
</div>
</template>

<script>
import errorImg from “../assets/imgErr.jpg”;
import films from “../assets/films.json”;

export default {
name: “DetailPage”,
props: [],
data: function() {
return {
id_: this.$route.params.id,
film: films[id_],
imgSrc: “”,
imgErrSrc: errorImg,
duration: “” + this.film.duration + “”,
summary: “” + this.film.summary
};
},
methods: {
loadImg(resolve, reject) {
let originImgSrc = this.film.poster;
let img = new Image();
img.src = originImgSrc;

img.onload = function() {
resolve({
src: img.src
});
};

img.onerror = function(e) {
reject(e);
};
},
getList(name, list) {
let result = “”;
result += name;
for (let i = 0; i < list.length; i++) {
result += list[i].name;
if (i !== list.length - 1) {
result += “ / “;
}
}
return result;
}
},
created: function() {
this.loadImg(
response => {
this.imgSrc = response.src;
},
reject => {
console.log(“”);
this.imgSrc = this.imgErrSrc;
}
);
},
computed: {
title: function() {
let originalTitle = “”;
originalTitle += this.film.title;
let index = originalTitle.indexOf(“ “);
let input = originalTitle[index - 1];
let isPunc =
(input >= “A” && input <= “Z”) || (input >= “a” && input <= “z”);
if (!isPunc) {
return (
originalTitle.slice(0, index) +
“<br>” +
originalTitle.slice(index + 1)
);
} else {
return originalTitle;
}
},
directors: function() {
return this.getList(“”, this.film.directors);
},
writers: function() {
return this.getList(“”, this.film.writers);
},
casts: function() {
return this.getList(“”, this.film.casts);
},
genres: function() {
let genres = this.film.genres;
let str = “”;
for (let i = 0; i < genres.length; i++) {
str += genres[i];
if (i !== genres.length - 1) {
str += “ / “;
}
}
return str;
},
pubdate: function() {
let dates = this.film.pubdate;
if (dates[0] === “”) {
return “”;
} else {
return “” + dates[0];
}
}
}
};
</script>

After clicking, the route shown in the browser is OK, like “http://localhost:8080/detail/11", however, the page shows nothing, and the error in the console is something like:

[vue.js warn]: Error in data(): “ReferenceError: id_ is not defined”

found in

-–> <DetailPage> at src/components/DetailPage.vue
<App> at src/App.vue… (1 recursive calls)

ReferenceError: id_ is not defined
at VueComponent.data (DetailPage.vue?b507:31)
at getData (vue.runtime.esm.js?2b0e:4742)
at initData (vue.runtime.esm.js?2b0e:4699)
at initState (vue.runtime.esm.js?2b0e:4636)
at VueComponent.Vue._init (vue.runtime.esm.js?2b0e:5000)
at new VueComponent (vue.runtime.esm.js?2b0e:5148)
at createComponentInstanceForVnode (vue.runtime.esm.js?2b0e:3283)
at init (vue.runtime.esm.js?2b0e:3114)
at merged (vue.runtime.esm.js?2b0e:3301)
at createComponent (vue.runtime.esm.js?2b0e:5972)

[vue.js warn]: Error in created hook: “TypeError: Cannot read property ‘poster’ of undefined”

found in

-–> <DetailPage> at src/components/DetailPage.vue
<App> at src/App.vue… (1 recursive calls)
<Root>

and many other warnings like that.

Later I assume that maybe it’s because the id param should be loaded before other attributes get access to it, so I add it to the created in DetailPage.vue.js like:

`created: function() {
this.film = films[this.$route.params.id];
this.loadImg(
response => {
this.imgSrc = response.src;
},
reject => {
console.log(“”);
this.imgSrc = this.imgErrSrc;
}
);
},

Now some information can be loaded, but some other cannot, like the poster and the duration. The warnings include:

vue.runtime.esm.js?2b0e:619 [vue.js warn]: Error in data(): “TypeError: Cannot read property ‘duration’ of undefined”

found in

-–> <DetailPage> at src/components/DetailPage.vue
<App> at src/App.vue… (1 recursive calls)
<Root>

TypeError: Cannot read property ‘duration’ of undefined
at VueComponent.data (DetailPage.vue?b507:34)
at getData (vue.runtime.esm.js?2b0e:4742)
at initData (vue.runtime.esm.js?2b0e:4699)
at initState (vue.runtime.esm.js?2b0e:4636)
at VueComponent.Vue._init (vue.runtime.esm.js?2b0e:5000)
at new VueComponent (vue.runtime.esm.js?2b0e:5148)
at createComponentInstanceForVnode (vue.runtime.esm.js?2b0e:3283)
at init (vue.runtime.esm.js?2b0e:3114)
at merged (vue.runtime.esm.js?2b0e:3301)
at createComponent (vue.runtime.esm.js?2b0e:5972)

So, why is that and how can I get all the information displayed?

Apologize if my poor English troubles you, and thanks in advance!

Solution :

the problem is caused by the data() function in the DetailPage component.

data: function() {
return {
id_: this.$route.params.id,
film: films[id_],
imgSrc: “”,
imgErrSrc: errorImg,
duration: “” + this.film.duration + “”,
summary: “” + this.film.summary
};
},

There are several issues:

film: films[id_] won’t work - id_ is not defined anywhere so film prop will be undefined as well.

duration: “” + this.film.duration + “” - you can’t access other data props using this.

You should make the data() function contain default/empty/placeholder data:

data: function() {
return {
id_: this.$route.params.id, // this is fine - you can access $route because it’s global
film: {}, // empty object or some object type containing placeholder props
imgSrc: “”,
imgErrSrc: errorImg,
duration: “”,
summary: “”
};
},

Then in the created or mounted hook, load it:

created: function() {
this.film = films[this.$route.params.id];
this.duration = “” + this.film.duration + “”;
this.summary = “” + this.film.summary;
this.loadImg(
response => {
this.imgSrc = response.src;
},
reject => {
console.log(“”);
this.imgSrc = this.imgErrSrc;
}
);
},