I need to set some data from component to the store
methods: { // my component
...mapMutations('lists', ['setContactListName']),//import function from the store
viewHandler (id, item) { // hadler
this.$router.push(`/company/sample-contact/${id}`);
this.setContactListName(item); // pass data to the function
}
}
state() { // store
return {
contactListName: {}
};
},
mutations:{
setContactListName (state, payload) {// handler
state.contactListName = payload;
}
}
After i clicked nothing happend, even there are no errors in the console
methods: { // my component
...mapMutations(['setContactListName']), //import function from the store
viewHandler(id, item) { // hadler
this.$router.push(`/company/sample-contact/${id}`);
this.setContactListName(item); // pass data to the function
}
}
or using this.$store.commit
methods: { // my component
viewHandler(id, item) { // hadler
this.$router.push(`/company/sample-contact/${id}`);
this.$store.commit('setContactListName', item);
}
}
Related
I have component MyComponent.vue where I have data value that constantly changes. I want to pass this value to javascript file(js file should know about changes of value everytime)
Why do I do that? Because my regular js file is a service layer for axios methods. I can import this file in many other components. The file contains axios methods and urls are dynamic.
I want those urls depend on data variable. This data variable comes from MyComponent.js
So the main goal is to make dynamic urls of axios that depend on data variable
I tried some code but it doesn't work, because js file(CategoryService.js) know nothing about this.categoryNumber.
MyComponent.vue:
<script>
export default {
data() {
return {
categoryNumber: 1
}
}
}
</script>
CategoryService.js
import http from "../../../http-common";
let category = "category1";
if (this.categoryNumber === 1) {
category = "category1";
} if (this.categoryNumber === 2) {
category = "category2";
}
class CategoryService {
get(id) {
return http.get(`/${category}/${id}`);
}
update(id, data) {
return http.put(`/${category}/${id}`, data);
}
create(data) {
return http.post(`/${category}`, data);
}
delete(id) {
return http.delete(`/${category}/${id}`);
}
getAll() {
return http.get(`/${category}/all`);
}
}
export default new CategoryService();
So with a bit of refactoring, you could easily get this working.
First of all, I would put the if/else logic of your class into it.
For convenience and scalability, I would use a Vuex store that will keep track of your categoryNumber and share it accross all your components.
Then I would bind my service to my Vue instance so I can easily access it in all my components as well as the store and I would pass the latter to my class as a parameter.
For the last part, I don't know the logic in the http-common file so the code I will show you is a bit nasty. But depending on wether or not you bound 'http' to axios, you could make use of axios interceptors to call the getCategoryNumber() method in every request.
Here's an idea of the implementation I would go for:
const CategoryService = class CategoryService {
constructor(store) {
this._store = store;
this.category = "category1";
}
getCategoryNumber() {
if (this._store.state.categoryNumber === 1) {
this.category = "category1";
}
if (this._store.state.categoryNumber === 2) {
this.category = "category2";
}
console.log(this.category); // for demo puprose
}
get(id) {
this.getCategoryNumber(); // We could use axios request interceptor instead of calling that in every route, but that works !
return http.get(`/${this.category}/${id}`);
}
update(id, data) {
this.getCategoryNumber();
return http.put(`/${this.category}/${id}`, data);
}
create(data) {
this.getCategoryNumber();
return http.post(`/${this.category}`, data);
}
delete(id) {
this.getCategoryNumber();
return http.delete(`/${this.category}/${id}`);
}
getAll() {
this.getCategoryNumber();
return http.get(`/${this.category}/all`);
}
}
const store = new Vuex.Store({
state: {
categoryNumber: 1
},
mutations: {
setCategoryNumber(state, payload) {
state.categoryNumber = payload;
}
}
});
// Bind your service to the Vue prototype so you can easily use it in any component with 'this.$service'
// pass it the store instance as parameter
Vue.prototype.$service = new CategoryService(store);
new Vue({
el: "#app",
store, // dont forget to bind your store to your Vue instance
methods: {
updateCategoryNumber() {
// Put here any logic to update the number
this.categoryNumber = this.categoryNumber === 1 ? 2 : 1;
this.checkServiceCategoryValue();
},
checkServiceCategoryValue() {
// for demonstration purpose
this.$service.getCategoryNumber();
}
},
computed: {
// Look for the store value and update it
categoryNumber: {
get() {
return this.$store.state.categoryNumber;
},
set(value) {
this.$store.commit("setCategoryNumber", value);
}
}
}
});
<div id="app">
<h2>number: {{ categoryNumber }}</h2>
<button type="button" #click="updateCategoryNumber()">
updateCategoryNumber
</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#2.0.0"></script>
Thanks to #Solar
I just added one more parameter for all urls and put the number of category to it
CategoryService.js:
class CategoryOneService {
get(id, category) {
return http.get(`/${category}/${id}`);
}
getAll(category) {
return http.get(`/${category}/all`);
}
}
functions.js:
let catNum = "";
function getQuestion() {
if (this.categoryNumber === 1) {
catNum = "category1";
}
if (this.categoryNumber === 2) {
catNum = "category2";
}
let questionId = this.questionNumber;
CategoryOneService.get(questionId, catNum)
.then(response => {
this.question = response.data.question;
this.answer = response.data.answer;
})
.catch(error => {
console.log(error);
});
}
I initiate loading scans through this action:
export function loadPickingScans (orderReference) {
return { type: SCANNING_LOAD_SCANS, orderReference };
}
It's called in my smart (page) component:
componentDidMount() {
const { loadPickingScans } = this.props;
loadPickingScans(this.props.match.params.orderReference);
}
This is the url:
enter code herehttp://localhost:3000/orders/my-order-reference/scans
this.props.match.params.orderReference correctly contains my-order-reference.
However, adding a log to my action, orderReference is received as undefined.
What should I do to receive this expected value?
Update
By request:
function mapDispatchToProps(dispatch) {
return {
loadPickingScans: () => dispatch(loadPickingScans())
};
}
In mapDispatchToProps, while dispatching the action, you haven't passed any argument to it and hence it logs undefined in the method, you need to write it like
function mapDispatchToProps(dispatch) {
return {
loadPickingScans: (value) => dispatch(loadPickingScans(value))
};
}
or simply
const mapDispatchToProps = {
loadPickingScans
}
I'm having trouble trying to read from apollo cache from within a react component the mutation works and writes to my server and returns the data but when passed to my update function it seems to lose the context of this when in inMemoryCache.js
"apollo-cache-inmemory": "^1.2.5"
"react-apollo": "^2.1.4"
"apollo-boost": "^0.1.7"
TypeError: Cannot read property 'read' of undefined
at ./node_modules/apollo-cache-inmemory/lib/inMemoryCache.js.InMemoryCache.readQuery
import React, { Component } from "react";
import { graphql } from "react-apollo";
import trim from "lodash/trim";
import AuthorForm from '../components/author-form';
import ALL_AUTHORS from "../graphql/getPosts.query";
import CREATE_AUTHOR from "../graphql/createAuthor.mutation";
class CreateAuthor extends Component {
state = {
errors: false
};
onSubmit(event) {
event.preventDefault();
const form = new FormData(event.target);
const data = {
firstName: form.get("firstName"),
lastName: form.get("lastName")
};
if (!data.firstName || !data.lastName) {
return this.setState({ errors: true });
}
this.create({
firstName: trim(data.firstName),
lastName: trim(data.lastName)
});
}
async create(variables) {
const { createAuthor } = this.props;
this.setState({ errors: false });
try {
await createAuthor({
variables,
update: (cache, data) => this.updateCache(cache, data)
})
} catch (e) {
this.setState({ errors: true })
}
}
updateCache({ readQuery, writeQuery }, { data: { createAuthor }, errors }) {
if (errors) {
return;
}
const { allAuthors } = readQuery({
query: ALL_AUTHORS,
defaults: {
allAuthors: []
}
});
/*eslint-disable*/ console.log(allAuthors);
}
render() {
return (
<div>
<AuthorForm onSubmit={this.onSubmit.bind(this)}/>
<OnError/>
</div>
);
}
}
export default graphql(CREATE_AUTHOR, { name: "createAuthor" })(CreateAuthor);
is it to do with me binding this to the onSubmit button? if so what is the proper way to attach a function to a element without losing context of this within the component and still allow apollo cache to function properly.
I was losing the context of this because I was deconstructing the first argument. this is what I settled on in the end.
It throw errors when there were no allAuthors on the ROOT_QUERY object so added it to my return statement.
this doesn't feel like an ideal way of updating the cache shouldn't default param passed to readQuery prevent the error thrown.
updateCache(cache, { data: { createAuthor }, errors }) {
if (errors || !cache.data.data.ROOT_QUERY.allAuthors) {
return;
}
const query = ALL_AUTHORS;
const { allAuthors } = cache.readQuery({
query,
defaults: {
allAuthors: []
}
});
const data = {
allAuthors: allAuthors.concat([createAuthor])
};
cache.writeQuery({
query,
data
});
}
I have two form components, which have a common JS validator.
import { validateInput } from './validateInput.js'
export default {
data () {
return {
email: 'a#a.com',
validEmail: false
}
},
methods: {
validateInput
},
watch: {
'email': function (val) {
this.validEmail = validateInput('email', val)
// ^ makes async request
}
}
}
The validator uses jQuery.ajax - async HTTP request to server. The problem is that, since validateInput() is asynchronous, it returns before it gets a response from the server (this.validEmail is undefined)
How do I update the Vue instance (this.validEmail) after the async request in this JS imported function is completed?
In simple terms - how do I access the Vue instance from inside validateInput in a way that works asynchronously?
I've tried passing the Vue instance as an argument, doesn't update:
validateInput('email', val, this)
----
// validateInput.js
email() {
$.ajax({
...
success(response) {
vueInstance.validEmail = response // doesn't work
}
})
vueInstance.validEmail = false
// ^^^^ this works
}
Try this
watch: {
'email': function (val) {
validateInput('email', val,this)
}
}
and
validateInput('email', val, ref)
----
// validateInput.js
email() {
$.ajax({
...
success(response) {
ref.validEmail = response // doesn't work
}
})
// ^^^^ this works
}
Or the traditional way to deal with asynchronous calls is pass a callback that has a closure over the variable that needs edited.
watch: {
'email': function (val) {
validateInput('email', val,function(response){this.validEmail=response})
}
}
//...
validateInput('email', val, cb)
//...
success(response) {
cb(response);
}
I can successfully upload to firebase storage and retrieve image URL using promises, but I wanted to link a progress bar to the percent completion of the upload. What I have achieved thus far: When I call this function from a component:
this.props.handleNewPrizeImageUpload(this.progressCallback, this.imageUploadCompletionCallback, prizeImage)
with these callbacks defined in the component:
progressCallback (progress) {
**//this.someOtherFunction(progress).bind(this)**
return console.log('Upload is ' + progress + '% done')
}
imageUploadCompletionCallback (url) {
**//this.props.someOtherFunctionB(url)**
console.log('SAVEPRIZEIMAGE RAN SUCCESFULLY RETURN URL : ', url)}
this function runs:
export function handleNewPrizeImageUpload (progressCallback, imageUploadCompletionCallback,
prizeImage) {
savePrizeImage(progressCallback, imageUploadCompletionCallback,prizeImage)
}
The savePrizeImage is a function that save the image and runs callback back functions accordingly.
I can successfully retrieve the progress value and the URL Data but the Problem I am facing is that I can't use my other defined function within those callbacks to do something the retrieved Data, I keep getting the error that this is not defined. I have tried bind(this) it does not work.
error:
Uncaught TypeError: Cannot read property 'someOtherFunction' of
undefined
What I have tried:
constructor (props) {
super(props)
this.someOtherFunction = this.someOtherFunction.bind(this)
}
and then calling it like so:
progressCallback (progress) {
console.log('Upload is ' + progress + '% done')
this.someOtherFunction(progress)
}
someOtherFunction (progress) {
console.log('HAHAHA')
}
Heer is the Entire component Code Block:
import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import {CreatePrizeForm} from 'components'
import * as ActionCreators from 'redux/modules/prizes'
export class CreatePrizeFormContainer extends Component {
static contextTypes = {
router: PropTypes.object.isRequired
}
constructor (props) {
super(props)
// this.someOtherFunction = this.someOtherFunction.bind(this)
// this.progressCallback = this.progressCallback.bind(this)
}
handlePrizeData (prizeImage) {
this.props.handleNewPrizeImageUpload(this.progressCallback, this.imageUploadCompletionCallback, PrizeImage)
}
progressCallback (progress) {
console.log('Upload is ' + progress + '% done')
//this.someOtherFunction(progress)
}
imageUploadCompletionCallback (url) {
console.log('SAVE TO FIREBASE RAN SUCCESFULLY RETURNED IMAGE URL : ', url)
}
someOtherFunction (progress) {
console.log('HAHAHA')
}
render () {
return (
<div>
<CreatePrizeForm addPrizeData = {(prizeImage) => { this.handlePrizeData(prizeImage) }}/>
</div>
)
}
}
function mapStateToProps (state, props) {
return {
}
}
function mapDispatchToProps (dispatch, props) {
return bindActionCreators(ActionCreators, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(CreatePrizeFormContainer)
When you pass a reference to a function (e.g. as argument, or other assignment), it will not have its context bound to it.
This is the case with the this.progressCallback argument:
this.props.handleNewPrizeImageUpload(this.progressCallback, ... )
That progressCallback argument variable is not linked to this, even though you pass it like that -- which is the cause of a lot of misunderstanding; you are not the only one.
Solve it as follows:
this.props.handleNewPrizeImageUpload(this.progressCallback.bind(this), ... )
See the very good Q&A on this.
Here is some of your code trimmed down to a working snippet:
"strict"
// Dummy Component class for this snippet only
class Component { constructor (props) { this.props = props } }
// Your class:
class CreatePrizeFormContainer extends Component {
constructor (props) { super(props) }
handlePrizeData (prizeImage) {
this.props.handleNewPrizeImageUpload(
this.progressCallback.bind(this),
this.imageUploadCompletionCallback.bind(this), prizeImage)
}
progressCallback (progress) {
console.log('Upload is ' + progress + '% done')
this.someOtherFunction(progress)
}
imageUploadCompletionCallback (url) {
console.log('SAVE TO FIREBASE RAN SUCCESFULLY RETURNED IMAGE URL : ', url)
}
someOtherFunction (progress) { console.log('HAHAHA') }
}
// Dummy savePrizeImage function for this snippet only
function savePrizeImage(progressCallback, imageUploadCompletionCallback, ImageFile) {
// Simulate two progress events
setTimeout(function() { progressCallback(50) }, 0)
setTimeout(function() { progressCallback(100) }, 500)
setTimeout(function() { imageUploadCompletionCallback('http://example.com') }, 510)
}
// Create instance, passing simple prop literal
var obj = new CreatePrizeFormContainer({
handleNewPrizeImageUpload: function(progressCallback,
imageUploadCompletionCallback, ImageFile) {
savePrizeImage(progressCallback, imageUploadCompletionCallback, ImageFile)
}
})
obj.handlePrizeData('dummyPrizeImage.img') // Call handlePrizeData