Vue returns both observer and array - arrays

I'm currently working on a private chatroom.
i started two browser from which one in incognito
when i send a message from browser window one i console.log the chats array but it's an vue observer object.
when i send a message from browser window 2 i console.log the same chats array, but it tels me it's an array
the first browser window gives an error this.chats.push is not a function
the second browser window accepts the function and saves the message and adds it to the chats array.
Can anybody explain to me why it is doing that?
Just curious
<template>
<div class="card card-default chat-box">
<div class="card-header">
<b :class="{'text-danger':isBlocked}">
{{friend.first_name}}
<span v-if="isBlocked">(Blocked)</span>
</b>
<!-- Close button -->
<a href="" #click.prevent="close">
<font-awesome-icon icon="times" class="float-right"/>
</a>
<!-- Close button ends here -->
<!-- Options -->
<div class="dropdown float-right">
<b-dropdown variant="link" no-caret toggle-class="text-decoration-none">
<template v-slot:button-content>
<font-awesome-icon icon="ellipsis-v"/>
</template>
<b-dropdown-item href="" #click.prevent="block" v-if="!isBlocked">Block</b-dropdown-item>
<b-dropdown-item href="" #click.prevent="unblock" v-if="isBlocked">Unblock</b-dropdown-item>
<b-dropdown-item href="" #click.prevent="clear">Clear</b-dropdown-item>
<b-dropdown-divider></b-dropdown-divider>
<b-dropdown-item active>Active action</b-dropdown-item>
<b-dropdown-item disabled>Disabled action</b-dropdown-item>
</b-dropdown>
</div>
<!-- Options Ends -->
</div>
<div class="card-body" v-chat-scroll>
<p class="card-text" :class="{'text-right': chat.type === false}" v-for="chat in chats" :key="chat.id">
{{chat.message}}
</p>
</div>
<form class="card-footer" #submit.prevent="send">
<div class="form-group">
<input type="text" class="form-control" placeholder="Write your message"
:disabled="isBlocked" v-model="message"
>
</div>
</form>
</div>
</template>
<script>
import {faIcons} from "#fortawesome/free-solid-svg-icons";
import {DropDownButtonPlugin} from "#syncfusion/ej2-vue-splitbuttons";
Vue.use(DropDownButtonPlugin);
export default {
props: ['friend'],
computed: {
queen() {
return faIcons;
}
},
data() {
return {
chats: [],
message: null,
isBlocked: false
}
},
methods: {
send() {
if (this.message) {
this.pushToChats(this.message);
axios.post('/dashboard/chats/messages/store', {
session_id: this.friend.session.id,
message: this.message,
to_user_id: this.friend.id
});
this.message = null;
}
},
pushToChats(message) {
// this.getAllMessages();
console.log(this.chats);
console.log(typeof this.chats);
this.chats.push({message: message, type: false, send_at: 'Just Now'});
},
close() {
this.$emit('close');
},
clear() {
this.chats = [];
},
block() {
this.isBlocked = true
},
unblock() {
this.isBlocked = false;
},
getAllMessages() {
axios.post(`/dashboard/chats/session/${this.friend.session.id}`)
.then(res => this.chats = res.data
);
}
},
created() {
this.getAllMessages();
Echo.private(`Chat.${this.friend.session.id}`).listen('PrivateChatEvent', (e) => {
this.chats.push({message: e.content, type: true, send_at: 'Just Now'})
});
},
name: "MessageComponent"
}
</script>
<style scoped>
.chat-box {
height: 400px;
}
.card-body {
overflow-y: scroll
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

Related

Get value from multiple checkbox and input to an array

How we can get the value of checkbox an input to an array?
Here is my code, I've tried this:
<cbComp
v-for="name in names"
v-bind:key="name"
v-model="selected_name[name]">
<span> {{ name }} </span>
</cbComp>
Name: {{ selected_name }}
data () {
return {
names: ['Michael', 'Millie', 'Brown'],
selected_name: []
}
},
This was the cbComp template:
<template>
<label class="checkbox">
<input
:id="cb"
type="checkbox"
:value="modelValue"
v-model="computedValue"
>
<label
:for="cb">
<slot/>
</label>
</label>
</template>
This is the javascript:
export default {
data: {
updateValue: this.value
},
props: {
value: undefined,
modelValue: {
type: undefined,
default: false
}
},
computed: {
computedValue: {
get () {
return this.updateValue
},
set (value) {
this.updateValue = value
this.$emit('input', value)
}
}
},
watch: {
value (value) {
this.updateValue = value
}
}
}
It turns out there is no error, and the selected name won't appear on {{ selected_name }}
You simply need to bind your v-model to the selected_name property. Here is a bare minimum example for you to work off of:
<html>
<div id="app">
<p>Names: {{ selected_name.join(', ') }}</p>
<label for="maxCheckbox">Max</label>
<input type="checkbox" value="Max" v-model="selected_name" id="maxCheckbox">
<label for="markCheckbox">Mark</label>
<input type="checkbox" value="Mark" v-model="selected_name" id="markCheckbox">
<label for="johnCheckbox">John</label>
<input type="checkbox" value="John" v-model="selected_name" id="johnCheckbox">
</div>
<script src="https://unpkg.com/vue#2"></script>
<script>
new Vue({
el: '#app',
data() {
return {
selected_name: []
}
}
});
</script>
</html>
As a side note, I would probably rename your selected_name to be more clear like selected_names or more commonly you'll find selectedNames camelCase.

Simple List Rendering in Vue with finding Index and passing props

so I do the beginning todo list stuff. I have this array
state() {
return {
news: [
{
id: 1,
title: "Titel 1",
text: "Beispieltext 1"
},
{
id: 2,
title: "Titel 2",
text: "Beispieltext 2"
}
]
}
},
I want to list items (NewsItem) in a list (NewsItemList) for every entry in the news-array. As simple as that.
This is my NewsItem
<template>
<div class="newsitem">
<h1
v-for="nachricht in news"
:key="nachricht.id"
>{{nachricht.title}}</h1>
<p
v-for="nachricht in news"
:key="nachricht.id"
>{{nachricht.text}}</p>
</div>
</template>
<script>
export default {
data() {
return {};
}
};
</script>
And this is my NewsItemList
<template>
<ul>
<li
v-for="nachricht in news"
:key="nachricht.id"
>
<NewsItem />
</li>
</ul>
</template>
<script>
import NewsItem from "#/components/NewsItem";
export default {
components: {
NewsItem,
},
computed: {
news() {
return this.$store.getters.getNewsList;
}
}
};
</script>
I want to map through my array in NewsItemList and give the information as props to my NewsItem. What is the simplest way?
In React, I needed to find the index with the findIndex() function. How can I do this in vue?
As I am just beginning, could you help me to find a simple way? Thank you!
Props
NewsItem
<template>
<div class="newsitem">
<h1>{{ title }}</h1>
<p>{{ text }}</p>
</div>
</template>
<script>
export default {
props: {
title: String,
text: String,
},
data() {
return {}
},
}
</script>
Now you can use that in your NewsItemList
<template>
<ul>
<li v-for="nachricht in news" :key="nachricht.id">
<NewsItem :title="nachricht.title" :text="nachricht.text"/>
</li>
</ul>
</template>
If I understood you correctly, you just need to pass prop with news item object :
Vue.component('NewsItem', {
template: `
<div class="newsitem">
<h1 >{{item.title}}</h1>
<p>{{item.text}}</p>
</div>
`,
props: ['item']
})
new Vue({
el: "#demo",
data() {
return {
news: [
{
id: 1,
title: "Titel 1",
text: "Beispieltext 1"
},
{
id: 2,
title: "Titel 2",
text: "Beispieltext 2"
}
]
}
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<ul>
<li
v-for="nachricht in news"
:key="nachricht.id"
>
<news-item :item="nachricht"></news-item>
</li>
</ul>
</div>

V-for not working when building array from external JSON file

Javascript:
// List of Products
const productsJSON = 'json/products.json';
// Component - Product Select
app.component('product-select', {
data() {
return {
selected: '',
options: []
}
},
template: `
<p v-for="(option, index) in options">test</p>
<div class="ui fluid labeled multiple search selection dropdown">
<input type="hidden"
name="products"
v-model="selected"
#change="selectProducts">
<i class="dropdown icon"></i>
<div class="default text">Select Products</div>
<div class="menu">
<div v-for="(option, index) in options"
class="item"
v-bind:data-value="option.name">
{{ option.name }}
</div>
</div>
</div>
`,
methods: {
selectProducts(event) {
this.selected = event.target.value.split(',');
console.log(this.selected);
}
},
beforeMount() {
const jsonResults = [];
this.options = jsonResults;
$.getJSON(productsJSON, function (data) {
jsonResults.push(...data);
});
console.log(jsonResults);
console.log(this.options)
}
});
I'm simply trying to populate the options: [] array with the array of objects returned from the JSON file in the $.getJSON function. Here is what the JSON file looks like:
[
{
"name": "White Gummy",
"value": "White Gummy"
},
{
"name": "Red Gummy",
"value": "Red Gummy"
},
{
"name": "Blue Gummy",
"value": "Blue Gummy"
}
]
My v-for is returning absolutely nothing, and the results of my two console.log functions are as follows:
Does anyone have any idea on what I'm doing wrong or if there is a better way to populate my array with the external .json file?
#Luckyfella provided a solution, which can be found in the created() lifecycle hook below:
// Component - Product Select
app.component('product-select', {
data() {
return {
selected: '',
options: null
}
},
template: `
<div class="ui fluid labeled multiple search selection dropdown">
<input type="hidden"
name="products"
v-model="selected"
#change="selectProducts">
<i class="dropdown icon"></i>
<div class="default text">Select Products</div>
<div class="menu">
<div v-for="(option, index) in options"
class="item"
v-bind:data-value="option.name">
{{ option.name }}
</div>
</div>
</div>
`,
methods: {
selectProducts(event) {
this.selected = event.target.value.split(',');
console.log(this.selected);
}
},
created: function () {
fetch(productsJSON)
.then(r => r.json())
.then(options => {
this.options = options;
});
}
});

Filter object array with pipe Angular 2

I have a
class:
export class Todo {
public id: number;
public name: string;
public isCompleted: boolean;
public dateCreated: Date;
public userName: string;
}
A service:
getTodos(): Observable < Todo[] > {
return this.http.get(this.todosUrl)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response) {
let body = res.json();
return body || {};
}
In my component:
getTodos(){
this.todoService.getTodos()
.subscribe(
todos => this.todos = todos,
error => this.errorMessage = <any>error
);
}
And html file:
<div class="ui large selection animated divided list">
<a *ngFor="let todo of (todos | todoFilter:false)" class="item">
<div class="right floated content">
<div class="ui vertical animated negative button" tabindex="0">
<div class="hidden content">Delete</div>
<div class="visible content">
<i class="fa fa-trash" aria-hidden="true"></i>
</div>
</div>
</div>
<i class="minus square outline icon"></i>
<div class="content">
<div class="header">{{todo.name}}</div>
<div class="description">{{todo.dateCreated | date:"MMM-dd-yyyy"}}</div>
</div>
</a>
</div>
The problem is, when I try to use this pipe to filter the completed todos, I keep getting an error that say Cannot read property filter of undefined.
Did I do something wrong or are there any ways to filter it without using an pipe?
My pipe:
transform(allTodos: Todo[], args?: boolean){
if (allTodos === null) {
return null;
}
return allTodos.filter(todo => todo.isCompleted);
}
Thank you.
Try to replace the if (allTodos === null) to just if (!allTodos)
I think the problem is that you're getting to the .filter even while your this.todos is still empty since you're only checking that it isn't null.

polymer 1.0 dom-repeat render not updating dom

I have an element which renders a array of objects using dom-repeat. There is also a feature to modify a particular object. However, after updating the object, even if the array is updated, the dom does not reflect the change (render is called as well). I am using Polymer provided mutation function (this.slice, have a look in the _commitChange function below). Can someone point out what is wrong here?
One thing to note is that individual items in the array are objects rather than primitives /
<dom-module id="em-person-qualifications">
<template>
<style include="shared-styles"></style>
<style>
:host {
display: block;
}
iron-pages {
height: 100%;
}
</style>
<iron-pages id="container" selected="0">
<div id="listContainer">
<template id="listTemplate" is="dom-repeat" items="[[data]]">
<paper-item>
<paper-item-body two-line>
<div>
<span>[[item.qualificationLevel.level]]</span>,
<span>[[item.qualificationLevel.levelCategory]]</span>
</div>
<div secondary>
<span>[[item.fromInstitute.edumateOrgDisplayName]]</span>
<span>[[item.status]]</span>
</div>
</paper-item-body>
<paper-icon-button icon="more-vert" on-tap="_loadModifyUI"></paper-icon-button>
</paper-item>
</template>
</div>
<div id="detailContainer">
<paper-toolbar>
<div class="title">
<us-com-i18n key="qualificationDetail"></us-com-i18n>
</div>
<paper-icon-button id="button" icon="clear" on-click="_showList"></paper-icon-button>
</paper-toolbar>
<div>
<iron-label for="levelContainer">
<us-com-i18n key="level"></us-com-i18n>
</iron-label>
<div id="levelContainer" class="layout horizontal wrap">
<us-com-freeorref data="{{currItem.qualificationLevel.level}}"></us-com-freeorref>
<paper-dropdown-menu label="select">
<paper-menu class="dropdown-content" attrForSelected="data-value" selected="{{currItem.qualificationLevel.levelCategory}}">
<paper-item data-value="10">Graduate</paper-item>
<paper-item data-value="11">Post-graduate</paper-item>
</paper-menu>
</paper-dropdown-menu>
</div>
<iron-label for="majorSubjectContainer">
<us-com-i18n key="majors"></us-com-i18n>
</iron-label>
<div id="majorSubjectContainer">
<template is="dom-repeat" items="[[currItem.majorSubjects]]">
<em-party-subject data="{{item}}"></em-party-subject>
</template>
</div>
<iron-label for="minorSubjectContainer">
<us-com-i18n key="minors"></us-com-i18n>
</iron-label>
<div id="minorSubjectContainer">
<template is="dom-repeat" items="[[currItem.minorSubjects]]">
<em-party-subject data="{{item}}"></em-party-subject>
</template>
</div>
<iron-label for="during">
<us-com-i18n key="during"></us-com-i18n>
</iron-label>
<div class="layout horizontal wrap" id="during">
<span><us-com-i18n key="from"></span>
<us-com-date data="{{currItem.fromMonthYear}}"></us-com-date>
<span><us-com-i18n key="to"></span>
<us-com-date data="{{currItem.toMonthYear}}"></us-com-date>
</div>
<paper-input type="text" value="{{currItem.status}}" label="status">
<us-com-formattedlongtext data="{{currItem.achievmentStatement}}" label="achievements"></us-com-formattedlongtext>
<div class="buttons">
<paper-button tabindex="0" raised autofocus on-click="_commitChange">
<iron-icon icon="check"></iron-icon>Ok
</paper-button>
</div>
</div>
</div>
</iron-pages>
</template>
<script>
(function() {
'use strict';
Polymer({
is: 'em-person-qualifications',
properties: {
data: {
type: Array,
value: function() {
return [{
'qualificationLevel': {
'level': 'Bsc',
'levelCategory': 10
},
'majorSubjects': [],
'minorSubjects': [],
'fromMonthYear': {},
'toMonthYear': {},
'fromInstitute': {
'edumateOrgDisplayName': 'XYZ College'
},
'status': 'ABCD',
'achievmentStatement': {}
}, {
'qualificationLevel': {
'level': 'M-Tech',
'levelCategory': 11
},
'majorSubjects': [],
'minorSubjects': [],
'fromMonthYear': {},
'toMonthYear': {},
'fromInstitute': {
'edumateOrgDisplayName': 'ABC College'
},
'status': 'EFGH',
'achievmentStatement': {}
}];
}
},
currItem: Object,
currIndex: Number,
currChange: String
},
behaviors: [app.I18nBehavior],
_showList: function() {
this._commitChange();
//this.$.container.selected = '0';
},
_loadAddUI: function() {
this.currChange = 'A';
this.currItem = {};
this.$.container.selected = '1';
},
_loadModifyUI: function(e) {
this.currChange = 'M';
this.currItem = e.model.item;
this.currIndex = this.data.indexOf(e.model.item);
this.$.container.selected = '1';
},
_loadDeleteUI: function() {
this.currChange = 'D';
//Find out the index from the data-index attribute of the button
this.currIndex = 0;
},
_commitChange: function() {
//var model = e.model;
//this.splice('data',)
if (this.currChange === 'A') {
this.push('data', this.currItem);
} else if (this.currChange === 'M') {
this.splice('data', this.currIndex, 1, this.currItem);
console.log('data[this.currIndex] = ' + this.data[this.currIndex].status);
} else if (this.currChange === 'D') {
this.splice('data', this.currIndex, 1);
}
this.$.listTemplate.render();
this.$.container.selected = '0';
}
});
})();
</script>
</dom-module>
Strings as I see in most examples.
I'm on a huge Polymer 1.9 project, and is currently switching to Polymer 2.0, and I can tell that things like updating a dom-repeat is handled in a better way in Polymer 2.
Updating subproperties in a dom-repeat is a nightmare in 1.0, and the simplest solution is to override dirty checking, where Polymer is checking every single property to see if the DOM needs to update.
var copiedData = JSON.parse( JSON.stringify(data) );
this.set('data', []);
this.set('data', copiedData);
If you're desperate, use this method, but it can create a performance issue on phones or if the list is over about 100 items. You could probably also just do ...
this.set('data', JSON.parse( JSON.stringify(data) ));
... as a totally new array will override dirty checking. Anyways, a dom-repeat wont update with this.splice if the length of the array doesn't change, so I would do something like this.set('data.' + this.currIndex, this.currItem) to update the subproperty directly instead of the splicing that occurs here:
_commitChange: function() {
// ...
} else if (this.currChange === 'M') {
this.splice('data', this.currIndex, 1, this.currItem);
Also, you don't need this as Polymer should update automatically:
this.$.listTemplate.render();

Resources