New view in Spark, using Vue - vue.js

I'm working on my first app built on the Spark foundation now, and I've hit a wall. I should mention that I've looked through the entire Vue Laracast twice now - but Vue is used differently in Spark, which has me confused. Hopefully someone can clarify this a bit for me.
So, the first custom view I've added so far is:
#extends('spark::layouts.app')
#section('content')
<master-servers>
<div class="container">
<!-- Add Server -->
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Add Server</div>
<div class="panel-body">
<form class="form-horizontal" role="form" method="POST" v-on:submit.prevent='methodAddServer'>
{{ csrf_field() }}
#if(count($errors) > 0)
<div class="alert alert-danger">
#foreach($errors->all() as $error)
<p>{{ $error }}</p>
#endforeach
</div>
#endif
#if(session('fail'))
<div class="alert alert-danger">
<p>{{ session('fail') }}</p>
</div>
#endif
#if(session('success'))
<div class="alert alert-success">
<p>{{ session('success') }}</p>
</div>
#endif
<!-- Server Label -->
<div class="form-group">
<label class="col-md-4 control-label">Server Label</label>
<div class="col-md-6">
<input type="text" class="form-control" name="name" v-model='addServer.name' value="{{ old('name') }}" autofocus>
</div>
</div>
<!-- IP -->
<div class="form-group">
<label class="col-md-4 control-label">IP Address</label>
<div class="col-md-6">
<input type="text" class="form-control" name="ip" v-model='addServer.ip' value="{{ old('ip') }}">
</div>
</div>
<!-- Add Button -->
<div class="form-group">
<div class="col-md-8 col-md-offset-4">
<button type="submit" class="btn btn-primary" :disabled="addServerFormBusy">
<i class="fa m-r-xs fa-sign-in"></i>Add server
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</master-servers>
#endsection
In the resources\assets\js\components, I then have a file named servers.js, which contains:
var base = require('../master/servers/servers');
Vue.component('master-servers', {
mixins: [base]
})
And finally, resources\assets\js\master\servers\servers.js contains:
module.exports = {
data: function() {
return {
addServer: [
{ name: '' },
{ ip: '' }
]
}
},
methods: {
methodAddServer: function(e) {
console.log(addServer);
this.addServerFormBusy = true;
this.$http.post('server', this.addServer);
}
}
};
The issue at hand: When browsing this page, and watching the console, I get the following:
Error when evaluating expression "addServer.name": TypError: Cannot
read property "name" of undefined
You are setting a non-existant path "addServer.name" on a vm instance.
Consider pre-initializing the poprety with the "data" option for more
reliable reactivity and better performance.
v-on:submit="methodAddServer" expects a function value, got undefined
What I've tried:
I've tried adding all of the code into the component without using a mixin as well (as a test) - but that resulted in the same issues.
I spent some time looking through how the views (as Vue's) are built in Spark now, but wind up getting lost in the structure quite a bit.
From everything I've understand when watching the Vue laracast, this should work - but as Spark is using some kind of other convention, I'm not sure it's supposed to here. I realize I could use it as shown in the Laracast, but I'd like to keep building using the same coding style that is used in Spark.
If any of you experts out there have any clue as to what might be going on or missing, or for that matter have any other tangible advise, I'd be very thankful!

The solution to this turned out to be exporting the Spark JS files and reviewing how it's defined there. Forms are defined within components and included in a bootstrap file, which I had completely missed.

Related

Submit values from multiple components

I am using vuejs-wizard to create registration page, I have each tab in component like this
<form-wizard color="#fcab1a" title="" subtitle="" finish-button-text="Register">
<tab-content title="Personal Info" icon="icon-location3 fa-2x">
<personal-info></personal-info>
</tab-content>
<tab-content title="Contact Info" icon="icon-box fa-2x">
<contact-info></contact-info>
</tab-content>
<tab-content title="Address" icon="icon-alarm fa-2x">
<address></address>
</tab-content>
</form-wizard>
personal info:
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Personal Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Full Name <span class="text-danger">*</span></label>
<input type="text" value="" class="form-control" v-model="name" />
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Age <span class="text-danger">*</span></label>
<input type="number" value="" class="form-control" v-model="age" />
</div>
</div>
</div>
</div>
</div>
</template>
contact info:
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Contact Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Mobile <span class="text-danger">*</span></label>
<input type="text" value="" class="form-control" v-model="mobile" />
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Email <span class="text-danger">*</span></label>
<input
type="number"
value=""
class="form-control"
v-model="email"
/>
</div>
</div>
</div>
</div>
</div>
</template>
so my question is, what is the best way to submit the form, do I need vuex to store the state or is there a better/easier way ?
Thanks
It depends...
The answer depends on various factors. For example, are you using vuex already, size of the app, test-ability of app, and even how are fields get validated (asynch/api validations?).
When it's a simple app, and I only have direct parent=>child relationships, I tend to skip adding the Vuex as a dependency. (but I mostly deal with SPAs, so I usually use it) YMMV.
In this case, that would require that the fields are defined in the parent. Then adding props and emitters for each value to the children, and a listener on the parent. As you start to add more fields though, you might find this tedious, and opt to pass fields in an object either in groups, or as whole (and only pick the ones you need in tab), and then you can implement your own v-model in the tab components which can make it pretty easy to pass the object around
If you're using a more recent Vue version (2.6+), you could use vue.observable to
share a store between multiple components without the bells/whistles of Vuex
There's a good article that shows how to build a vuex clone with it, but in reality it's much, much simpler than that to create a store that would suit your needs. Let me know in the comments if you're interested in how to implement it, and I can describe it.
Rolling with custom store
it's really as simple as this
Create a store.js file with...
import Vue from 'vue';
const store = Vue.observable({
name: null,
email: null,
age: null,
mobile: null,
});
export default store;
then in any component that you want to have access to it, add the store during create
import store from "../store";
export default {
name: "PersonalInfo",
created() {
this.$store = store;
}
};
now the all the store is available to you in the template through $store
<input type="text" value class="form-control" v-model="$store.name">
codesandbox example
You lose the benefits, such as "time-traveling" over mutations that Vuex offers. Because you're not dealing with the scale that the flux pattern (vuex) was meant for, I would use this solution in an app this size.
Ya you should just use vuex, it combines like data so that it isn't spread out across multiple files. If you are going to be sending this information back to your backend, it makes it easier to have most backend connections in one place. without the wizard thing I redid your code with a store like this. Note the computed property instead of using data. By doing it as a computed property you don't have to write any code to change the variables stored inside of the store.
Personal.vue
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Personal Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Full Name <span class="text-danger">*</span></label>
<input
type="text"
value=""
class="form-control"
v-model="register.name"
/>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Age <span class="text-danger">*</span></label>
<input
type="number"
value=""
class="form-control"
v-model="register.age"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
register() {
return this.$store.state.register;
},
},
};
</script>
Contact.vue
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Contact Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Mobile <span class="text-danger">*</span></label>
<input
type="text"
value=""
class="form-control"
v-model="register.mobile"
/>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Email <span class="text-danger">*</span></label>
<input
type="number"
value=""
class="form-control"
v-model="register.email"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
register() {
return this.$store.state.register;
},
},
methods: {
submit() {
this.$store.dispatch("register", {
person: this.register,
});
},
},
};
</script>
<style></style>
store/index.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
register: {},
},
actions: {
// register({commit}, data){
//put in some stuff here.
//},
},
});
If you decide to go the store route, all you have to do is this
1. npm install vuex
2. add a folder inside of your src folder called store
3. add a file named index.js
4. go to main.js and add this line"import store from "./store";"
5. where it says new "Vue({" add "store" this will register it.
Vuex is super easy and makes life way easier as your project gets bigger.
The .sync modifier provides two way binding pattern to props, you can read about it here https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier.
In the parent component you can use the .sync modifier this way:
<ChildComponent :name.sync="parentNameProperty" />
...
data: () => ({ parentNameProperty: '' }),
...
Then in the child component you receive name as prop and you can emit an event to update the value in the parent component, by using watch or a method:
...
props: {
name: {
type: String,
default: ''
}
}
...
this.$emit(update:parentNameProperty, newValue)
...
Vuex is a great way to handle state, but is fine to use the above pattern for small applications.

