change local data object on change of global state - laravel

I am trying to edit and update using vuex. I have two components, one is form for entry/edit and another for viewing data. When I click on edit button of view component I want to populate the form component with the data from backend.
Note that I already have a data object on form component that is bind with form and used to store data. I want to change it on edit. I have no problem on getting data from backend.
I just can not change the local data object from state
This is my codes what I've tried
Form component:
import { mapState } from 'vuex'
export default {
data(){
return{
form:{
id:'',
name:'',
email:'',
phone:'',
address:''
}
}
},
computed:{
...mapState({
data (state) {
this.form=state.employee; //This is where I am stuck
}
}),
}
state:
export default{
state:{
employee:{}
},
getters:{}
},
mutations:{
setEmployee(state,employee){
state.employee=employee;
}
},
actions:{
fetchEmployee({commit},id){
axios.put('employee-edit/'+id)
.then(res=>res.data)
.then(employee=>{
commit('setEmployee',employee)
})
}
}
}
view component:
export default {
methods:{
editEmployee(id){
this.$store.dispatch('fetchEmployee',id);
}
}
}

Multiple issues/misunderstandings with your approach.
1) computed properties are "passive" and supposed to return a value "computed" from other values. Directly assigning to your local state is probably not what you want to do.
2) HTTP methods: In general the HTTP PUT method replaces the resource at the current URL with the resource contained within the request.
Please read up on http methods and how they are supposed to be used. You want GET
3) mapState is a helper method if you need multiple getters from vuex state store. I suggest you use this.$store.getters.myVariableInState for simple tasks like in your example.
What you probably want is more along these lines:
// store
getters:{
employee: state => state.employee
}
// component
computed: {
employee() {
return this.$store.getters.employee
}
}
If your actions was already dispatched earlier and the data is available in the store all you then need is
methods: {
editEmployee() {
this.form = this.employee
}
}
Since your question stated "change local data on state change", here is an approach for that:
watch your local computed property
watch: {
employee() {
// this.employee (the computed value) now refers to the new data
// and this method is triggered whenever state.employee changes
}
}

Related

How to pass an object from axios catch block to parent component with Vue.js

