How do I watch all keys in a data object in Vue 2 - watch

My data object:
data: {
selected: {
'type': null,
'instrument': null
},
My template:
<select v-model="selected['instrument']" #change="switchFilter('instrument', $event)">
<option v-for="instrument in instruments" :value="instrument.value">#{{ instrument.text }}</option>
</select>
<select v-model="selected['type']" #change="switchFilter('type', $event)">
<option v-for="type in types" :value="type.value">#{{ type.text }}</option>
</select>
How can I watch both selected indexes at the same time? I want to do something like this everytime any of the indexes updates:
watch: {
selected: function(o, n) {
...
}
}

You can use deep option provided by the watcher from vue. As stated in the docs:
To also detect nested value changes inside Objects, you need to pass in deep: true in the options argument. Note that you don’t need to do so to listen for Array mutations.
You code will look like following:
watch: {
'selected': {
handler: function (val, oldVal) {
console.log('watch 1', 'newval: ', val, ' oldVal:', oldVal)
},
deep: true
}
}

I think you can do this:
watch: {
$data: {
handler: function(val, oldVal) {
console.log(val)
},
deep: true
}
},

watch: {
'selected.type': function (newSelectedType) {
console.log(newSelectedType)
},
'selected.instrument': function (newSelectedinstrument) {
console.log(newSelectedinstrument)
}
}
If you are just trying to calculate a new data from selected, you can just use computed properties, since the data of Vue are reactive, the computed properties can also detect the changes of data.
If you want to use a single function to watch the entire object, you can use $watch with deep: true:
mounted () {
this.$watch('$data.selected', this.onSelectedUpdate, { deep: true })
}
note that '$data.selected' is a string, Vue will parse it.
and in your methods:
onSelectedUpdate (newSelected) {
console.log(newSelected)
}

Related

How to create a mongoose schema that makes sure that user provide only one subcategory?

I am trying to create a product for an e-commerce website. I want to make sure that the user provides only one sub category for the product. But the way I have implemented my schema user can create product even without providing any sub category and I want the user to select only one sub category. My current schema is like this:
category: {
type: String,
required: true,
enum: {
values: [
"men",
"women",
"kids",
]
}
},
subCategory: {
men: {
type: String,
enum: {
values: ["shirts", "t-shirts", "trousers", "jeans"],
},
},
women: {
type: String,
enum: {
values: ["shirts", "t-shirts", "trousers", "jeans"],
},
}
kids: {
type: String,
enum: {
values: ["shirts", "t-shirts", "trousers", "jeans"],
},
}
}
But right now even if I don't provide any subCategory the product is still created. Please help me out with this one.
I used pre validate middleware in mongoose to solve this.
productSchema.pre("validate", function (next) {
if (!this.subCategory.men && !this.subCategory.women && !this.subCategory.kids) {
return next(new Error("Please provide atleast one value for sub category"));
}
if (
(this.subCategory.men && this.subCategory.women && this.subCategory.kids) ||
(this.subCategory.women && this.subCategory.kids) ||
(this.subCategory.men && this.subCategory.kids) ||
(this.subCategory.women && this.subCategory.men)
) {
return next(new Error("Please provide only one value for sub category"));
}
next();
});

Form handling and validation with vuex best practice

I have a big form (10+ inputs) which values I want to bind (like v-model) with object I have in vuex store, on submission it needs to send axios request to server (server will handle validation) and respond with error object (this field is required, this value is too short, etc...).
I will provide minimalistic example with just one input field.
Object student is for student itself obviously, and error object will be there to handle errors from server response.
App.vue
<template>
<div>
<input v-model="student.name">
<span>{{error.name}}</span>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "App",
computed: {
student.name: {
get () {
return this.$store.state.student.name
},
set (value) {
this.$store.commit('setStudentName', value)
}
},
error.name: {
get () {
return this.$store.state.error.name
},
set (value) {
this.$store.commit('setErrorName', value)
}
}
}
}
</script>
and this is mu vuex store:
export default {
state: {
student: { name: ''},
error: { name: ''}
},
mutations: {
setStudentName: (state, student.name) => (state.student.name = student.name),
setErrorName: (state, error.name) => (state.error.name = error.name)
}
}
So this works perfectly, but imagine having 10+ inputs and having to write setters and getters for 10 inputs x 2 objects (student and error), that is like 40 setters&getters at least.
Is there a easier way to do this?
I have also tried vuex-forms which is great, but package is incomplete and documentation is missing, tried vuex-map-fields, which is good only for handling one object at the time.
All suggestions are welcome, what it the correct way to do this?

