我正在使用vuejs 2.0和laravel 5.3。我从子组件(一个预先输入组件)发出一个自定义事件,我正在使用v-on指令捕获此事件。
以下是我的组件的代码:
<template>
<input ref="input" class="typeahead-suggestions"
:class="classes"
:id = "id"
v-bind:value="value"
v-on:input="updateValue($event.target.value)"
v-on:blur="formatValue"
:placeholder="placeholder">
</template>
<script>
var Bloodhound = require('typeahead.js')
export default {
data: function() {
var id = 'typeahead-suggestion' + parseInt(Math.random() *100000);
return {
id,
defaultSuggestions: [],
query: ''
};
},
props: {
param: {
type: String,
default: 'item'
},
value: {
type: String,
default: ''
},
classes: {
type: String,
default: ''
},
displayKey: {
type: String,
default: ''
},
suggestionTemplate: {
type: String,
default: ''
},
name: {
type: String,
default: 'Vue Auto Complete'
},
prefetch: {
type: String,
default: ''
},
defaultSuggestion: {
type: Boolean,
default: false
},
remote: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
local: {
type: Array,
default: function () {
return [];
}
},
responseWrapper: {
type: String,
default: ''
}
},
watch: {
local: function(newVal) {
if (this.defaultSuggestion) {
this.defaultSuggestions = [...newVal];
}
this.resetTypeahead();
}
},
mounted: function() {
this.initTypeahead();
if (this.local.length) {
this.defaultSuggestions = [...this.local];
}
},
methods: {
updateValue: function (value) {
this.$emit('input', value);
},
formatValue: function () {
this.$refs.input.value = this.value;
},
transformer: function (response) {
if (this.responseWrapper) {
response = response[this.responseWrapper];
}
if (this.defaultSuggestion && this.local.length === 0) {
this.defaultSuggestions = response.splice(0, 5);
}
return response;
},
bloodhoundOption: function() {
var bloodhoundConfig = {};
if (this.prefetch) {
var prefetch = {
cache: false,
url: this.prefetch
};
if (this.defaultSuggestion) {
prefetch = {...prefetch, transform: this.transformer};
}
bloodhoundConfig = { prefetch};
}
if (this.local) {
bloodhoundConfig = {
local: this.local,
...bloodhoundConfig
}
}
if (this.remote) {
bloodhoundConfig = {
remote: {
url: this.remote,
wildcard: '%QUERY',
transform: this.transformer
},
...bloodhoundConfig
}
}
return bloodhoundConfig;
},
parseTemplate: function(data) {
var res = Vue.compile(this.suggestionTemplate);
var vm = new Vue({
data,
render: res.render,
staticRenderFns: res.staticRenderFns
}).$mount();
return vm.$el;
},
getSource: function() {
var self = this;
var bloodhoundConfig = this.bloodhoundOption();
var datumTokenizer = this.displayKey ? Bloodhound.tokenizers.obj.whitespace(this.displayKey)
: Bloodhound.tokenizers.whitespace;
var engine = new Bloodhound({
datumTokenizer,
queryTokenizer: Bloodhound.tokenizers.whitespace,
...bloodhoundConfig
});
var source = function(q, sync, async) {
if (q === '' && self.defaultSuggestions.length>0 && self.defaultSuggestion) {
sync(self.defaultSuggestions);
} else {
engine.search(q, sync, async);
}
};
return this.defaultSuggestion ? source : engine;
},
resetTypeahead: function() {
$(document).find('#' + this.id).typeahead('destroy');
this.initTypeahead();
},
initTypeahead: function() {
var self = this;
var templates = {};
if (this.suggestionTemplate) {
templates = {suggestion: self.parseTemplate}
};
var dataset = {
name: 'Typeahead-Suggestion',
display: this.displayKey,
source: this.getSource(),
templates
};
$(document).find('#' + self.id).typeahead({
minLength: 0,
highlight: true
}, dataset)
.on('typeahead:select', function(event, suggession) {
self.$emit('input', self.displayKey? suggession[self.displayKey]: suggession);
self.$emit('selected', suggession);
// Set value of hidden input to the id of selected item
$('input[name="' + self.param + '"]').attr("value", suggession['id']);
self.$emit(self.param + '_typeahead');
});
}
}
}
</script>
在上面的代码中,我通过以下语句在完成预先选择('typeahead:select')后发出一个自定义事件:
self.$emit(self.param + '_typeahead');
这是我的root vue实例:
var typeahead = require('./components/typeahead.vue');
if (formElm = document.getElementById('form')) {
new Vue({
el: "#form",
data: {
form: new Form(formElm),
initialData: (typeof initialData !== 'undefined') ? initialData : [],
categories: (typeof categories !== 'undefined') ? categories : [],
parents: (typeof parents !== 'undefined') ? parents : []
},
components: {
'typeahead': typeahead
},
mounted() {
if (typeof initialData !== 'undefined') {
for (let field in initialData) {
this.form[field] = initialData[field];
}
}
},
methods: {
onSubmit() {
// get all the fields
let formData = this.form.data();
let form = this.form;
let formInfo = form.info(formElm);
// setup the constraints for validate.js
var constraints = {
name: {
presence: {
message: "is required."
}
},
description: {
presence: {
message: "is required."
}
},
category_id: {
presence: {
message: "is required."
}
}
};
// validate the fields
validate.async(formData, constraints)
.then(function(success) {
if (formInfo['method'] == 'PUT') {
form.put(formInfo['url']);
} else {
form.post(formInfo['url']);
}
})
.catch(function(error) {
form.onFail(error);
});
},
onSuccess(response) {
form.reset();
}
}
});
}
以下是我使用此组件的刀片模板:
<form method="post" id="form" action="{{ url('admin/eventType') }}" @submit.prevent="onSubmit"
@keydown="form.errors.clear($event.target.name)">
{{ csrf_field() }}
<div class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" name="name" v-model="form.name" placeholder="Name">
<span class="help text-danger" v-if="form.errors.has('name')" v-text="form.errors.get('name')"></span>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" id="description" name="description" v-model="form.description" placeholder="Description"></textarea>
<span class="help text-danger" v-if="form.errors.has('description')" v-text="form.errors.get('description')"></span>
</div>
<div class="form-group">
<label for="category_id">Category</label>
<input type="hidden" id="category_id" name="category_id" v-model="form.category_id" />
<typeahead v-on:category_id_typeahead="form.errors.clear('category_id')" param="category_id" :default-suggestion="true" :local="categories" display-key="value" classes="form-control">
</typeahead>
<span class="help text-danger" v-if="form.errors.has('category_id')" v-text="form.errors.get('category_id')"></span>
</div>
<div class="form-group">
<label for="parent_id">Parent</label>
<input type="hidden" id="parent_id" name="parent_id" v-model="form.parent_id" />
<typeahead param="parent_id" :default-suggestion="true" :local="parents" display-key="value" classes="form-control">
</typeahead>
</div>
<button type="submit" class="btn btn-default" :disabled="form.errors.any()">Create New Event Type</button>
</form>
在上面的模板中,我通过v-on指令捕获组件标记中的事件:
<typeahead v-on:category_id_typeahead="form.errors.clear('category_id')"
param="category_id" :default-suggestion="true"
:local="categories" display-key="value"
classes="form-control">
问题:我正在使用v-if指令决定根据方法的输出显示错误文本:
<span class="help text-danger" v-if="form.errors.has('category_id')" v-text="form.errors.get('category_id')"></span>
由于触发'category_id_typeahead'会清除此字段的错误,因此form.errors.has('category_id')方法的输出应为false,因此应删除错误但不会删除。根据我通过console.log打印的内容,错误对象在触发事件后不包含此字段的错误,但在对表单进行其他更改之前不会删除文本。似乎v-if不会在此步骤中呈现。
以下是我的表单类的代码:
const formToJSON = elements => [].reduce.call(elements, (data, element) => {
if (!['submit'].includes(element.type) && !['_token'].includes(element.name)) {
data[element.name] = element.value;
}
return data;
}, {});
const formInfo = elements => [].reduce.call(elements, (data, element) => {
if (['_method'].includes(element.name)) {
data['method'] = element.value;
}
if (typeof initialData !== 'undefined' && data['method'] == "PUT") {
data['id'] = initialData['id'];
}
return data;
}, {});
// get entity
const currentEntity = function() {
url = window.location.href;
adminPosition = url.indexOf('admin/') + 6;
entity = url.substring(adminPosition, url.indexOf('/', adminPosition));
return entity;
}
class Errors {
constructor() {
this.errors = { };
}
has(field) {
console.log('has error field : ', field, ' => ', this.errors.hasOwnProperty(field));
return this.errors.hasOwnProperty(field);
}
any() {
return Object.keys(this.errors).length > 0;
}
get(field) {
if (this.errors[field]) {
return this.errors[field][0];
}
}
record(errors) {
this.errors = errors;
}
clear(field) {
if (field) {
console.log('Before : clear error of ', field, ' error:', this.errors[field]);
delete this.errors[field];
return;
}
this.errors = {};
}
}
class Form {
constructor(formElm) {
// get form data
data = formToJSON(formElm.elements);
this.originalData = data;
for (let field in data) {
this[field] = data[field];
}
this.errors = new Errors();
}
info(formElm) {
data = formInfo(formElm.elements);
entity = currentEntity();
if (data['method'] == 'PUT') {
data['url'] = '/admin' + '/' + entity + '/' + data['id'];
} else {
data['url'] = '/admin' + '/' + entity;
}
return data;
}
data() {
let data = {};
for (let property in this.originalData) {
var propertyValue = document.getElementById(property);
if (typeof propertyValue !== 'undefined' && propertyValue.value !== null &&
propertyValue.value !== "") {
data[property] = propertyValue.value;
} else if (typeof initialData !== 'undefined' &&
typeof initialData[property] !== 'undefined') {
data[property] = initialData[property];
}
}
return data;
}
reset() {
for (let field in this.originalData) {
this[field] = '';
}
typeaheads = document.querySelectorAll('[id^="typeahead"]');
for (let item in typeaheads) {
typeaheads[item].value = '';
}
this.errors.clear();
}
post(url) {
return this.submit('post', url);
}
put(url) {
return this.submit('put', url);
}
submit(requestType, url) {
return new Promise((resolve, reject) => {
axios[requestType](url, this.data())
.then(response => {
this.onSuccess(response.data);
resolve(response.data);
})
.catch(error => {
this.onFail(error.response.data);
reject(error.response.data);
});
});
}
onSuccess(data) {
this.reset();
}
onFail(errors) {
this.errors.record(errors);
}
}
module.exports = Form;
我该如何解决这个问题?我错过了什么吗?
答案 0 :(得分:0)
您尝试对复杂的ojbects(Form)属性进行更改。 Vue将getter和setter添加到触发(UI)更新的属性。 https://vuejs.org/v2/guide/reactivity.html
在您的情况下,表单更新中某处的更新不会通知视图执行渲染更新。
你应该让v-on:category_id_typeahead="form.errors.clear('category_id')"
调用一个单独的方法来执行form.errors.clear('category_id');
以及触发ui更新的内容。这可能是https://vuejs.org/v2/api/#vm-forceUpdate或更改的新data:{showError:false...
属性。