VueJS - v-model in for-loop

I'm trying to build something like the questions in OkayCupid, but all the questions - which are different forms - are located on the same component.
I use an object of questions and 3 possible answers for each question, and I use v-for to loop through the object and create cards with a question, 3 answers with radios, and a submit button.
The problem is that I want to get not only the answer the user chooses, but also the question it belongs to.
Here is my form in the template:
<div class="container">
<div class="row">
<div
class="col-lg-3 col-md-4 col-6"
v-for="(question,index) in questionCollection"
:key="index"
>
<form class="form">
<div class="img-fluid img-thumbnail shadow-lg p-3 mb-5 bg-white rounded">
<!-- <input type="text" :value="question.question" v-model="q" /> -->
<h3 class="d-block mb-4 h-100" alt data-holder-rendered="true">{{ question.question }}</h3>
<div class="card-body container">
<div class="card-text form-check">
<input
class="form-check-input"
type="radio"
name="gridRadios"
id="a1"
:value="question.answer1"
v-model="answer"
/>
<h4 class="font-weight-light" for="a1">{{ question.answer1 }}</h4>
</div>
<div class="card-text form-check">
<input
class="form-check-input"
type="radio"
name="gridRadios"
id="a2"
:value="question.answer2"
v-model="answer"
/>
<h4 class="font-weight-light" for="a2">{{ question.answer2 }}</h4>
</div>
<div class="card-text form-check">
<input
class="form-check-input"
type="radio"
name="gridRadios"
id="a3"
:value="question.answer3"
v-model="answer"
/>
<h4 class="font-weight-light" for="a3">{{ question.answer3 }}</h4>
</div>
</div>
<div class="card-text container">
<small class="text-muted">{{ question.user }}</small>
<button
href="#"
class="btn btn-primary my-3 mx-10 btn float-right shadow-sm rounded"
#click.prevent="answerQuestion"
>Save</button>
</div>
</div>
</form>
</div>
</div>
</div>
And the script:
export default {
name: "questions",
data() {
return {
q: null,
answer: null
};
},
}
As you can see, at the beginning of the form, I tried to get the question element using v-model in a "fake" input, but it gives me an error that it's conflicted with the v-bind of the value (the question) I want to grab. Of course, I can't use v-model on the headline itself because Vue allows to use it only on inputs.
I've tried to change the v-model into v-model="questionCollection[index].question, but then I have no idea how to get it in the script and, let's say, log it to the console with the corresponding answer.
One way to handle this is to submit the question and answer together in the Save button's click-handler. That is, change answerQuestion() to receive question (the iterator variable in v-for) and answer, and update the Save button's click handler in the template to pass those two variables:
// template
<div v-for="(question, index) in questionCollection">
...
<button #click.prevent="answerQuestion(question, answer)">Save</button>
</div>
// script
answerQuestion(question, answer) {
console.log({ question, answer })
}
demo