I am using Laravel 7 and Vue.js 2.
I want to pass an object of validation errors from the catch block of an axios call to a parent component but for some reasons it doesn't work.
This is the code of the axios call:
runReport: function() {
let self = this;
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
this.errors = {};
})
.catch(function(error) {
console.log(error.response.data);
self.errors = error.response.data;
alert(self.errors);
self.$emit('failure');
self.$emit('passErrors', self.errors); //problem
console.log('call ended');
});
}
This is the code in the parent component:
<template>
<div>
<report-meeting #passMeetings="onPassMeetings" #failure="displayTable=false" #success="displayTable=true"></report-meeting>
<hr>
<validated-errors :errorsMeeting="errorsMeeting" #passErrors="onPassErrors" v-if="displayTable===false"></validated-errors>
<table-report :meetingsSelected="meetingsSelected" v-if="displayTable===true"></table-report>
</div>
</template>
<script>
import TableReport from "./TableReport.vue"
import ReportMeeting from "./ReportMeeting.vue"
import ValidatedErrors from "./ValidatedErrors.vue"
export default {
components: {
'table-report': TableReport,
'report-meeting': ReportMeeting,
'validated-errors': ValidatedErrors
},
mounted() {
console.log('Component mounted.');
},
data: function() {
return {
displayTable: false,
meetingsSelected: {},
errorsMeeting: {}
}
},
methods: {
onPassMeetings(value) {
console.log(value);
this.meetingsSelected = value;
},
onPassErrors(value) {
console.log('errors passed'); //never used
this.errorsMeeting = value;
}
}
}
</script>
In the console I visualize no errors (except an 422 Unprocessable Entity). The strange thing is that the first emit works (failure), but the second one doesn't work (passErrors).
In the parent function onPassErrors I put a console.log that is never used so I suppose that the function is never called.
Can help?
This is likely caused by an event name mismatch, which can occur when using in-DOM templates because HTML attributes are automatically lower-cased (#passErrors becomes #passerrors in the DOM).
When using the development build of Vue, you'd see a warning in the browser's console:
[Vue tip]: Event "passerrors" is emitted in component but the handler is registered for "passErrors". Note that HTML attributes are case-insensitive and you cannot use v-on to listen to camelCase events when using in-DOM templates. You should probably use "pass-errors" instead of "passErrors".
This is not a problem in single file components (demo 1) or string templates (demo 2), but if you must stick with in-DOM templates, custom event names are recommended to be kebab-case:
<!-- Parent.vue -->
<MyComponent #pass-errors="onPassEvent" />
// MyComponent.vue
runReport() {
this.$emit('pass-errors', /*...*/)
}
demo 3

will it break the react-redux design to create method which called in other action creator?

I don't know if we should stick to the react-redux style all the time.
With react-redux, we put logic to the action creator, no matter simple action or async request. however, it indeed helps us to keep the work flow simple and clear:dispatch a action, and reducer to process the action to change data in store.
but I am curious about:
can I create a function which is not an action creator? does this break the redux design?
for example:
action-creator-1.js
const getData=()=>(dispatch,getState)=>{
return Promise.resolve().then(()=>{
//do long-running work
});
};
export default{
getData
};
action-creator-2.js
import action1 from 'action-creator-1';
const loadData=()=>(dispatch,getState)=>{
return Promise.resolve().then(()=>{
//? return action1.getData();
})
.then(()=>{
//do other work
})
}
can we set getData a simple function in action creator file:
const getData=(state)=>{
return Promise.resolve().then(()=>{
//get local data
})
}
can this make sense?
Actions are payloads of information that send data from your application to your store. They are the only source of information for the store. You send them to the store using store.dispatch().
In action.js
import {serviceCall} from './service-call';
export function showLoader() {
return {
type: 'SHOW_LOADER',
payload: true
};
}
export function loadData() {
Store.dispatch(showLoader());
return {
type: 'SET_WIDGET_DATA',
payload: return Promise.resolve().then(response => {
return response
})
}
}
export function getData() {
Store.dispatch(showLoader());
return {
type: 'GET_PAGE_DATA',
payload: return Promise.resolve().then(response => {
Store.dispatch(loadData());
})
}
}
export function serviceCall() {
Store.dispatch(showLoader());
return {
type: 'GET_HTTP_DATA',
payload: serviceCall({
url: 'http://'
})
}
}
can I create a function which is not an action creator? does this break the redux design? ==> yes this will not break your redux design as we also create another .js file for http call like service.js.

Is it possible to change props value from method in Vue component?

I have a component and i am passing value 543 to props :prop-room-selected,
<navigation-form :prop-room-selected='543'>
</navigation-form>
Now, From a button click, i am calling the function updateCoachStatus to change the value of propRoomSelected, but the props value is not updating.
{
template: '#navigation-form',
props: ['propRoomSelected'],
data: function () {
return {
roomSelected: this.propRoomSelected,
}
},
methods:{
updateCoachStatus: function(event){
this.propRoomSelected = 67;
}
}
}
I dont know how to change the value of props from function. Is it possible in Vue to update the value of props??
What you are doing will throw a warning in Vue (in the console).
[Vue warn]: Avoid mutating a prop directly since the value will be
overwritten whenever the parent component re-renders. Instead, use a
data or computed property based on the prop's value. Prop being
mutated: "propRoomSelected"
The value will actually change inside the component, but not outside the component. The value of a parent property cannot be changed inside a component, and, in fact, the updated value will be lost if the parent re-renders for any reason.
To update the parent property, what you should do is $emit the updated value and listen for the change in the parent.
Vue.component("navigation-form",{
template: '#navigation-form',
props: ['propRoomSelected'],
data: function () {
return {
roomSelected: this.propRoomSelected,
}
},
methods:{
updateCoachStatus: function(event){
this.$emit("update-room-selected", 67) ;
}
}
})
And in your parent template listen for the event
<navigation-form :prop-room-selected='propRoomSelected'
#update-room-selected="onUpdatePropRoomSelected">
</navigation-form>
Here is an example.
This is a common pattern and Vue implemented a directive to make it slightly easier called v-model. Here is a component that supports v-model that will do the same thing.
Vue.component("navigation-form-two",{
template: '#navigation-form-two',
props: ['value'],
data: function () {
return {
roomSelected: this.value,
}
},
methods:{
updateCoachStatus: function(event){
this.$emit("input", 67) ;
}
}
})
And in the parent template
<navigation-form-two v-model="secondRoomSelected">
</navigation-form-two>
Essentially, for your component to support v-model you should accept a value property and $emit the input event. The example linked above also shows that working.
Another approach is using a computed property for handling props:
{
template: '#navigation-form',
props: ['propRoomSelected'],
data () {
return {
roomSelected: this.computedRoomSelected,
changeableRoomSelected: undefined
}
},
computed: {
computedRoomSelected () {
if (this.changeableRoomSelected !== undefined) {
return this.changeableRoomSelected
}
return this.propRoomSelected
}
},
methods: {
updateCoachStatus (event) {
this.changeableRoomSelected = 67
}
}
}

How can I access Vue JS props in a method in a component?

I may be wrong in my understanding of props but I can't seem to be able to pass a prop to a component and then use the value in a method?
So far I am able to get data from a fixed API and output everything from the vue component, now I would like the api source to be dependent on the variable passed to the component.
My blade template:
<projectstatuses :userslug="this-user" :projectslug="this-project"></projectstatuses>
Then in my Vue Component:
export default {
props: {
userslug: {
type: String,
default: "other-user",
},
projectslug: {
type: String,
default: "other-project",
}
},
data() {
return {
statuses : [],
}
},
created(){
this.getStatuses();
},
methods : {
getStatuses(){
console.log(this.userslug);
console.log(this.projectslug);
axios.get('/api/' + this.userslug + '/' + this.projectslug)
.then((response) => {
let statuses = response.data;
this.statuses = statuses.statuses;
console.log(response.data.statuses);
})
.catch(
(response) => {
console.log('error');
console.log(response.data);
}
);
}
}
}
In the console I get the default values, if I remove the default values I get undefined. I have tried removing the api method and simply console logging the values but I still get undefined. Is what I'm trying to do possible or have I completely misunderstood how props work?
You are trying to bind this-user and this-project as a properties not as values ,
So you will need to define them in the data object in the parent,
but if you want to pass this-user and this-project just as value remove the : try that:
<projectstatuses userslug="this-user" projectslug="this-project"></projectstatuses>
Dynamic-Props
Don't add : in your template:
<projectstatuses userslug="this-user" projectslug="this-project"></projectstatuses>
Vue will expect there's data bound to this-user.

Routing in Extjs with DeftJs

deftjs looks really promising as it adds exactly the necessary things I missed in the MVC implementation of ExtJs.
What I actually miss is a functionality that makes routing possible/ easy. Extjs has a Ext.ux.Router functionality but I formerly used code like this with help of this lib:
initRoutes: function () {
var me = this;
Log.debug('Books.controller.App: initRoutes');
//use PATH.JS library until ExtJs supports routing as Sencha Touch 2.0 does. (see utils\Path)
Path.map("#/home").to(function () {
me.getController('Home').index();
});
Path.map("#/trackingsheet").to(function () {
me.getController('TrackingSheet').index();
});
Path.root('#/home');
Path.listen();
}
As the procedure of creating the crucial parts in deftjs is now exactly the other way around (view creates the controller) I certainly cannot refer to a controller's method and instantiate the view and make it the visible one. I have a pretty simple card layout here - what means only one view can be visible at a time, it is not necessary to go any deeper like this (e.g. make a task pane visible or the like).
What is the preferred way to do it?
I can think of making the Viewport a view factory having some methods like the controller before.
Thanks,
da5id
I solved this problem by using Ext.util.History class in a history context class that can raise an event when the hash changes:
Ext.define('myApp.context.HistoryContext', {
mixins: {
observable: 'Ext.util.Observable'
},
constructor: function(config) {
var me = this;
if (config == null) {
config = {};
}
this.initConfig(config);
Ext.util.History.add('home');
//init Ext.util.History; if there is a hash in the url,
//controller will fire the event
Ext.util.History.init(function(){
var hash = document.location.hash;
me.fireEvent('tokenChange', hash.replace('#', ''));
});
//add change handler for Ext.util.History; when a change in the token occurs,
//this will fire controller's event to load the appropriate content
Ext.util.History.on('change', function(token){
me.fireEvent('tokenChange', token);
});
this.mixins.observable.constructor.call(this);
this.addEvents('tokenChange');
return this.callParent(arguments);
}
});
Then you can inject this context in to your controller, and observe the token change, and implement the action in dispatch method:
Ext.define('myApp.controller.HomeController', {
extend: 'Deft.mvc.ViewController',
inject: [
'historyContext'
],
control: {
appContainer: {},
home: {
click: 'addHistory'
},
about: {
click: 'addHistory'
}
},
observe: {
historyContext: {
tokenChange: "dispatch"
}
},
init: function() {
return this.callParent(arguments);
},
switchView: function(view) {
//change this to get the cards for your card layout
this.getAppContainer().add(Ext.ComponentMgr.create({
xtype : view,
flex : 1
}));
},
addHistory: function(btn) {
var token = btn.itemId;
Ext.util.History.add(token);
},
dispatch: function(token) {
// switch on token to determine which content to load
switch(token) {
case 'home':
this.switchView('view-home-Index');
break;
case 'about':
this.switchView('view-about-Index');
break;
default:
break;
}
}
});
This should be ok for the first level routing (#home, #about), but you need to implement your own mechanism to fetch the token for the second and third level routes. (#home:tab1:subtab1) You can possibly create a service class that can handle fetching the hash and inject the service to each controllers to dispatch.
For further discussion in this topic, go to https://github.com/deftjs/DeftJS/issues/44

Resources