Vue.js undefined error on object value when loaded and rendered - javascript

OK. I'm not a total newbie and do have some Vue xp but this is bugging me. What really obvious thing am I missing.
I have an object loaded via an ajax call inside a mounted method:
job: {
"title": "value",
"location": {
"name":"HONG KONG"
}
}
When I call {{ job.title }} all good. When I call {{ job.location.name }} I have an undefined error but the value renders. When I call {{ job.location }} I get the json object so it is defined.
Aaargh! I'm sure it's really simple but can't possibly see why this isn't as straight forward as it should be.
// Additional
This is my entire Vue class
const router = new VueRouter({
mode: 'history',
routes: []
});
const app = new Vue( {
router,
el: '#app',
data: {
job: {}
},
mounted: function () {
var vm = this
jQuery.ajax({
url: 'https://xxx' + this.jobId,
method: 'GET',
success: function (data) {
vm.job = data;
},
error: function (error) {
console.log(error);
}
});
},
computed: {
jobId: function() {
return this.$route.query.gh_jid
}
}
})

When your component renders it tries to get value from job.location.name but location is undefined before ajax request is completed. So I guess error is kind of Cannot read property 'name' of undefined.
To fix this you can define computed property locationName and return, for example, empty string when there is no loaded job object yet:
computed:{
//...
locationName() {
return this.job.location ? this.job.location.name : '';
}
}
Or you can define computed for location and return empty object if there is no location, or you can just add empty location object to your initial data(if you are sure that your API response always has location) like job: { location: {}} all ways will fix your issue.
Also there is a way to fix it with v-if directive in your template:
<div v-if="job.location">
{{ job.location.name }}
<!-- other location related stuff -->
</div>

An ES6 solution for you:
computed: {
getJobName(){
return this.job?.location.name
}
}
Optional Chaining

Related

Exported data is null when using it in another javascript vue js file

