Vuejs: watching state variable from component - javascript

I have a global variable that is populated with an API call when a component is mounted.
I also have a chart component that I would like to show if that variable is not null (i.e. has the request has finished and it has been populated).
At the moment to render the chart I am using this:
<template>
<div class="container">
<b-chart
v-if="$store.state.lists[api_key] != null"
:chartdata="$store.state.lists[api_key]"
:options="options"
/>
</div>
</template>
I have tried moving this check $store.state.lists[api_key] != null to computed or watch, to minimise the inline scripting, but I can't seem to get it to work. Would someone please show me how.

Try this:
computed: {
canShowChart() {
return this.$store.state.lists[this.api_key] != null;
}
}
<b-chart
v-if="canShowChart"
:chartdata="$store.state.lists[api_key]"
:options="options"
/>

Since null values are interpreted as "falsy", and assuming you have an "api_key" data variable, you can use it this way:
computed: {
chartData() {
return this.$store.state.lists[this.api_key]
}
}
<template>
<div class="container">
<b-chart
v-if="chartData"
:chartdata="chartData"
:options="options"
/>
</div>
</template>

Related

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

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.

Vue getter returns undefined when page reload

I have a blog with some posts. When you click on the preview you will redirect on the page post.
On the page of the post, I use a getter to load the correct post (I use the find function to return object.name which corresponds to the correct object in the array of objects).
const state = {
ricettario: [], // data that contains all recipes (array of objects)
}
const actions = {
// Bind State and Firestore collection
init: firestoreAction(({ bindFirestoreRef }) => {
bindFirestoreRef('ricettario', db.collection('____').orderBy('data'))
})
const getters = {
caricaRicetta(state) {
console.log('Vuex Getter FIRED => ', state.ricettario)
return nameParamByComponent => state.ricettario.find(ricetta => {
return ricetta.name === nameParamByComponent
})
}
}
In the component, I call the getter in the computed property
computed: {
...mapGetters('ricettaStore', ['caricaRicetta']),
ricetta() {
return this.caricaRicetta(this.slug) // this.slug is the prop of the URL (by Router)
}
}
Anything goes in the right way but when I reload the page in the POST PAGE, the getter will fire 2 times:
1. return an error because the state is null
2. return the correct object
// screen below
So everything works fine from the front but not at all in the console and in the App.
I think the correct way is to call the getters in the created hook. What I've to change? It is a problem with the computed prop, getters or state?
POST PAGE:
<template>
<div v-if="ricetta.validate === true" id="sezione-ricetta">
<div class="container">
<div class="row">
<div class="col s12 m10 offset-m1 l8 offset-l2">
<img
class="img-fluid"
:src="ricetta.img"
:alt="'Ricetta ' + ricetta.titolo"
:title="ricetta.titolo"
/>
</div>
</div>
</div>
</div>
<div v-else>
...
</div>
</template>
You are trying to validate undifined property. So you need to check ricetta first.
Try like this:
<div v-if="ricetta && ricetta.validate === true" id="sezione-ricetta">
Database synchronization is asynchronous, ricettario is initially an empty array. Computed value is recomputed once synchronization is finished and ricettario array is filled, the component is updated.
Even if ricettario weren't empty, find may return undefined if it finds nothing. This needs to be handled where ricetta is used:
<div v-if="ricetta && ricetta.validate" id="sezione-ricetta">
The error log is quite explicit, there is a xxx.validate somewhere in your Ricetta component template, but that xxx is undefined.
Because of this, your app crashes and stops working. I doubt it has anything to do with Vuex

VueJS/Laravel - Sharing props between Laravel and Vue

I have defined a component called EditorNavigation.vue like so:
<template>
<div>
<ul>
<li v-bind:class="{'is-active':(active === field.id)}" v-for="field in fields">
<a :href="'/streams/' + stream_token + '/fields/' + field.id">{{field.name}}</a>
</li>
</ul>
<div>
</template>
<script>
export default {
props: ["fields", "active", "stream_token"],
created() {
this.fields = JSON.parse(this.fields);
this.active = JSON.parse(this.active);
this.stream_token = JSON.parse(this.stream_token);
}
};
</script>
As you can see in my component, I need three variables:
Fields (array of all fields)
An unique token for a specific resource
The current active field id (so I can set the is-active class).
In my Laravel view file, I use the component like this:
show.blade.php
<editor-navigation fields="{{ json_encode($stream->fields) }}" active="{{ json_encode($field->id) }}" stream_token="{{ json_encode($field->stream->token) }}"></editor-navigation>
So above code works fine, however it feels a bit "messy" - since I need to use the editor-navigation component in a lot of pages, and I am wondering what will happen as soon as I need another variable sent to it - I have to update it in all places.

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>

Render a component within another in React

When a state is changed, React triggers componentDidUpdate() method, and by then I do:
componentDidUpdate: function () {
React.render(new SubmitButton, $('.uploader-submit').get(0));
}
As you saw, I'm rendering a SubmitButton when a specific state is changed, but my question is: is this the best behavior to get this feature done?
My scenario is: I'm uploading a photo. When the input[type=file] is changed, I create a new state property and then the componentDidUpdate() is triggered, invoking the SubmitButton.
This is my render() method:
render: function () {
return (
<div className="uploader">
<header className="uploader-header">
<div className="uploader-actions pull-left">
<div className="uploader-submit"></div>
<CancelButton router={this.props.router} />
</div>
<UploadButton callback={this.imageSelectedCallback} />
</header>
<Preview imageUri={this.state.imageUri} />
</div>
)
}
Couldn't I do something like the <Preview /> component? I mean, it is there, but something just appears when this.state.imageUri is different of null. This is the implementation of Preview:
var Preview = {
render: function () {
return (
<img src={this.props.imageUri} />
)
}
};
module.exports = React.createClass(Preview);
Yes, I know — "Preview" is invisible by default because it is an image, but I want to know if there's another approach to reach what I want: to show something based on a state, using the render method.
React doesn't render falsy values, be it a component or an attribute (like in the Preview case), e.g.
<div>{null}</div>
<img src={null} />
renders to
<div></div>
<img/>
So typically you just create a variable and conditionally assign it a component or null as was also suggested in another answer:
var button = null;
if(myConditionForShowingButton) {
button = <SubmitButton />;
}
-- or simply --
var button = myConditionForShowingButton ?
<SubmitButton /> :
null;
In cases where the component gets bigger it's typically more readable and cleaner to have a subroutine for rendering that part
var complexComponent = condition ?
this.renderComplexComponent() :
null
Yes. If-Else in JSX.
render: function () {
var submitButton;
if (this.state.imageSelected)
submitButton = <SubmitButton />;
return (
<div className="uploader">
<header className="uploader-header">
<div className="uploader-actions pull-left">
<div className="uploader-submit">{ submitButton }</div>
<CancelButton router={this.props.router} />
</div>
<UploadButton callback={this.imageSelectedCallback} />
</header>
<Preview imageUri={this.state.imageUri} />
</div>
)
}

Categories