angular 5, Keeping value on inputs and adding it to JS object

I am making a web tool in angular 5 to help in the creation of new XML.
I am having trouble passing around the values of inputs and keeping them in the same input at the same time.
I Have a form with an event of ngSubmit that uses as a paremeter the reference of an ngForm
<form class="form-group" (ngSubmit)="createSimpleTypeList(f)" #f="ngForm" >
<div class="container">
<div class="row">
<div class="col-sm-12" >
<button class="btn btn-danger flex ">Construir Json!</button>
</div>
</div>
</div>
<hr>
<div class="container" *ngFor="let type of objSimpleType;let i =index">
<hr>
<div class="row">
<div class="col-sm-3">
<input type="text" [(ngModel)]="type.name" name="nameType"/>
<br>
Inserte el nombre del tipo:
</div>
<div class="col-sm-3">
<input type="text" [(ngModel)]="type.type" name="type"/>
<br>
Inserte el tipo:
</div>
<div class="col-sm-2">
<input type="number" [(ngModel)]="type.min" name="min"/>
<br>
Inserte minimo:
</div>
<div class="col-sm-2">
<input type="number" [(ngModel)]="type.max" name="max"/>
<br>
Inserte maximo:
</div>
<div class="col-sm-2">
<input type="checkbox" name="number" /> Numero?
</div>
</div>
</div>
<hr>
</form>
This is the createSimpleTypeList method
console.log(form.value.nameType);
this.objSimpleType.push({
'-name': form.value.nameType,
'xsd:restriction': {
'-base': form.value.type,
'xsd:minLength': {
'-value': form.value.min
},
'xsd:maxLenght': {
'-value': form.value.max
}
}
});
this.typeList.emit(form.value.nameType);
this.sharedNameSpace.emit(this.namespace);
The problem I am having is that I have not found a way to push new items into objSimpleType, without erasing the value of inputs, and if I manage to not erase the value of the inputs then the values of the form wont push.
this is how it looks.how the inputs work right now
This is what I need
How the inputs should be
I am still new to angular, and I would really appreciate the help.
Now that I have looked found the solution, I had two mistakes in the code above.
First my logic was not well executed, I needed to work with unique IDs,and call them dynamically.
Second, I was having trouble to call them , and after reading a lot, I found that I can call the parameters of a Json with brackets[].
"form.value['nameType' + this.i]" --Something like this is what i Needed.

Vue v-model data binding issue