i am developing a shopify theme, currently i am working on shopping cart page, i have CartForm.js file where i am importing data from cartData.js file. This is CartForm.js...
import { store } from "./../shared/cartData.js";
if (document.querySelector('.cart-form')) {
let productForm = new Vue({
el:".cart-form",
delimiters: ['${', '}'],
data(){
return{
cart:null,
}
},
created(){
this.getCart();
},
methods:{
getCart(){
store.getCart();
this.cart=store.state.cartData;
console.log("cart data: "+this.cart);
},
and this is cartData.js file
export const store = {
state: {
cartData:null
},
getCart(){
alert("store get cart called!...")
axios.get('/cart.js')
.then( response => {
this.state.cartData=response.data;
console.log("Responsed data="+this.state.cartData);
})
.catch( error => {
new Noty({
type: 'error',
layout: 'topRight',
text: 'There was an error !!'
}).show();
});
}
}
As you can see i am explicitly calling store.getCart(); in CartForm's getCart() method, and when "Responsed data" gets printed it shows that this.state.cartData filled with data, but when i am using it like this: this.cart=store.state.cartData; this.cart is null, why is null? Any help is appreciated
This happens because your API takes a while to respond, but JavaScript continues running the function in parallel. Your state is still 'null' while the call hasn't returned, thus this.cart will be set to null, unless you tell JavaScript to wait until the call is finished.
Try making the getCart() method an asynchronous function:
methods:{
async getCart(){
await store.getCart();
this.cart=store.state.cartData;
console.log("cart data: "+this.cart);
},
If cart should always be the same as the data in your store, a better way to do this might be to use a computed property for cart. This returns the store.state directly and will help you keep a single source of truth for your data.
computed: {
cart() {
return store.state.cartData
}
}

Trying to access an API using Vue js

I'm having some trouble trying to access an API to get or fetch data. I'm still currently new to vue.js and javascript. I'm getting an error Uncaught SyntaxError: Invalid shorthand property initializer. I can't seem to understand what the error means or seems to indicate.
<body>
<div id="vue-app">
{{ articles }}
</div>
<body>
var article = new Vue({
el: '#vue-app',
data: {
articles = ''
},
created: function () {
this.fetchData();
},
methods: {
fetchData: function () {
var that = this
this.$http.get('localhost/aim-beta/rest/export/json/article'),
function (data) {
vm.articles = data.main.temp;
}
}
}
});
Instead of using this.$http, use axios library for making api calls.
I think you can't use equal in the JS object syntax
data: {
articles = ''
}
Try
data: function() {
return () {
articles: ‘’
}
}
And specify http:// to the localhost
this.$http.get('http://localhost/aim-beta/rest/export/json/article'),
function (data) {
this.articles = data.json()
}
Use this for the data JSON object:
data: {
articles: ''
}
Then, use Promise for firing the HTTP request (note that I used the http:// with the URL):
this.$http.get('http://localhost/aim-beta/rest/export/json/article')
.then(function(response){
this.articles = response.json();
});
Source : Documentation

Vuejs: How to properly pass props to component data

i'm playing with Vue 2.0, but there is something unclear..
how can i pass the props to the internal data of a component?
Looking at the documentation it seems that i have done right.
HTML
<lista-servizi :servizi="modello"></lista-servizi>
the "modello" is a data already defined.
THE VUE COMPONENT
Vue.component('lista-servizi', {
template:
'<span class="blu">{{listaServizi.lineaGialla.descrizione}}</span>',
props: ['servizi'],
data: function(){
return{
listaServizi : this.servizi
}
basically i try to give to data listaServizi the same value as props servizi,
but in the console i have the following message:
[Vue warn]: Error in render function: "TypeError: Cannot read property 'descrizione' of undefined"
found in
---> <ListaServizi>
<Root>
You should used computed instead.
Vue.component('lista-servizi', {
//...
computed: {
listaServizi() {
return this.servizi
}
}
}
Most likely you have a problem with modello.
By defining the modello, your code works fine. Below is an example based on your code that works:
<div id="app-1">
<lista-servizi :servizi="modello"></lista-servizi>
</div>
Vue.component('lista-servizi', {
template: '<span class="blu">{{listaServizi.lineaGialla.descrizione}}</span>',
props: ['servizi'],
data: function(){
return{
listaServizi : this.servizi
}
}
})
var app1 = new Vue({
el: '#app-1',
data: {
modello: {
lineaGialla : {
descrizione : " This is a description"
}
}
}
})
Here is a link to a working bin
https://jsbin.com/qoloqoz/1/edit?html,js,output

Polymer dom-repeat issue

While rendering with Polymer an array of objects, it keeps launching me an exception.
Here's the data model retrieved from server:
{
"lastUpdate":"yyyy-MM-ddTHH:mm:ss.mmm",
"info": [
{
"title": "Some nice title"
},
...
]
}
Here's my Polymer component template:
<dom-module is="items-list">
<template>
<dl>
<dt>last update:</dt>
<dd>[[$response.lastUpdate]]</dd>
<dt>total items:</dt>
<dd>[[$response.info.length]]</dd>
</dl>
<template is="dom-repeat" items="{{$response.info}}">
{{index}}: {{item.title}}
</template>
</template>
<script src="controller.js"></script>
</dom-module>
And here's the controller:
'use strict';
Polymer(
{
properties: {
info: {
type: Array
},
$response: {
type: Object,
observer: '_gotResponse'
}
},
_gotResponse: function(response)
{
console.log(response);
if (response.info.length)
{
try
{
//here I try to set info value
}
catch(e)
{
console.error(e);
}
}
},
ready: function()
{
//set some default value for info
},
attached: function()
{
//here I request the service for the info
}
}
);
If tried to set info value as:
this.info = response.info;
this.set('info', response.info);
this.push('info', response.info[i]); //inside a loop
But the result breaks after rendering the first item, the exception launched is:
"Uncaught TypeError: Cannot read property 'value' of null"
If info === $response.info then why not just use items={{info}} in the repeat?
edit:
Try to modify your code in the following way.
'use strict';
Polymer({
properties: {
$response: {
type: Object
}
},
_gotResponse: function(response)
{
console.log(response);
...
this.$response = response
...
},
attached: function()
{
//here I request the service for the info
someAjaxFn(someParam, res => this._gotResponse(res));
}
});
You don't need an observer in this case. You only use an explicit observer when the implicit ones don't/won't work. I.e. fullName:{type:String, observer:_getFirstLastName}
or
value:{type:String, observer:_storeOldVar}
...
_storeOldVar(newValue, oldValue) {
this.oldValue = oldValue;
}
if you are updating the entire array, ie this.info, then you simple use this.info = whatever once within your function. If you don't want to update the entire array, just some element within it, then you will want to use polymer's native array mutation methods as JS array method doesn't trigger the observer.
Again, since your template doesn't use info, then you don't need the property info. If you want to keep info, then don't store info within $response. In fact, $ has special meaning in polymer so try not to name properties with it. you can simply use the property info and lastUpdate for your polymer.
Last note, beware of variable scoping when you invoke functions from polymer. Since functions within polymer instances often use this to refer to itself, it may cause
Uncaught TypeError: Cannot read property 'value' ..."
as this no longer refers to the polymer instance at the time of resolve.
For example, suppose your host html is
...
<items-list id='iList'></items-list>
<script>
iList._gotResponse(json); // this will work.
setTimeout(() => iList.gotResponse(json),0); //this will also work.
function wrap (fn) {fn(json)}
wrap (iList._gotResponse); //TypeError: Cannot read property '$response' of undefined.
function wrap2(that, fn) {fn.bind(that)(json)}
wrap2 (iList,iList._gotResponse); // OK!
wrap (p=>iList._gotResponse(p)) //OK too!!
wrap (function (p) {iList._gotResponse(p)}) //OK, I think you got the idea.
</script>

knockout throws Message: TypeError: <xxx> is not a function. What does it mean?

I have this code:
<ul id="chats" data-bind="foreach: chats">
<li>
<div class="chat_response" data-bind="visible: CommentList().length == 0">
<form data-bind="submit: $root.addComment">
<input class="comment_field" placeholder="Comment…" data-bind="value: NewCommentText" />
</form>
</div>
<a class="read_more_messages" data-bind="visible: moreChats, click: showMoreChats">Read More Messages...</a>
</li>
</ul>
function ChatListViewModel(chats) {
// Data
var self = this;
self.chats = ko.observableArray(ko.utils.arrayMap(chats, function (chat) {
return { CourseItemDescription: chat.CourseItemDescription,
CommentList: ko.observableArray(chat.CommentList),
CourseItemID: chat.CourseItemID,
UserName: chat.UserName,
ChatGroupNumber: chat.ChatGroupNumber,
ChatCount: chat.ChatCount,
NewCommentText: ko.observable("")
};
}));
self.moreChats = ko.observable(true);
self.showMoreChats = function () {
var LastChatGroupNumber = self.chats()[self.chats().length - 1].ChatGroupNumber;
$.ajax({
url: $.CourseLogic.dataitem.GetMoreChatsUrl,
data: "{chatGroupNumber: " + ko.toJSON(LastChatGroupNumber + 1) + "}",
type: "post",
contentType: "application/json",
success: function (chats) {
var chatList = self.chats();
$.each(chats, function (index, chat) {
self.chats.push(chat);
});
}
});
}
}
ko.applyBindings(new ChatListViewModel(initialData));
But I get this error when the showMoreChats function is called:
Unable to parse bindings.
Message: TypeError: CommentList is not a function;
Bindings value: visible: CommentList().length == 0
What does it mean?
It's not that CommentList is undefined, it's just that it's not an observable (hence not a function). The reason being that in your ajax callback, you are just pushing the 'chat' objects received from your server "as is". You're not creating for example a new observableArray called CommentList, but you're just putting a bare array CommentList - hence the thrown error by KO.
You would need to make the same transformation as you did when constructing self.chats in the viewmodel constructor, e.g.:
$.each(chats, function(index, chat) {
self.chats.push(
{
CourseItemDescription: chat.CourseItemDescription,
CommentList: ko.observableArray(chat.CommentList),
CourseItemID: chat.CourseItemID,
UserName: chat.UserName,
ChatGroupNumber: chat.ChatGroupNumber,
ChatCount: chat.ChatCount,
NewCommentText: ko.observable("")
}
);
});
By the way, you should also take a look at the ko.mapping plugin, it can do this transformation for you.
Edit: Also, for better performance you shouldn't push each individual item into the observable array, but add them in one bulk operation, something like:
self.chats( self.chats().concat( ko.utils.arrayMap(chats, function(chat) {
return { /* ... same as before ... */ };
} ) );
Found this question via Google, adding my case for reference.
It's really, really stupid; yet this is a mistake caused by inattention I often make:
When using one of the ko.utils array functions (arrayMap, arrayForEach, arrayFirst, etc.), Knockout will throw this error when you forget to specify the source array, eg. when you do:
ko.utils.arrayMap(function(item) { ... })
instead of:
ko.utils.arrayMap(myArray, function(item) { ... });
It means your binding cannot detect CommentList, i.e. CommentList is undefined in the current context.

Categories

Resources