apply function from methods to reverse string in paragraph in vue.js - javascript

Dears, I have tried to apply function to reverse string in paragraph text in vue.js,
I have created function to reverse words in methods called (reverseword) and added it card using :rule="reverseword()",but it does not work. your support is highly appreciated
Code:
<div class="post-box">
<span class="post-viwes">{{viwes}}</span>
<h3 class="post-title">{{title}}</h3>
<span class="post-date">{{date}}</span>
<p class="post-content">{{content}}</p>
<div class="row">
<div class = "col-sm-6 text-right">
<span class="post-author">{{author}} </span>
</div>
<div class = "col-sm-6 text-right" :rules="reverseword()">
<span class="post-category" >{{category.toUpperCase()}}</span>
</div>
</div>
)
</div>
</template>
<script>
export default {
props:["viwes","title","date","content","author","category"],
name:"posts",
methods: {
reverseWord: function () {
this.category = this.category.split('').reverse().join('')
}
}};
</script>```

Your reverseWord method attempts to mutate a prop (category).
You can't mutate a prop, because the mutation would be overwritten by the first update from the parent.
If/when you do want/need to change a prop value, you have do it in the parent component which will then pass the change down to the child component, through the props binding.
Updating the parent from child can be done by
either using $emit
or by using a store, external to both the child and the parent.
If, in fact, you don't want to mutate category, you just need to be able to use its reverse inside the template, create a computed property:
computed: {
reversedCategory() {
return this.category.split('').reverse().join('');
}
}
Use it in template as you would use a normal property:
<div class = "col-sm-6 text-right" :rules="reversedCategory">
<span class="post-category" >{{category.toUpperCase()}}</span>
</div>
The computed is reactive. Meaning every time category changes, reverseCategory will update accordingly.

Related

Proper way of object copy from one element to another in Vue.js

I am new to Vue.js (I mostly use PHP) and I am trying to creating simple view where user can add an object from one component and place it's copy into another component.
Main template
<template>
<div class="left">
<TaskList :tasks="tasks" v-on:pinned-add-task="pinnedAddTask" />
</div>
<div class="right">
<PinnedList :pinned="pinned" />
</div>
</template>
TaskList
<template>
<div class="task-list">
<div v-for="task in tasks" :key="task.id">
<TaskItem :task="task" v-on:pinned-add-task="$emit('pinned-add-task', task)" />
</div>
</div>
</template>
TaskItem
<template>
<div>
<p>{{task.name}}</p>
<button v-on:click="$emit('pinned-add-task', task)">+</button>
</div>
</template>
And as far as I am aware object "task" is passed by reference and when I try to create an empty object or an array and insert "task" into that newly created object/array when I change original "task" it is also being changed inside that new object and I don't want that.
I am getting my data (tasks) from API that I have created and I am using pagination system so I want to be able to switch pages without losing it from the pinned page.
I created code which looks like this but I don't like it and I don't think that's a good way to do this:
pinnedAddTask(item) {
let pQuantity = 1; // I use this value because I want to be able to pin one task multipletimes
let left = this.pinned;
let right = [];
for (let task of this.pinned) {
if(item.id == task.id) {
pQuantity = task.quantity + 1;
left = this.pinned.filter(eItem => eItem.id < item.id);
right = this.pinned.filter(eItem => eItem.id > item.id);
}
}
const clone = {...item, quantity: pQuantity};
this.pinned = [...left, clone, ...right];
}
Can anyone confirm or reject this?
Yes this one is fine if thats just a shallow copy [ level-one object].
But if you are having a nested object then you might have to use recursive methodology or use any external libary like lodash

Shorter way to assign emitted value to data

I pass my simple data from child to parent and I'm just wondering if I could omit to create a method for assigning this value to data? I would like to be able to assign it directly in the template.
My code is:
Child
<h1 #click="$emit('title', 'Home')" />
Parent
<div #title="onTitleChange" />
onTitleChange(newTitle) {
this.title = newTitle;
}
I would like to see something like this:
Child
<h1 #click="$emit('title', 'Home')" />
Parent
<div #title="title = value" />
You can access the emitted event’s value with $event. So, just a minor change in your parent.
<div #title="title = $event" />
Reference: https://v2.vuejs.org/v2/guide/components.html#Emitting-a-Value-With-an-Event

Vue - dynamically create divs that use e.g v-on:click

I have something like that:
<div v-if="dataIsLoaded" v-for="(line, index) in json.data" v-on:dblclick="edit(index)" v-html="test(index)">
</div>
and test(index) returns html-ish string:
<div id=${index} v-on:click="remove(index)"></div>
How can I make it work?
My goal is:
If dataIsLoaded == true, then foreach (line, index) in json.data perform
test(index) and return its output to that container and display it as html.
Meanwhile output from test(index) can have different functions/events associated with them defined via string (as I showed above).
Why are you using v-html here? v-html should really only be needed in very specific and limited situations; I don't think it applies here. The string <div id=${index} v-on:click="remove(index)"></div> is not plain HTML, it's a Vue template string, so it won't work. v-html is also susceptible to XSS attacks; all in all I say avoid it like the plague.
Do something like this instead:
<template v-if="dataIsLoaded">
<div v-for="(line, index) in json.data" #dblclick="edit(index)">
<!-- Type1 -->
<div v-if="line.type === 'Type1'" :id="index" #click="remove(index)"></div>
<!-- Type2 -->
<div v-else-if="line.type === 'Type2'">...</div>
</div>
</template>
I've added the type property on each line object as a discriminator to determine which template should be used for the line.
Also I've hoisted the v-if="dataIsLoaded" into a <template> above the div because v-if is evaluated for each div generated by the v-for and the condition doesn't depend on the children so it needn't be repeated for each child (a minor optimization).
If you don't like the idea of having lots of v-if and v-else-if (a sort of "switch" statement in the template) then you can use <component :is="..."> instead:
<div v-for="(line, index) in json.data" #dblclick="edit(index)">
<component :is="line.type" :id="index"/>
</div>
import Type1 from './type1.vue'
import Type2 from './type2.vue'
export default {
components: {
Type1,
Type2
}
}
It's not possible with v-html, as the doc states
Updates the element’s innerHTML. Note that the contents are inserted
as plain HTML - they will not be compiled as Vue templates
A more component-thinking solution is to create a component(s) with needed behaviour and paste them as a results of v-for:
// componentWithOnClick.js
<script>
export default {
name: 'ComponentWithOnClick',
methods: {
remove() {
//
}
}
</script>
<template>
<div id=${index} v-on:click="remove(index)"></div>
</template>
and then use it in the parent file:
//app.js
import ComponentWithOnClick from './ComponentWithOnClick.vue'
{
components: {
ComponentWithOnClick
}
//
<div v-if="dataIsLoaded" v-for="(line, index) in json.data" v-on:dblclick="edit(index)">
<ComponentWithOnClick></ComponentWithOnClick>
</div>

Changing the attribute of each element in a v-for array

I am attempting to figure things out with Vue way of writing javascript.
Considering this scenario:
at .vue template
<button v-on:click="biggerFont()" class="btn btn-s btn-default" type="button" name="button">A</button>
<div v-for="(tweet, index) in tweets">
<div class="each_tweet">
<textarea v-on:keyup="typing(index)"
v-model="tweet.content"
placeholder="What's happening now">{{ tweet.content }}</textarea>
</div>
</div>
at .vue <script>
export default {
methods: {
biggerFont: function() {
//can I somehow use this.tweets.style.fontSize to influence all the fontSize?
document.getElementsByClassName("each_tweet").style.fontSize = "xx-large"
}
}
}
My question is:
how do I go about changing the font-size of each value of the textarea in tweets? Is there a default Vue way of influencing these fontSize?
I tried and failed using document.getElementsByClassName.style.fontSize and it does not seems to be the Vue way. Thank you!
I believe the Vue way of doing this is using class and style bindings. For example:
<textarea v-bind:class="{ big: enlarge}">{{item}}</textarea>
https://jsfiddle.net/e064m859/
document.getElementsByClassName method returns a nodeList so you have to access a DOM element using its index.
export default {
methods: {
biggerFont: function() {
var tweets=document.getElementsByClassName("each_tweet");
for(var i=0;i<tweets.length;i++){
tweets[i].style.fontSize = "xx-large";
}
}
}
}

Ember Js computed property in component fails when observing object property in service

Have an interesting problem in Ember js.
Here below is the component advanced-search/component.js with two computed properties roomType & room dependent on the service services/advanced-search queryObj.{roomId,roomTypeId} object properties.
The roomType computed property fires and updates the template correctly when an option is selected from the template. However, interestingly, the room computed property fails to fire when a room is selected from the template. This I verified by putting a console.log('checking room computed fired') inside the room computed property.
To explore this anomaly further, I did the following:
I uncommented the code you see in the init method that sets the rooms array which populates the room list dropdown and commented the code in the actions hash inside the setRoomType action method that was initially setting the rooms array. After these changes, the room computed property fires correctly and updates the template.
I noticed the array returned by the this.get('store).findAll('roomType') resulted in the roomType computed property to fire correctly and update the template, so I attempted to change the call for rooms inside setRoomType from roomType.get('rooms') to this.get('store') to see if it also resulted in the room computed property to fire correctly but it still did NOT fire the room computed property. So, I concluded that both the roomType and room computed properties could only fire and update the template correctly if their dropdown list arrays were set in the component's init method.
advanced-search/component.js
export default Ember.Component.extend({
advancedSearch: Ember.inject.service('advanced-search'),
queryObj: Ember.computed.alias('advancedSearch.queryObj'),
init() {
this._super(...arguments);
// I believe because the 'roomTypes` array is set in this init method, the 'roomType' computed property fires correctly and updates the template
this.get('store').findAll('roomtype').then((roomTypes) => {
this.set('roomTypes', roomTypes);
});
// When the 'rooms' array is also initialized here, the 'room' computed property fires successfully on 'room' selection from the dropdown and updates the template
// this.get('store').findAll('room').then((rooms) => {
// this.set('rooms', rooms);
// });
},
roomTypes: [],
roomType: Ember.computed('queryObj.{roomTypeId}', function() {
var that = this;
return this.get('roomTypes').find(function(roomType) {
return that.get('queryObj').roomTypeId === roomType.id;
});
}),
rooms: [],
room: Ember.computed('queryObj.{roomId}', function() {
console.log('checking room computed fired')
var that = this;
return this.get('rooms').find(function(room) {
return that.get('queryObj').roomId === room.id;
});
}),
actions: {
setRoomType(roomType) {
this.get('advancedSearch').setRoomType(roomType);
this.set('room', null);
if (roomType) {
// When rooms array initialized from here the room computed property fails to fire on room selection from drop down
roomType.get('rooms').then((rooms) => {
this.set('rooms', rooms);
});
} else {
this.set('rooms', null);
}
},
setRoom(room) {
this.get('advancedSearch').setRoom(room);
}
}});
Here below is the service code:
services/advanced-search
export default Ember.Service.extend({
queryObj: {},
setRoomType(roomType) {
this.set('queryObj.roomTypeId', roomType.id);
},
setRoom(room) {
this.set('queryObj.roomId', room.id);
}});
Here below is the component template:
advanced-search/template.hbs
<div class="col-md-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3>Advanced Search</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-2">
{{#power-select placeholder="Room type" allowClear=true selected=roomType options=roomTypes onchange=(action "setRoomType") as |roomType| }} {{roomType.name}} {{/power-select}}
</div>
<div class="col-md-2">
{{#power-select placeholder="Room No" allowClear=true selected=room options=rooms onchange=(action "setRoom") as |room| }} {{room.name}} {{/power-select}}
</div>
</div>
</div>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
NB: My desire is for the 'rooms' property to be set from the action method 'setRoomType' through the 'roomType' model's relationship property called 'rooms' so that the rooms are always filtered according to the 'roomType' selected.
Your help will be greatly appreciated.
Ember Version:
Ember Inspector 2.0.4
Ember 2.8.2
Ember Data 2.9.0
jQuery 3.1.1
Ember Simple Auth 1.1.0
You should not set the computed properties like this.set('room', null); This line of code is the culprit i guess.Once you set computed properties like this then this computed property will not fire again.
What you might need is room computed property with get and set. refer setting computed properties guide
test:Ember.computed('dependantKey',{
get(key){
return 'result';
},
set(key,value){
return value;
}
})

Categories