Perform a facet search query with Algolia autocomplete

My index objects has a city field and I'd like to retrieve these with autocomplete, but documentation seems missing about how to perform a query (only basic search documentation is available), I found a prototype IndexCore.prototype.searchForFacetValues in the autocomplete.js but I have no idea to use it.
You should be able to use the following source:
var client = algoliasearch("YourApplicationID", "YourSearchOnlyAPIKey");
var index = client.initIndex("YourIndex");
autocomplete("#search-input", { hint: false }, [
{
source: function(query, callback) {
index
.searchForFacetValues({
facetName: "countries",
facetQuery: query
})
.then(function(answer) {
callback(answer.hits);
})
.catch(function() {
callback([]);
});
},
displayKey: "my_attribute",
templates: {
suggestion: function(suggestion) {
return suggestion._highlightResult.my_attribute.value;
}
}
}
]);
This uses the searchForFacetValues method to get the results.

Creating a data context with a data option in iron-router not working

Whilst using iron router 1.011 with Meteor I created a router which returns a Mongo db cursor to a collection subset using a route parameter; see below:
Router.route('/categories/:slug', function() {
this.render('CategoryShow', {
data: {
category_products: Products.find({
category: this.params.slug
})
}
})
});
This works fine, however I didn’t see this as particularly elegant and decide to pass the options as an object as per the documents; see below.
Router.route('/categories/:slug', {
data: function() {
templateData = {
category_products: Products.find({
category: this.params.slug
})
}
return templateData;
},
name: 'category.show'
});
This does not return any data to the template but I notice that this pattern seem to work find when using findOne() instead of find(). Note that the template is declared as:
<Template name="CategoryShow">
// bla bla
</Template>
Please advise

Model attributes destroyed on form data save

I'm having a problem saving an existing model after a form submit in my backbone/marionette app.
So, I submit the form and use backbone.syphon to create a representation of the form data. The object I build looks like this :
{languages: {
{
de_DE: {
default: false
enabled: true
},
en_US: {
...
}
...
}
}
I'm trying to save it to a backbone model with attributes that looks like this:
attributes: {
id: "5"
languages: {
de_DE: {
default: false,
label: "German"
language: "de_DE"
selected: false
},
en_CA: {
...
},
...
}
}
The problem is that when I save the existing model using model.save(data) using the above data structure for my data, the default and label instances are completely removed from my model. They're not even sent to the server. They are just completelely removed, though they do sit in the previousAttrs object.
The instance of my model's sync setup looks so:
sync: function(method, model, options){
var baseUrl = window.location.origin+'/qp-api/v1/master-app/';
var config = {}
switch(method){
case "create":
break;
case "read":
config = _.extend(config, {
method: "GET",
url: baseUrl+this.id+'/languages'
});
break;
case "update":
config =_.extend({
method: "PUT",
url: baseUrl+this.id+'/languages'
});
break;
case "delete":
break;
};
options = _.extend(options, config);
return Backbone.Model.prototype.sync.call(this, method, model, options);
},
What am I doing wrong? I thought backbone's save function would only update the changed attrs. It looks to me like my data object should map to the setup of my models attrs. Shouldn't they just update? Am I not understanding something about how existing model's save?
At first I want to mention that it's not a good idea to make such checks if(languages.save(data){ .... }) . model.save() will return promise object, so your if condition will not work as expected.
One of the solutions for your issue is to override languages model's save method.
var Languages = Backbone.Model.extend({
// Declaration of you model
save: function (attrs, options) {
// merge attrs here with defaults/initial values
return this.constructor.__super__.save.call(this, attrs, options);
}
})
Hope this helps!