I have a pair of standard Bootstrap radio buttons and I wanted v-model to set a value in the vue data object when one is selected.
This works just fine in a JSfiddle, however this doesn't work when running locally on my own environment.
I can get other directives working fine like 'v-if', but v-model just doesn't seem to work at all.
Nothing comes up in the console, and when using vue dev tools extension; I can see that the 'test' object is never updated when one of the radio buttons is selected.
I'm using the latest version of bootstrap, jquery and the vue development version.
<div class="container margin-top-rem" id="app">
<div class="row">
<div class="col-12">
<div class="card text-center" v-if="testIf">
<div class="tab-content" id="nav-tabContent">
<div class="tab-pane fade show active card-body-padding">
<div class="row">
<div class="col-12">
<div class="form-group">
<div class="btn-group btn-group-toggle" data-toggle="buttons">
<label class="btn btn-secondary">
<input type="radio" name="options" autocomplete="off" v-model="testVModel" :value="false" />false
</label>
<label class="btn btn-secondary">
<input type="radio" name="options" autocomplete="off" v-model="testVModel" :value="true" />true
</label>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div>test value is {{testVModel}}</div>
</div>
</div>
</div>
var app = new Vue({
el: '#app',
data: {
testIf: 'blah',
testVModel: ''
}
})
It looks like this is a conflict between bootstrap and Vue.
Two options to resolve are:
1) Use Bootstrap-vue
2) Remove 'data-toggle="buttons"' from:
<div class="btn-group-toggle" data-toggle="buttons">

Vue.js not updating template when changing child objects

I'm extending laravel spark and wanted to try to validate registration before actually sending it off.
All data is handled nicely and correctly from the server but the errors do not pop-up.
The code being used for validation is as following:
module.exports = {
...
methods: {
...
validate(field, value) {
var formData = {
field: field,
value: value
};
var form = new SparkForm(formData);
Spark.post('/register_validate', form).then(response => {
this.registerForm.addSuccess(field);
}).catch(errors => {
this.registerForm.addError(field, errors[field]);
});
}
}
};
After, i extended SparkForm and added these methods (successes is just a copy of errors, used to display validation success messages):
/**
* SparkForm helper class. Used to set common properties on all forms.
*/
window.SparkForm = function (data) {
...
this.addError = function(field, errors) {
form.successes.remove(field);
form.errors.add(field, errors);
},
this.addSuccess = function(field) {
form.errors.remove(field);
form.successes.add(field, true);
}
};
And finally i added some methods on the SparkFormErrors.
/**
* Spark form error collection class.
*/
window.SparkFormErrors = function () {
...
this.add = function(field, errors) {
this.errors[field] = errors;
};
this.remove = function(field) {
delete this.errors[field];
}
};
In the console no errors are shown and in the network tab i can see the correct messages coming true, also when i add a console log in for example the response callback i can see the actual errors or success messages. But they are not drawn on screen.
For completeness i'll include the important content of the template blade file:
#extends('spark::layouts.app')
#section('content')
<spark-register-stripe inline-template>
<!-- Basic Profile -->
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
{{ trans('auth.register.title') }}
</div>
<div class="panel-body">
<!-- Generic Error Message -->
<div class="alert alert-danger" v-if="registerForm.errors.has('form')">
#{{ registerForm.errors.get('form') }}
</div>
<!-- Registration Form -->
<form class="form-horizontal" role="form">
<!-- Name -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('name')}">
<label class="col-md-4 control-label">{{ trans('user.name') }}</label>
<div class="col-md-6">
<input type="name" class="form-control" name="name" v-model="registerForm.name" autofocus #blur="validate('name', registerForm.name)">
<span class="help-block" v-show="registerForm.errors.has('name')">
#{{ registerForm.errors.get('name') }}
</span>
</div>
</div>
<!-- E-Mail Address -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('email')}">
<label class="col-md-4 control-label">{{ trans('user.email') }}</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" v-model="registerForm.email" #blur="validate('email', registerForm.email)">
<span class="help-block" v-show="registerForm.errors.has('email')">
#{{ registerForm.errors.get('email') }}
</span>
</div>
</div>
<!-- Password -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('password')}">
<label class="col-md-4 control-label">{{ trans('user.password') }}</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" v-model="registerForm.password" #blur="validate('password', registerForm.password)">
<span class="help-block" v-show="registerForm.errors.has('password')">
#{{ registerForm.errors.get('password') }}
</span>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button class="btn btn-primary" #click.prevent="register" :disabled="registerForm.busy">
<span v-if="registerForm.busy">
<i class="fa fa-btn fa-spinner fa-spin"></i>{{ trans('auth.register.submitting') }}
</span>
<span v-else>
<i class="fa fa-btn fa-check-circle"></i>{{ trans('auth.register.submit') }}
</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</spark-register-stripe>
#endsection
Any bright mind seeing what i'm missing/forgetting?